2019-04-19 22:11:33 +02:00
|
|
|
<p class="f7">
|
|
|
|
<a href="/suggest.html">suggestion box</a> –
|
|
|
|
<a href="https://git.high5.nl/why-vi.rocks/log/">latest additions</a> –
|
|
|
|
<a href="/also.html">see also</a> –
|
|
|
|
<a href="/colophon.html">colophon</a>
|
|
|
|
</p>
|
2019-04-06 16:45:57 +02:00
|
|
|
|
2023-06-11 11:39:03 +02:00
|
|
|
<div class="logo-container">
|
|
|
|
<img id="logo" class="mt4" src="vi-sw.png" alt="Logo">
|
|
|
|
</div>
|
2019-04-07 09:49:48 +02:00
|
|
|
|
2023-06-07 16:59:13 +02:00
|
|
|
_Why? [vi](https://en.wikipedia.org/wiki/Vi) is the **de facto** standard
|
2019-04-19 22:11:33 +02:00
|
|
|
text editor in any Unix-like operating system._
|
2019-04-06 16:45:57 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
Here is a collection of [vi(1)](https://man.openbsd.org/vi.1)/[ex(1)](https://man.openbsd.org/ex.1) commands and command sequences.<br>
|
|
|
|
Tested with [nvi](https://en.wikipedia.org/wiki/Nvi) 1.79 and 2.1.3 (unicode).
|
2019-04-10 20:55:18 +02:00
|
|
|
|
2020-05-10 13:26:58 +02:00
|
|
|
[Bill Joy's greatest gift to man - the vi editor](https://www.theregister.co.uk/2003/09/11/bill_joys_greatest_gift/)
|
|
|
|
|
2019-04-06 23:50:55 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Yank/delete lines
|
2019-04-06 16:45:57 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
(1) mark the first line: `mk`<br>
|
|
|
|
(2) move to last line<br>
|
|
|
|
(3a) yank: `y'k`<br>
|
|
|
|
(3b) delete: `d'k`<br>
|
|
|
|
(4) move to destination line<br>
|
|
|
|
(5) put with `P` or `p`<br>
|
2019-04-06 16:45:57 +02:00
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Apply regex to lines
|
2019-04-06 16:45:57 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
(1) mark the first line: `mk`<br>
|
|
|
|
(2) mark the last line: `ml`<br>
|
2019-04-06 23:00:29 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:'k,'ls/<em>regex</em>/<em>power</em>/g
|
|
|
|
</pre>
|
2019-04-06 23:00:29 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
2019-04-22 22:02:28 +02:00
|
|
|
## Increment / Decrement number in command mode
|
|
|
|
|
|
|
|
(1) move cursor to number<br>
|
|
|
|
(2a) increment by one: #+<br>
|
|
|
|
(2b) increment by N (5): 5#+<br>
|
|
|
|
(3a) decrement by one: #-<br>
|
|
|
|
(3b) decrement by N (9): 9#-
|
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Add # to a block
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
:'k,'ls/^/#/
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
2020-05-10 13:26:58 +02:00
|
|
|
## Remove trailing whitespace from every line
|
|
|
|
|
2020-07-19 09:02:38 +02:00
|
|
|
<pre>
|
|
|
|
:%s/ *$//
|
|
|
|
</pre>
|
2020-05-10 13:26:58 +02:00
|
|
|
|
|
|
|
|
2023-06-06 23:00:54 +02:00
|
|
|
## Remove tabs
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
:%s/<u><CTRL-V><TAB></u>//g
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Remove trailing whitespace from a block
|
2019-04-06 23:00:29 +02:00
|
|
|
|
2020-07-19 09:02:38 +02:00
|
|
|
<pre>
|
|
|
|
:'k,'ls/\ *$//
|
|
|
|
</pre>
|
2019-04-06 23:00:29 +02:00
|
|
|
|
2019-04-15 08:44:30 +02:00
|
|
|
|
2019-04-22 22:02:28 +02:00
|
|
|
## Remove the first N-characters from every line
|
2019-04-19 22:11:33 +02:00
|
|
|
|
|
|
|
N = 5
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
:%s/^.\{0,<em>5</em>\}//
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
|
|
|
## Delete all lines N-character long
|
2019-04-15 08:44:30 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
N = 10
|
2019-04-16 09:45:50 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:g/^.\{<em>10</em>\}$/d
|
|
|
|
</pre>
|
2019-04-16 09:45:50 +02:00
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Delete all lines _except_ N-character long
|
2019-04-16 09:45:50 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
N = 10
|
2019-04-08 19:33:45 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:g!/^.\{<em>10</em>\}$/d
|
|
|
|
</pre>
|
2019-04-08 19:33:45 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
|
|
|
## Search/replace paths using # as delimiter
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
:%s#<em>/usr/local/log</em>#<em>/var/log</em>#g
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
2023-06-06 23:02:49 +02:00
|
|
|
## Search/replace ^M with LF
|
2023-06-06 23:00:54 +02:00
|
|
|
|
2023-06-06 23:02:49 +02:00
|
|
|
<pre>
|
|
|
|
:g/<u><CTRL-V><ENTER></u>/s///g
|
|
|
|
</pre>
|
2023-06-06 23:00:54 +02:00
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Write the file as root
|
2019-04-06 16:45:57 +02:00
|
|
|
|
|
|
|
:w !doas tee %
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
|
|
|
## Diff the file on disk with the buffer
|
2019-04-06 16:45:57 +02:00
|
|
|
|
|
|
|
:w !diff -u % -
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
|
|
|
## Make a backup of the file on disk
|
2019-04-06 16:45:57 +02:00
|
|
|
|
2019-04-06 18:21:54 +02:00
|
|
|
:!cp % %.bak
|
2019-04-06 16:45:57 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
|
|
|
## Sort all lines
|
2019-04-06 16:45:57 +02:00
|
|
|
|
|
|
|
:%!sort
|
|
|
|
|
2019-04-06 18:21:54 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Sort a block
|
|
|
|
|
|
|
|
`}` won't be shown
|
2019-04-06 18:21:54 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
2019-04-20 08:38:21 +02:00
|
|
|
!<u>}</u>sort
|
2019-04-19 22:11:33 +02:00
|
|
|
</pre>
|
2019-04-07 12:57:19 +02:00
|
|
|
|
2019-04-07 12:45:27 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Sort from the current line to EOF
|
2019-04-07 12:45:27 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
`G` won't be shown
|
2019-04-07 12:45:27 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
!<u>G</u>sort
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
|
|
|
## Delete duplicated lines in the file
|
2019-04-06 16:45:57 +02:00
|
|
|
|
|
|
|
:%!uniq
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Delete duplicated lines in the block
|
|
|
|
|
|
|
|
`}` won't be shown
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
!<u>}</u>uniq
|
|
|
|
</pre>
|
2019-04-06 18:21:54 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Delete duplicated lines till EOF
|
2019-04-06 18:21:54 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
`G` won't be shown
|
2019-04-07 12:57:19 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
!<u>G</u>uniq
|
|
|
|
</pre>
|
2019-04-07 12:45:27 +02:00
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Underline all lines starting with `pattern`
|
2019-04-07 12:45:27 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:g/^<em>pattern</em> /t.|s/./=/g
|
|
|
|
</pre>
|
2019-04-08 11:00:53 +02:00
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Search for `pattern`, print the containing function (start with `def`) and line number
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:g/<em>pattern</em>/?^ *<em>def</em> ?#
|
|
|
|
</pre>
|
2019-04-08 11:00:53 +02:00
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Add # to paragraph containing `pattern`
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:g/<em>pattern</em>/?^$?+,//-s/^/#
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
|
|
|
## Sort content of a multiline CSS block
|
2019-04-08 11:00:53 +02:00
|
|
|
|
|
|
|
:g/{$/+,/^}/-!sort
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
|
|
|
## Sort content of a multiline CSS block (media queries)
|
2019-04-08 11:00:53 +02:00
|
|
|
|
|
|
|
:g/^[^@].*{$/+,/}/-!sort
|
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Format content of `<p>` tag to fixed width
|
|
|
|
|
|
|
|
width = 40
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:g/<p>/+,/<\/p>/-!fmt -<em>40</em>
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
2020-05-10 13:26:58 +02:00
|
|
|
## Format whole document
|
|
|
|
|
|
|
|
:%!fmt -s
|
|
|
|
|
|
|
|
In your .nexrc
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
map gF :%!fmt -s<u><CTRL-V><ENTER></u>
|
|
|
|
</pre>
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Reverse all lines, move `m` all lines to 0
|
2019-04-19 18:57:09 +02:00
|
|
|
|
|
|
|
:g/1*/m0
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
|
|
|
## Swap `Lastname, Firstname` to `Firstname, Lastname`
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-08 19:54:24 +02:00
|
|
|
:%s/\(.*\), \(.*\)/\2 \1/
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
|
|
|
## Convert to lowercase
|
2019-04-15 15:20:39 +02:00
|
|
|
|
2019-04-20 08:46:47 +02:00
|
|
|
:%s/.*/\L&/
|
2019-04-15 15:20:39 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
|
2023-06-06 23:00:54 +02:00
|
|
|
## Surround text with pattern
|
|
|
|
|
|
|
|
:%s/.*/`pattern` & `pattern`/
|
|
|
|
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Join all lines
|
2019-04-06 18:21:54 +02:00
|
|
|
|
|
|
|
:%j
|
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Copy `t` or move `m` lines containing `pattern`
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
:g/<em>pattern</em>/t$
|
|
|
|
:g/<em>pattern</em>/m$
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
## Select a column of a table
|
|
|
|
Select 3rd column separated by colon (`:`)
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:%!awk -F'<em>:</em>' '{print $<em>3</em>}'
|
|
|
|
</pre>
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Insert the sum of a list of numbers after an arbitrary number of lines
|
2019-04-06 18:21:54 +02:00
|
|
|
|
2019-04-20 08:46:47 +02:00
|
|
|
(1) mark the first line: `mk`
|
|
|
|
(2) mark the last line: `ml`
|
2019-04-06 18:21:54 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:'k,'l!awk 'END{print "<em>total:</em>", i}{i+=$1; print}'
|
|
|
|
</pre>
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
or
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:'k,'l!awk 'END{print "<em>total:</em>", i} ++i || 1'
|
|
|
|
</pre>
|
2019-04-08 11:00:53 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Email the block
|
2019-04-06 16:45:57 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
|
|
|
:?^$?+,//-w !mail -s "<em>subject</em>" <em>email@example.com</em>
|
|
|
|
</pre>
|
2019-04-10 10:25:06 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
## Enable and use `ex` history
|
2019-04-10 10:25:06 +02:00
|
|
|
|
2019-04-27 09:43:45 +02:00
|
|
|
(1) Set `ESC` key to enable history or add to `~/.nexrc`:
|
2019-04-20 08:48:09 +02:00
|
|
|
|
2019-04-19 22:11:33 +02:00
|
|
|
<pre>
|
2019-04-20 08:48:09 +02:00
|
|
|
:set cedit=<u><CTRL-V><ESC></u>
|
|
|
|
</pre>
|
2019-04-10 10:25:06 +02:00
|
|
|
|
2019-04-20 08:48:09 +02:00
|
|
|
(2) Use it with:
|
|
|
|
|
|
|
|
<pre>
|
2019-04-19 22:11:33 +02:00
|
|
|
:<u><ESC></u>
|
|
|
|
</pre>
|
2019-04-27 09:43:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
## Integrate with tmux buffer
|
|
|
|
|
|
|
|
(1) cut text from current position to mark 'm' into tmux buffer. Hit undo to put text back into vi buffer.
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
!'mtmux load-buffer -
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
(2) paste text from tmux buffer into vi buffer.
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
:r!tmux show-buffer
|
|
|
|
</pre>
|
|
|
|
|
2019-06-05 11:10:31 +02:00
|
|
|
(3) Map in ~/.nexrc (command mode)
|
2019-04-27 09:43:45 +02:00
|
|
|
|
|
|
|
<pre>
|
|
|
|
map gx !'mtmux load-buffer -<u><CTRL-V><ENTER></u>
|
|
|
|
map gy !'mtmux load-buffer -<u><CTRL-V><ENTER></u>u
|
|
|
|
map gp :r!tmux show-buffer<u><CTRL-V><ENTER></u>
|
|
|
|
</pre>
|
2019-04-27 10:29:52 +02:00
|
|
|
|
|
|
|
|
2019-06-05 11:10:31 +02:00
|
|
|
## Remap ESC to ALT-i in ~/.nexrc (insert mode)
|
2019-04-27 10:29:52 +02:00
|
|
|
|
|
|
|
<pre>
|
|
|
|
map! <u><CTRL-V><ALT-i></u> <u><CTRL-V><ESC></u>
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|