474 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			474 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| <!-- <img align="right" width="350px" src="doc/completion-macos.png"/> -->
 | |
| 
 | |
| <img align="left" src="doc/isocline-inline.svg"/>
 | |
| 
 | |
| # Isocline: a portable readline alternative.
 | |
|  
 | |
| Isocline is a pure C library that can be used as an alternative to the GNU readline library (latest release v1.0.9, 2022-01-15).
 | |
| 
 | |
| - Small: less than 8k lines and can be compiled as a single C file without 
 | |
|   any dependencies or configuration (e.g. `gcc -c src/isocline.c`).
 | |
|   
 | |
| - Portable: works on Unix, Windows, and macOS, and uses a minimal
 | |
|   subset of ANSI escape sequences.
 | |
|     
 | |
| - Features: extensive multi-line editing mode (`shift-tab`), (24-bit) color, history, completion, unicode, 
 | |
|   undo/redo, incremental history search, inline hints, syntax highlighting, brace matching,
 | |
|   closing brace insertion, auto indentation, graceful fallback, support for custom allocators, etc.
 | |
|   
 | |
| - License: MIT. 
 | |
| 
 | |
| - Comes with a Haskell binding ([`System.Console.Isocline`][hdoc].
 | |
| 
 | |
| Enjoy,
 | |
|   Daan
 | |
|   
 | |
| <!--  <img align="right" width="350px" src="doc/history-win.png"/> -->
 | |
|   
 | |
| # Demo
 | |
| 
 | |
|   
 | |
| 
 | |
| Shows in order: unicode, syntax highlighting, brace matching, jump to matching brace, auto indent, multiline editing, 24-bit colors, inline hinting, filename completion, and incremental history search.  
 | |
| <sub>(screen capture was made with [termtosvg] by Nicolas Bedos)</sub>
 | |
| 
 | |
| # Usage
 | |
| 
 | |
| Include the isocline header in your C or C++ source:
 | |
| ```C
 | |
| #include <include/isocline.h>
 | |
| ```
 | |
| 
 | |
| and call `ic_readline` to get user input with rich editing abilities:
 | |
| ```C
 | |
| char* input;
 | |
| while( (input = ic_readline("prompt")) != NULL ) { // ctrl+d/c or errors return NULL
 | |
|   printf("you typed:\n%s\n", input); // use the input
 | |
|   free(input);  
 | |
| }
 | |
| ```
 | |
| 
 | |
| See the [example] for a full example with completion, syntax highligting, history, etc.
 | |
| 
 | |
| # Run the Example
 | |
| 
 | |
| You can compile and run the [example] as:
 | |
| ```
 | |
| $ gcc -o example -Iinclude test/example.c src/isocline.c
 | |
| $ ./example
 | |
| ```
 | |
| 
 | |
| or, the Haskell [example][HaskellExample]:
 | |
| ```
 | |
| $ ghc -ihaskell test/Example.hs src/isocline.c
 | |
| $ ./test/Example
 | |
| ```
 | |
| 
 | |
| 
 | |
| # Editing with Isocline
 | |
| 
 | |
| Isocline tries to be as compatible as possible with standard [GNU Readline] key bindings.
 | |
| 
 | |
| ### Overview:
 | |
| ```apl
 | |
|        home/ctrl-a       cursor     end/ctrl-e
 | |
|          ┌─────────────────┼───────────────┐    (navigate)
 | |
|          │     ctrl-left   │  ctrl-right   │
 | |
|          │         ┌───────┼──────┐        │    ctrl-r   : search history
 | |
|          ▼         ▼       ▼      ▼        ▼    tab      : complete word
 | |
|   prompt> it is the quintessential language     shift-tab: insert new line
 | |
|          ▲         ▲              ▲        ▲    esc      : delete input, done
 | |
|          │         └──────────────┘        │    ctrl-z   : undo
 | |
|          │    alt-backsp        alt-d      │
 | |
|          └─────────────────────────────────┘    (delete)
 | |
|        ctrl-u                          ctrl-k
 | |
| ```
 | |
| 
 | |
| <sub>Note: on macOS, the meta (alt) key is not directly available in most terminals. 
 | |
| Terminal/iTerm2 users can activate the meta key through
 | |
| `Terminal` → `Preferences` → `Settings` → `Use option as meta key`.</sub>
 | |
| 
 | |
| ### Key Bindings
 | |
| 
 | |
| These are also shown when pressing `F1` on a Isocline prompt. We use `^` as a shorthand for `ctrl-`:
 | |
| 
 | |
| | Navigation        |                                                 |
 | |
| |-------------------|-------------------------------------------------|
 | |
| | `left`,`^b`       | go one character to the left |
 | |
| | `right`,`^f   `   | go one character to the right |
 | |
| | `up           `   | go one row up, or back in the history |
 | |
| | `down         `   | go one row down, or forward in the history |
 | |
| | `^left        `   | go to the start of the previous word |
 | |
| | `^right       `   | go to the end the current word |
 | |
| | `home`,`^a    `   | go to the start of the current line |
 | |
| | `end`,`^e     `   | go to the end of the current line |
 | |
| | `pgup`,`^home `   | go to the start of the current input |
 | |
| | `pgdn`,`^end  `   | go to the end of the current input |
 | |
| | `alt-m        `   | jump to matching brace |
 | |
| | `^p           `   | go back in the history |
 | |
| | `^n           `   | go forward in the history |
 | |
| | `^r`,`^s      `   | search the history starting with the current word |
 | |
|   
 | |
| 
 | |
| | Deletion        |                                                 |
 | |
| |-------------------|-------------------------------------------------|
 | |
| | `del`,`^d     `   | delete the current character |
 | |
| | `backsp`,`^h  `   | delete the previous character |
 | |
| | `^w           `   | delete to preceding white space |
 | |
| | `alt-backsp   `   | delete to the start of the current word |
 | |
| | `alt-d        `   | delete to the end of the current word |
 | |
| | `^u           `   | delete to the start of the current line |
 | |
| | `^k           `   | delete to the end of the current line |
 | |
| | `esc          `   | delete the current input, or done with empty input |
 | |
|   
 | |
| 
 | |
| | Editing           |                                                 |
 | |
| |-------------------|-------------------------------------------------|
 | |
| | `enter        `   | accept current input |
 | |
| | `^enter`,`^j`,`shift-tab` | create a new line for multi-line input |
 | |
| | `^l           `   | clear screen |
 | |
| | `^t           `   | swap with previous character (move character backward) |
 | |
| | `^z`,`^_      `   | undo |
 | |
| | `^y           `   | redo |
 | |
| | `tab          `   | try to complete the current input |
 | |
|   
 | |
| 
 | |
| | Completion menu   |                                                 |
 | |
| |-------------------|-------------------------------------------------|
 | |
| | `enter`,`left`    | use the currently selected completion |
 | |
| | `1` - `9`         | use completion N from the menu |
 | |
| | `tab, down    `   | select the next completion |
 | |
| | `shift-tab, up`   | select the previous completion |
 | |
| | `esc          `   | exit menu without completing |
 | |
| | `pgdn`,`^enter`,`^j`   | show all further possible completions |
 | |
|   
 | |
| 
 | |
| | Incremental history search        |                                                 |
 | |
| |-------------------|-------------------------------------------------|
 | |
| | `enter        `   | use the currently found history entry |
 | |
| | `backsp`,`^z  `   | go back to the previous match (undo) |
 | |
| | `tab`,`^r`,`up`   | find the next match |
 | |
| | `shift-tab`,`^s`,`down`  | find an earlier match |
 | |
| | `esc          `   | exit search |
 | |
| 
 | |
| 
 | |
| # Build the Library
 | |
| 
 | |
| ### Build as a Single Source
 | |
| 
 | |
| Copy the sources (in `include` and `src`) into your project, or add the library as a [submodule]:
 | |
| ```
 | |
| $ git submodule add https://github.com/daanx/isocline
 | |
| ```
 | |
| and add `isocline/src/isocline.c` to your build rules -- no configuration is needed. 
 | |
| 
 | |
| ### Build with CMake
 | |
| 
 | |
| Clone the repository and run cmake to build a static library (`.a`/`.lib`):
 | |
| ```
 | |
| $ git clone https://github.com/daanx/isocline
 | |
| $ cd isocline
 | |
| $ mkdir -p build/release
 | |
| $ cd build/release
 | |
| $ cmake ../..
 | |
| $ cmake --build .
 | |
| ```
 | |
| This builds a static library `libisocline.a` (or `isocline.lib` on Windows)
 | |
| and the example program:
 | |
| ```
 | |
| $ ./example
 | |
| ```
 | |
| 
 | |
| ### Build the Haskell Library
 | |
| 
 | |
| See the Haskell [readme][Haskell] for instructions to build and use the Haskell library.
 | |
| 
 | |
| 
 | |
| # API Reference
 | |
| 
 | |
| * See the [C API reference][docapi] and the [example] for example usage of history, completion, etc.
 | |
| 
 | |
| * See the [Haskell API reference][hdoc] on Hackage and the Haskell [example][HaskellExample].
 | |
| 
 | |
| 
 | |
| # Motivation
 | |
| 
 | |
| Isocline was created for use in the [Koka] interactive compiler. 
 | |
| This required: pure C (no dependency on a C++ runtime or other libraries), 
 | |
| portable (across Linux, macOS, and Windows), unicode support, 
 | |
| a BSD-style license, and good functionality for completion and multi-line editing.
 | |
| 
 | |
| Some other excellent libraries that we considered:
 | |
| [GNU readline],
 | |
| [editline](https://github.com/troglobit/editline),
 | |
| [linenoise](https://github.com/antirez/linenoise),
 | |
| [replxx](https://github.com/AmokHuginnsson/replxx), and 
 | |
| [Haskeline](https://github.com/judah/haskeline).
 | |
| 
 | |
| 
 | |
| # Formatted Output
 | |
| 
 | |
| Isocline also exposes functions for rich terminal output
 | |
| as `ic_print` (and `ic_println` and `ic_printf`). 
 | |
| Inspired by the (Python) [Rich][RichBBcode] library, 
 | |
| this supports a form of [bbcode]'s to format the output:
 | |
| ```c
 | |
| ic_println( "[b]bold [red]and red[/red][/b]" );
 | |
| ```
 | |
| Each print automatically closes any open tags that were
 | |
| not yet closed. Also, you can use a general close
 | |
| tag as `[/]` to close the innermost tag, so the
 | |
| following print is equivalent to the earlier one:
 | |
| ```c
 | |
| ic_println( "[b]bold [red]and red[/]" );
 | |
| ```
 | |
| There can be multiple styles in one tag
 | |
| (where the first name is used for the closing tag):
 | |
| ```c
 | |
| ic_println( "[u #FFD700]underlined gold[/]" );
 | |
| ```
 | |
| 
 | |
| Sometimes, you need to display arbitrary messages
 | |
| that may contain sequences that you would not like
 | |
| to be interpreted as bbcode tags. One way to do
 | |
| this is the `[!`_tag_`]` which ignores formatting
 | |
| up to a close tag of the form `[/`_tag_`]`.
 | |
| ```c
 | |
| ic_printf( "[red]red? [!pre]%s[/pre].\n", "[blue]not blue!" );
 | |
| ```
 | |
| 
 | |
| Predefined styles include `b` (bold),
 | |
| `u` (underline), `i` (italic), and `r` (reverse video), but
 | |
| you can (re)define any style yourself as:
 | |
| ```c
 | |
| ic_style_def("warning", "crimson u");
 | |
| ```
 | |
| 
 | |
| and use them like any builtin style or property:
 | |
| ```c
 | |
| ic_println( "[warning]this is a warning![/]" );
 | |
| ```
 | |
| which is great for adding themes to your application.
 | |
| 
 | |
| Each `ic_print` function always closes any unclosed tags automatically.
 | |
| To open a style persistently, use `ic_style_open` with a matching
 | |
| `ic_style_close` which scopes over any `ic_print` statements in between.
 | |
| ```c
 | |
| ic_style_open("warning");
 | |
| ic_println("[b]crimson underlined and bold[/]");
 | |
| ic_style_close();
 | |
| ```
 | |
| 
 | |
| # Advanced
 | |
| 
 | |
| 
 | |
| ## BBCode Format
 | |
| 
 | |
| An open tag can have multiple white space separated
 | |
| entries that are
 | |
| either a _style name_, or a primitive _property_[`=`_value_].
 | |
| 
 | |
| ### Styles
 | |
| 
 | |
| Isocline provides the following builtin styles as property shorthands:
 | |
| `b` (bold), `u` (underline), `i` (italic), `r` (reverse video),
 | |
| and some builtin styles for syntax highlighting:
 | |
| `keyword`, `control` (control-flow keywords), `string`,
 | |
| `comment`, `number`, `type`, `constant`.
 | |
| 
 | |
| Predefined styles used by Isocline itself are:
 | |
| 
 | |
| - `ic-prompt`: prompt style, e.g. `ic_style_def("ic-prompt", "yellow on blue")`.
 | |
| - `ic-info`: information (like the numbers in a completion menu).    
 | |
| - `ic-diminish`: dim text (used for example in history search).
 | |
| - `ic-emphasis`: emphasized text (also used in history search).
 | |
| - `ic-hint`: color of an inline hint.
 | |
| - `ic-error`: error color (like an unmatched brace).   
 | |
| - `ic-bracematch`: color of matching parenthesis.
 | |
| 
 | |
| ### Properties
 | |
| 
 | |
| Boolean properties are by default `on`:
 | |
| 
 | |
| - `bold` [`=`(`on`|`off`)]
 | |
| - `italic` [`=`(`on`|`off`)]
 | |
| - `underline` [`=`(`on`|`off`)]
 | |
| - `reverse` [`=`(`on`|`off`)]
 | |
| 
 | |
| Color properties can be assigned a _color_:
 | |
| 
 | |
| - `color=`_color_
 | |
| - `bgcolor=`_color_
 | |
| - _color_: equivalent to `color=`_color_.
 | |
| - `on` _color_: equivalent to `bgcolor=`_color_.
 | |
| 
 | |
| A color value can be specified in many ways:
 | |
| 
 | |
| - any standard HTML [color name][htmlcolors].
 | |
| - any of the 16 standard ANSI [color names][ansicolors] by prefixing `ansi-` 
 | |
|   (like `ansi-black` or `ansi-maroon`).   
 | |
|   The actual color value of these depend on the a terminal theme.
 | |
| - `#`_rrggbb_ or `#`_rgb_ for a specific 24-bit color.
 | |
| - `ansi-color=`_idx_ or `ansi-bgcolor=`_idx_, where _idx_ specifies an entry in the
 | |
|   standard ANSI 256 [color palette][ansicolor256] (between 0 and 255). 
 | |
|   Use _idx_ 256 for the ANSI default color.
 | |
| 
 | |
| The `width` property makes the text at least _width_ long:
 | |
| 
 | |
| - `width=`_width_ [`;`_align_ [`;`_fill_] ]
 | |
| 
 | |
| where _width_ is the column with, _align_ is `left`, `center`, or `right`,
 | |
| and _fill_ the fill character (`' '`).
 | |
| 
 | |
| The _maxwidth_ property makes text at most _width_ long; when the content
 | |
| it is wider, the left- or right side (depending on the alignment) 
 | |
| will have three dots (`...`) to visualize that content is cut off. 
 | |
| 
 | |
| - `maxwidth=`_width_ [`;`_align_]
 | |
| 
 | |
| 
 | |
| ## Environment Variables
 | |
| 
 | |
| - `NO_COLOR`: if present no colors are displayed.
 | |
| - `CLICOLOR=1`: if set, the `LSCOLORS` or `LS_COLORS` environment variables are used to colorize
 | |
|   filename completions.
 | |
| - `COLORTERM=`(`truecolor`|`256color`|`16color`|`8color`|`monochrome`): enable a certain color palette, see the next section.
 | |
| - `TERM`: used on some systems to determine the color
 | |
| 
 | |
| ## Colors
 | |
| 
 | |
| Isocline supports 24-bit colors and any RGB colors are automatically
 | |
| mapped to a reduced palette on older terminals if these do not
 | |
| support true color. Detection of full color support
 | |
| is not always possible to do automatically and you can
 | |
| set the `COLORTERM` environment variable expicitly to force Isocline to use
 | |
| a specific palette:
 | |
| - `COLORTERM=truecolor`: use 24-bit colors.  
 | |
|   <img width="500px" src="doc/color/ansi-truecolor.png"/>
 | |
| - `COLORTERM=256color`: use the ANSI 256 color palette.  
 | |
|   <img width="500px" src="doc/color/ansi-256color.png"/>
 | |
| - `COLORTERM=16color` : use the regular ANSI 16 color 
 | |
|    palette (8 normal and 8 bright colors).  
 | |
|    <img width="500px" src="doc/color/ansi-16color.png"/>
 | |
| - `COLORTERM=8color`: use bold for bright colors.
 | |
| - `COLORTERM=monochrome`: use no color.
 | |
| 
 | |
| The above screenshots are made with the 
 | |
| [`test_colors.c`](https://github.com/daanx/isocline/blob/main/test/test_colors.c) program. You can test your own
 | |
| terminal as:
 | |
| ```
 | |
| $ gcc -o test_colors -Iinclude test/test_colors.c src/isocline.c
 | |
| $ ./test_colors
 | |
| $ COLORTERM=truecolor ./test_colors
 | |
| $ COLORTERM=16color ./test_colors
 | |
| ```
 | |
| 
 | |
| ## ANSI Escape Sequences
 | |
| 
 | |
| Isocline uses just few ANSI escape sequences that are widely
 | |
| supported:
 | |
| - `ESC[`_n_`A`, `ESC[`_n_`B`, `ESC[`_n_`C`, and `ESC[`_n_`D`,
 | |
|   for moving the cursor _n_ places up, down, right, and left.
 | |
| - `ESC[K` to clear the line from the cursor.
 | |
| - `ESC[`_n_`m` for colors, with _n_ one of: 0 (reset), 1,22 (bold), 3,23 (italic),
 | |
|    4,24 (underline), 7,27 (reverse), 30-37,40-47,90-97,100-107 (color),
 | |
|    and 39,49 (select default color).
 | |
| - `ESC[38;5;`_n_`m`, `ESC[48;5;`_n_`m`, `ESC[38;2;`_r_`;`_g_`;`_b_`m`, `ESC[48;2;`_r_`;`_g_`;`_b_`m`: 
 | |
|   on terminals that support it, select 
 | |
|   entry _n_ from the
 | |
|   256 color ANSI palette (used with `XTERM=xterm-256color` for example), or directly specify
 | |
|   any 24-bit _rgb_ color (used with `COLORTERM=truecolor`) for the foreground or background.
 | |
|     
 | |
| On Windows the above functionality is implemented using the Windows console API
 | |
| (except if running in the new Windows Terminal which supports these escape
 | |
| sequences natively).
 | |
| 
 | |
| ## Async and Threads
 | |
| 
 | |
| Isocline is _not_ thread-safe and `ic_readline`_xxx_ and `ic_print`_xxx_ should
 | |
| be used from one thread only.
 | |
| 
 | |
| The best way to use `ic_readline` asynchronously is
 | |
| to run it in a (blocking) dedicated thread and deliver
 | |
| results from there to the async event loop. Isocline has the
 | |
| ```C
 | |
| bool ic_async_stop(void)
 | |
| ```
 | |
| function that is thread-safe and can deliver an
 | |
| asynchronous event to Isocline that unblocks a current
 | |
| `ic_readline` and makes it behave as if the user pressed
 | |
| `ctrl-c` (which returns NULL from the read line call).
 | |
| 
 | |
| ## Color Mapping
 | |
| 
 | |
| To map full RGB colors to an ANSI 256 or 16-color palette
 | |
| Isocline finds a palette color with the minimal "color distance" to
 | |
| the original color. There are various
 | |
| ways of calculating this: one way is to take the euclidean distance
 | |
| in the sRGB space (_simple-rgb_), a slightly better way is to 
 | |
| take a weighted distance where the weight distribution is adjusted
 | |
| according to how big the red component is ([redmean](https://en.wikipedia.org/wiki/Color_difference),
 | |
| denoted as _delta-rgb_ in the figure), 
 | |
| this is used by Isocline),
 | |
| and finally, we can first translate into a perceptually uniform color space
 | |
| (CIElab) and calculate the distance there using the [CIEDE2000](https://en.wikipedia.org/wiki/Color_difference)
 | |
| algorithm (_ciede2000_). Here are these three methods compared on
 | |
| some colors: 
 | |
| 
 | |
| 
 | |
| 
 | |
| Each top row is the true 24-bit RGB color. Surprisingly,
 | |
| the sophisticated CIEDE2000 distance seems less good here compared to the 
 | |
| simpler methods (as in the upper left block for example)
 | |
| (perhaps  because this algorithm was created to find close
 | |
| perceptual colors in images where lightness differences may be given
 | |
| less weight?). CIEDE2000 also leads to more "outliers", for example as seen
 | |
| in column 5. Given these results, Isocline uses _redmean_ for
 | |
| color mapping. We also add a gray correction that makes it less
 | |
| likely to substitute a color for a gray value (and the other way
 | |
| around).
 | |
| 
 | |
| 
 | |
| ## Possible Future Extensions
 | |
| 
 | |
| - Vi key bindings.
 | |
| - kill buffer.
 | |
| - make the `ic_print`_xxx_ functions thread-safe.
 | |
| - extended low-level terminal functions.
 | |
| - status and progress bars.
 | |
| - prompt variants: confirm, etc.
 | |
| - ...
 | |
| 
 | |
| Contact me if you are interested in doing any of these :-)
 | |
| 
 | |
| 
 | |
| # Releases
 | |
| 
 | |
| * `2022-01-15`: v1.0.9: fix missing `ic_completion_arg` (issue #6), 
 | |
|    fix null ptr check in ic_print (issue #7), fix crash when using /dev/null as both input and output.
 | |
| * `2021-09-05`: v1.0.5: use our own wcwidth for consistency; 
 | |
|   thanks to Hans-Georg Breunig for helping with testing on NetBSD.
 | |
| * `2021-08-28`: v1.0.4: fix color query on Ubuntu/Gnome
 | |
| * `2021-08-27`: v1.0.3: fix duplicates in completions 
 | |
| * `2021-08-23`: v1.0.2: fix windows eol wrapping
 | |
| * `2021-08-21`: v1.0.1: fix line-buffering
 | |
| * `2021-08-20`: v1.0.0: initial release  
 | |
|   
 | |
| 
 | |
| 
 | |
| [GNU readline]: https://tiswww.case.edu/php/chet/readline/rltop.html
 | |
| [koka]: http://www.koka-lang.org
 | |
| [submodule]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
 | |
| [Haskell]: https://github.com/daanx/isocline/tree/main/haskell
 | |
| [HaskellExample]: https://github.com/daanx/isocline/blob/main/test/Example.hs
 | |
| [example]: https://github.com/daanx/isocline/blob/main/test/example.c
 | |
| [termtosvg]: https://github.com/nbedos/termtosvg
 | |
| [Rich]: https://github.com/willmcgugan/rich
 | |
| [RichBBcode]: https://rich.readthedocs.io/en/latest/markup.html
 | |
| [bbcode]: https://en.wikipedia.org/wiki/BBCode
 | |
| [htmlcolors]: https://en.wikipedia.org/wiki/Web_colors#HTML_color_names
 | |
| [ansicolors]: https://en.wikipedia.org/wiki/Web_colors#Basic_colors
 | |
| [ansicolor256]: https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
 | |
| [docapi]: https://daanx.github.io/isocline
 | |
| [hdoc]: https://hackage.haskell.org/package/isocline/docs/System-Console-Isocline.html
 |