Nvi - Enhancing comfort II

10 Jan 2024

In this series some of the existing keybindings in nvi will be discussed.

The purpose of this article is not to list all the existing commands, but to describe the most unusual novelties that behave differently. Knowledge of the general usage of vi is recommended.

There are keybindings for two of vi's modes. These are insert mode and command mode. Some are valid when the editor is in insert mode. Most of the (useful) commands have been created for the command mode. First, some interesting commands for insert mode are described:

Most of the insert keybindings start with the control modifier and are used to modify the text being typed. The important thing to remember is that only the text in the inserted buffer is affected. After switching to another mode, the text you typed before cannot be changed with these insert keybindings. The most useful are:

<control-D>	Erase to the previous shiftwidth column boundary.
<control-T>	Insert sufficient tab and space characters to move forward to the next shiftwidth column boundary.
		If the expandtab option is set, only insert space characters.
<control-H>	Erase the last character.
<control-W>	Erase the last word. The definition of word is dependent on the altwerase and ttywerase options.
Source: https://man.openbsd.org/vi

Deleting the previous indent column is only possible if there is no text on the line. This works best in combination with control-T, because if the autoindent option is set, the indentation is automatically set on the previous line. In nvi, there is no convenience option to make the autoindent option depend on the type of text currently being viewed. Finish: When you are editing code and the cursor is on a new line, you can use control-D or T to change the indentation.

The other options are best for conveniently deleting words or characters without leaving insert mode or the home row. Unfortunately, as mentioned above, only text that is currently inserted can be deleted. In conclusion, these keybindings are only of limited use. Nevertheless, we're in nvi, and in nvi any feature should be considered a gift.

Next up are the command mode keybindings. I will only describe a few here, and two features will be honoured with their own blog entries (specifically: screens and tags). The first feature shown here is an elegant way of moving the cursor quickly:

[count] <control-D>	Scroll forward count lines. [...]
[count] <control-U>	Scroll backward count lines. [...]
Source: https://man.openbsd.org/vi

These movements are like simple page up and page down functions that don't need further elaboration. It's mentioned here because it's a nice feature that can be added to your personal nvi toolbox. Another nice feature is the control-G shortcut. This is used to display more information about the file, the status and the current position. Note that a percentage of the current position can only be given if the lines are counted all the way to the end of the file. If the file is too large to process, it may be necessary to go to the last line and back to the start position (mmG'm).

The undo and redo actions in nvi are another speciality. Normally it's easy to go back and forth in the change history. This is only indirectly possible in nvi. The keys to use are u, u, U and .. The lower case u is shown twice because it's used to switch between undo and redo. Think of it as a binary state machine, where the dot key is responsible for repeating the undo or redo action. The capital letter U is used to return the whole line to the state it was in before the cursor was focussing on it (not repeatable with the dot key).

Other interesting keyboard shortcuts include ~ to toggle a letter between uppercase and lowercase, #+ and #- to increment and decrement the next number on the line, and < and > to increment and decrement the indentation of the selected line or multiple lines to a specified number.

The @ feature is responsible for executing a buffer of vi commands. The challenge here this is to record the commands and insert them into a specific buffer. Usually you have the q command at hand to record it into a specific buffer, but nvi doesn't have that. It's possible to load the buffer with the buffer modifying commands. It doesn't matter if the resulting buffer is line-oriented or character-oriented. This method is best explained with an example. The following text is given as a start:

123 456
abc def
123 456

And the aim is to delete the last two characters of every second word on every line of this block of text and to append a colon, the command for this is:

Gowxxa:^[0<ESC>"qddH@q@@@@

That is: G (go to EOF), o (add a line below the cursor and switch to input mode), wxxa:^[j (the commands to be buffered soon), <ESC> (switch back to command mode), "q (select q as the target buffer for the next command), dd (delete the line where the cursor is and yank the line to the specified target buffer), H (set the cursor to the first line), @q (execute commands in the named buffer q), @@ (execute commands in the previously selected buffer).

To further delve into the commands that are inserted into the q buffer: w (move the cursor to the next word), xx (delete two characters), a (append text and thus switch to insert mode), : (add the colon), ^[ (leave insert mode (this can be entered with control-V+<ESC>)), 0 (move the cursor to the beginning of the current line). After executing the given commands, the text block now looks like this:

123 6:
abc f:
123 6:

To end this blog entry correctly, use the command ZZ to write the currently opened file and quit nvi. If entered twice, any files left to edit will be ignored and the editor will quit anyway. This is an excellent transition to the third part of the enhancing comfort series. The third part will be about multiple files and how to handle them properly in nvi.