# [`readline-vim`][] Upgrade [Readline][] from [vi][] to [Vim][]. [`readline-vim`][] is a [Python][] script that generates [Readline][] macros (4658 of them) that provide [Vim][] [motions][], [operators][] and [text objects][] (the ones that are useful on a single line) as well as functionality from the popular [`vim-surround`][]. [`readline-vim`]: https://git.rcrnstn.net/rcrnstn/readline-vim [Readline]: https://en.wikipedia.org/wiki/Readline [vi]: https://en.wikipedia.org/wiki/vi [Vim]: https://en.wikipedia.org/wiki/Vim_(text_editor) [Python]: https://www.python.org [motions]: https://vimhelp.org/motion.txt.html [operators]: https://vimhelp.org/motion.txt.html#operator [text objects]: https://vimhelp.org/motion.txt.html#text-objects [`vim-surround`]: https://github.com/tpope/vim-surround ## Background [Readline][] is a library that provides line-editing for interactive command-line programs. It is used in a great many places, including shells such as [Bash][] (where it originated) and [REPL][]s such as [Python][]'s or [Octave][]'s. Using [`rlwrap`][], Readline can even be used with programs that don't have built-in support for it, such as [MATLAB][] (as in `rlwrap -a matlab -nodesktop -nosplash`). There are bindings in many languages, so if you're a developer you can easily use it in your own projects. These factors make it fairly ubiquitous. Readline can be configured with an [`inputrc`][] file, usually placed at [`~/.inputrc`][], which supports an [`$include`][] directive useful for including other files, e.g. from `~/.inputrc.d/`. Readline uses [Emacs][]-like key bindings by default. If you're a Vim fan though, you probably use its vi mode, activated by `set editing-mode vi` in the configuration or through some mechanism in the containing program, such as Bash's `set -o vi`. If you *are* a Vim fan though, this has the potential of landing you in a sort of [uncanny valley][] since (Readline's) vi (mode) does not include the many improvements contained in Vim that you have come to rely on, mainly [text objects][] and the related plugins. This is especially bad if you're using Readline inside Vim's own [`:terminal`][]. [`readline-vim`][] aims to fix this. [Bash]: https://en.wikipedia.org/wiki/Bash_(Unix_shell) [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop [Octave]: https://en.wikipedia.org/wiki/GNU_Octave [`rlwrap`]: https://manpages.debian.org/rlwrap/rlwrap.1.html [MATLAB]: https://en.wikipedia.org/wiki/MATLAB [`inputrc`]: https://www.mankier.com/3/readline#Initialization_File [`~/.inputrc`]: https://www.mankier.com/3/readline#Files-~/.inputrc [`$include`]: https://www.mankier.com/3/readline#Initialization_File-Conditional_Constructs [Emacs]: https://en.wikipedia.org/wiki/Emacs [uncanny valley]: https://en.wikipedia.org/wiki/Uncanny_valley [`:terminal`]: https://vimhelp.org/terminal.txt.html#:terminal ## Usage Clone the repository ```sh git clone https://git.rcrnstn.net/rcrnstn/readline-vim cd readline-vim ``` and either append the output to your `~/.inputrc` ```sh ./readline-vim >> ~/.inputrc ``` or use a separate file and include it (recommended) ```sh mkdir -p ~/.inputrc.d ./readline-vim >| ~/.inputrc.d/readline-vim.inputrc ``` ```sh cat >> ~/.inputrc << 'EOF' ## `readline-vim` $include ~/.inputrc.d/readline-vim.inputrc EOF ``` ### Use vi mode by default ```sh cat >> ~/.inputrc << 'EOF' ## Use vi editing mode set editing-mode vi EOF ``` ### Indicate mode with cursor shape ```sh cat >> ~/.inputrc << 'EOF' ## Indicate mode with cursor shape set show-mode-in-prompt On $if term=linux # https://www.kernel.org/doc/Documentation/admin-guide/vga-softcursor.rst set vi-cmd-mode-string \1\e[?8c\2 set vi-ins-mode-string \1\e[?0c\2 set emacs-mode-string \1\e[?0c\2 $else # https://vt100.net/docs/vt510-rm/DECSCUSR # http://invisible-island.net/xterm/ctlseqs/ctlseqs.html set vi-cmd-mode-string \1\e[1 q\2 set vi-ins-mode-string \1\e[5 q\2 set emacs-mode-string \1\e[5 q\2 $endif EOF ``` ## Features - Motions - vi (built-in) - Word (upper and lower case versions) - `w` *word beginning, forwards* - `e` *word end, forwards* - `b` *word beginning, backwards* - Other - `h`, `l` *left, right* - `0`, `^`, `$` *line beginning, first non-whitespace character, end*. - `|` *(first) column* - `;`, `,` *next, previous searched character* - `%` *matching pair character* - Vim - Word (upper and lower case versions) - `ge` *word end, backwards* - Other - `g_` *last non-whitespace character* - `)`, `(` *sentence, forwards, backwards* (only works with sentences after ".") - Operators - vi - `d` *delete* - `c` *change* - `y` *[yank][]* - [`vim-surround`][] - `ds` *delete surroundings* - `cs` *change surroundings* - `ys` *add surroundings ("you surround")* - Text objects - Motions (vi and Vim) - Implicit line (repeat last character of operator) - Vim words (`a` *an* and `i` *inner*, upper and lower case versions) - `w` *word* - Vim pairs (`a` *an* and `i` *inner* versions) - Distinct - Matching - `(`, `)`, `b` - `{`, `}`, `B` - `[`, `]` - Other - `<`, `>` - Nondistinct - Quotes - `"`, `'`, `` ` `` - Punctuation - `#`, `$`, `%`, `&`, `@`, `\\`, `^` `_`, `|`, `~` - `.`, `,`, `:`, `;`, `!`, `?` - `+`, `-`, `*`, `/`, `=` [yank]: https://en.wiktionary.org/wiki/yank ## Bugs Some macros clobber the `a`, `b` and/or `c` marks. Only forward motions work as expected with the surround operators. Most known bugs exhibit themselves at the end, and sometimes the beginning, of line. This is sometimes unavoidable, sometimes a trade-off to produce correct behavior elsewhere, mostly single-character words and/or whitespace: - `ge`/`gE` motions: Jumps past last word on line, does not jump past first word on line. - word/WORD text objects: Always excludes the last character on line. - Implicit line text object (as used in e.g. `yss)`): Includes whitespace at the beginning and end of line. ## Implementation notes This is an honest attempt to solve this problem "optimally". However, Readline macros are not [Turing complete][Turing completeness] (in contrast to e.g. VimScript) which makes this kind of hard (and more fun :smile:). Testing suggests that marks are not supported by all applications when combined with operators in macros. No workaround has been found. Counts used in macros have to come before the operator. That is, `2cl` works but `c2l` does not. Macros are what Vim calls recursive, i.e. they can make use of other macros in their expansion. To use Readline commands (such as `upcase-word`) they need to be bound to a key sequence (if no binding exists already) before being used in macros. [Turing completeness]: https://en.wikipedia.org/wiki/Turing_completeness ## Related projects - [`bash-surround`][] Defines the macros manually and therefore "only" provides 234 of them, missing some functionality. The instructions are Bash centric, although it uses general Readline macros. - [Athame][] Patches Readline to route keystrokes through an actual Vim process. Also provides patches for [Zsh][]. This means that whatever your local Vim supports, Athame supports (including plugins)! Can be a little unreliable if your local Vim has a ton of plugins, especially if they interact with the terminal in non-obvious ways. - [Zsh][] Has built-in [text objects][Zsh text objects] and [surround][Zsh surround] support. It even has a Vim-like [Visual mode][Zsh Visual mode]. The popular [Oh My Zsh][] Zsh configuration framework extends this with its [`vi-mode`][Oh My Zsh `vi-mode`] plugin. There are also several other plugins: - [`softmoth/zsh-vim-mode`][] - [`jeffreytse/zsh-vi-mode`][] - [`thlorenz/readline-vim`][] Exclusive to [Node.js][] [`readline`][Node.js `readline`] and only defines vi bindings, not Vim bindings. [`bash-surround`]: https://github.com/liloman/bash-surround [Athame]: https://github.com/ardagnir/athame [Zsh]: https://en.wikipedia.org/wiki/Zsh [Zsh text objects]: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Text-Objects [Zsh surround]: https://sourceforge.net/p/zsh/code/ci/master/tree/Functions/Zle/surround [Zsh Visual mode]: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#index-visual_002dmode [Oh My Zsh]: https://ohmyz.sh [Oh My Zsh `vi-mode`]: https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/vi-mode [`softmoth/zsh-vim-mode`]: https://github.com/softmoth/zsh-vim-mode [`jeffreytse/zsh-vi-mode`]: https://github.com/jeffreytse/zsh-vi-mode [`thlorenz/readline-vim`]: https://github.com/thlorenz/readline-vim [Node.js]: https://nodejs.org [Node.js `readline`]: https://nodejs.org/api/readline.html#readline_readline ## License Licensed under the [ISC License][], see the [`LICENSE`](LICENSE) file. [ISC License]: https://choosealicense.com/licenses/isc/