Nextvi(1)

πŸ“¦
version 5.3 Β· 3 versions
, in alpine 3.24

Nextvi(1) β€” from the General Commands Manual, bestie, and yes this is the whole vibe. Welcome to Nextvi, the manual page that's gonna make your terminal feel like main character energy. 🫢

🌸 NAME 🌸

Nextvi β€” A small vi/ex terminal text editor

✨ SYNOPSIS ✨

vi [-aemsv] [file ...]

πŸ’– DESCRIPTION πŸ’–

Okay so Nextvi is a modern clone of the command-line text editor vi(1), initially developed by Bill Joy in 1976 for Unix-based systems. The lore is real. πŸ’…

Nextvi builds on a bunch of standard vi(1) features, including that iconic unique modal interface where you switch between normal, insert, and command modes for efficient text manipulation. The way it lets you switch modes? Obsessed.

Extra glow-up features include an unrestricted macro system, syntax highlighting, keymaps, bidirectional UTF-8 support, and numerous other features. Nextvi stays highly efficient, portable, and hackable, which keeps it relevant and high quality for years to come. Periodt.

πŸŽ€ OPTIONS πŸŽ€

  • -a β€” Enable alternate terminal screen
  • -e β€” Enter ex mode on startup
  • -m β€” Disable initial file read message
  • -s β€” Enter raw ex mode on startup
  • -v β€” Unset all options

🧚 MANPAGE NOTATION 🧚

  • <x> β€” A closure where x represents a character literal
  • [x] β€” A closure where x represents optional argument
  • {x} β€” A closure where x represents required argument
  • "x" β€” A closure where x represents a string
  • <^X> β€” Represents a ctrl key X
  • < > β€” Separates alternatives in a closure
  • x-y β€” Range from x to y

πŸ”₯ VI NORMAL πŸ”₯

Okay bestie, hydrate, because this is the big one. Every command below is exactly what it is, no cap. πŸ§ƒ

  • [#]j β€” Move # lines down
  • [#]k β€” Move # lines up
  • [#]+, [#]<^M>, [#]<Enter> β€” Move # lines down, cursor after indent
  • [#]- β€” Move # lines up, cursor after indent
  • [#]h β€” Move # columns left
  • [#]l β€” Move # columns right
  • [#]f{arg} β€” Move to arg character found forward # times
  • [#]F{arg} β€” Move to arg character found backward # times
  • [#]t{arg} β€” Move until arg character found forward # times
  • [#]T{arg} β€” Move until arg character found backward # times
  • [#], β€” Repeat last <f F t T> move backward # times
  • [#]; β€” Repeat last <f F t T> move forward # times
  • [#]E β€” Move to end of word # times, skip punctuation
  • [#]e β€” Move to end of word # times
  • [#]B β€” Move to start of word backward # times, skip punctuation
  • [#]b β€” Move to start of word backward # times
  • [#]W β€” Move to start of word forward # times, skip punctuation
  • [#]w β€” Move to start of word forward # times
  • vw β€” Toggle single-line mode for <E e B b W w Space Backspace> regions
  • [#]( β€” Move to next sentence boundary down # times
  • [#]) β€” Move to next sentence boundary up # times
  • [#]{ β€” Move to next <{> section down # times
  • [#]} β€” Move to next <{> section up # times
  • [#][ β€” Move to next <Newline> section down # times
  • [#]] β€” Move to next <Newline> section up # times
  • ^ β€” Move to start of line after indent
  • 0 β€” Move to start of line
  • $ β€” Move to end of line
  • [#]| β€” Goto # column
  • [#]<Space> β€” Move # characters forward
  • [#]<^H>, [#]<Backspace> β€” Move # characters backward
  • % β€” Move to closest <] ) } [ ( {> pair
  • {#}% β€” Move to # percent line number
  • '{<a-z ` ' [ ] *>} β€” Move to a line mark
  • `{<a-z ` ' [ ] *>} β€” Move to a line mark with cursor position
  • gg β€” Goto first line in buffer
  • [#]G β€” Move to last line in buffer or # line
  • H β€” Move to highest line on a screen
  • L β€” Move to lowest line on a screen
  • M β€” Move to middle line on a screen
  • z. β€” Center screen at cursor
  • z<^M>, z<Enter> β€” Center screen at top row
  • z- β€” Center screen at bottom row
  • [#]<^E> β€” Scroll down 1 or # lines, retain # and cursor position
  • [#]<^Y> β€” Scroll up 1 or # lines, retain # and cursor position
  • [#]<^D> β€” Scroll down half a screen size. If [#], set scroll to # lines
  • [#]<^U> β€” Scroll up half a screen size. If [#], set scroll to # lines
  • <^B> β€” Scroll up full screen size
  • <^F> β€” Scroll down full screen size
  • # β€” Show global and relative line numbers
  • 2# β€” Toggle show global line numbers permanently
  • 4# β€” Toggle show relative line numbers after indent permanently
  • 8# β€” Toggle show relative line numbers permanently
  • V β€” Toggle show hidden characters: <Space Tab Newline>
  • <^C> β€” Toggle show line motion numbers for <l h e b E B w W>
  • {<1-5>}<^C> β€” Select line motion number mode 1-5
  • <^V> β€” Loop through line motion number modes
  • [#]<^R> β€” Redo # times
  • [#]u β€” Undo # times
  • <^I>, <Tab> β€” Open file path from cursor to end of line
  • <^K> β€” Write current buffer to file. Force write on 2nd attempt
  • [#]<^W>{arg} β€” Unindent arg region # times
  • [#]<{arg} β€” Indent left arg region # times
  • [#]>{arg} β€” Indent right arg region # times
  • [#]"{<arg>}{cmd} β€” Execute normal cmd using register arg. # applies to cmd
  • R β€” Print registers and their contents
  • [#]&{<arg>} β€” Execute arg register macro in non-blocking mode # times
  • [#]@{<arg>} β€” Execute arg register macro in blocking mode # times
  • [#]@@ β€” Execute last macro # times, blocking
  • [#]&& β€” Execute last macro # times, non-blocking
  • [#]@:, [#]&: β€” Execute <:> register as an ex command # times
  • [#]. β€” Repeat last normal command # times. Repeatable commands: <x X D C s S Y p P r d c ~ gu gU g~ ! > < ^W i I a A o O ci di J K>
  • [#]v. β€” Repeat last normal command moving down across # lines
  • [#]Q β€” Enter ex mode. # retains current horizontal scroll
  • : β€” Enter ex prompt
  • [#]!{arg} β€” Enter pipe ex prompt based on # or arg region
  • vv β€” Enter ex prompt with the last line from history buffer b-1
  • [#]vr β€” Enter %s/ ex prompt. Insert # words from cursor
  • [#]vt[#arg] β€” Enter .,.+0s/ ex prompt. Insert # of lines from cursor. Insert #arg words from cursor
  • [#]v/ β€” Enter re ex prompt to set regex keyword. Insert # words from cursor
  • v; β€” Enter ! ex prompt
  • [#]vi β€” Enter %s/ ex prompt. Contains regex for changing spaces to tabs. # modifies tab width
  • [#]vI β€” Enter %s/ ex prompt. Contains regex for changing tabs to spaces. # modifies tab width
  • vo β€” Remove trailing white spaces and <\r> line endings
  • <^G> β€” Print buffer status info
  • 1<^G> β€” Enable permanent status row
  • 2<^G> β€” Disable permanent status row
  • ga β€” Print character info
  • 1ga β€” Enable permanent character info row
  • 2ga β€” Disable permanent character info row
  • [#]gw β€” Hard word wrap a line to # column limit. Default: 80
  • [#]gq β€” Hard word wrap a buffer to # column limit. Default: 80
  • [#]g~{arg} β€” Switch character case for arg region # times
  • [#]gu{arg} β€” Switch arg region to lowercase # times
  • [#]gU{arg} β€” Switch arg region to uppercase # times
  • [#]~ β€” Switch character case # times forward
  • i β€” Enter insert mode
  • I β€” Enter insert mode at start of line after indent
  • A β€” Enter insert mode at end of line
  • a β€” Enter insert mode 1 character forward
  • o β€” Create a new line down and enter insert mode
  • O β€” Create a new line up and enter insert mode
  • [#]s β€” Delete # characters and enter insert mode
  • [#]S β€” Delete all across # lines and enter insert mode
  • [#]c{arg} β€” Delete arg region # times and enter insert mode
  • C β€” Delete from cursor to end of line and enter insert mode
  • [#]d{arg} β€” Delete arg region # times
  • D β€” Delete from cursor to end of line
  • [#]x β€” Delete # characters from cursor forward
  • [#]X β€” Delete # characters from cursor backward
  • di{<arg>} β€” Delete inside arg pairs
  • ci{<arg>} β€” Change inside arg pairs
  • [#]r{arg} β€” Replace # characters with arg from cursor forward
  • [#]K β€” Split a line # times
  • [#]J β€” Join # lines
  • [#]y{arg} β€” Yank arg region # times
  • [#]Y β€” Yank # lines
  • [#]p β€” Paste default register below current line or after cursor position # times
  • [#]P β€” Paste default register above current line or before cursor position # times
  • m{<a-z ` ' [ ] *>} β€” Set buffer local line mark
  • <^T> β€” Set global line mark 0. Global marks are always valid
  • {<0 2 4 6 8>}<^T> β€” Set a global line mark #
  • {<1 3 5 7 9>}<^T> β€” Switch to a global line mark #
  • [#]<^7>{<0-9>}, [#]<^_>{<0-9>}, [#]<^/>{<0-9>} β€” Show buffer list and switch based on # or 0-9 index when prompted
  • <^^>, <^6> β€” Swap to previous buffer
  • [#]<^N> β€” Swap to next buffer, # changes direction [forward backward]
  • \ β€” Swap to /fm/ buffer b-2
  • {#}\ β€” Refresh secondary directory listing in b-2
  • vb β€” Recurse into b-1 history buffer. Insert current line into ex prompt on exit
  • z1 β€” Set alternative keymap to Farsi keymap
  • z2 β€” Set alternative keymap to Russian keymap
  • ze β€” Switch to English keymap
  • zf β€” Switch to alternative keymap
  • zL β€” Set td ex option to 2
  • zl β€” Set td ex option to 1
  • zr β€” Set td ex option to -1
  • zR β€” Set td ex option to -2
  • [#]/ β€” Regex search prompt, move down 1 or # matches
  • [#]? β€” Regex search prompt, move up 1 or # matches
  • [#]n, [#]N β€” Repeat regex search, move [down up] 1 or # matches
  • <^A> β€” Regex search 1 word from cursor, no center, wraparound move [up down]
  • * β€” Regex search, no center, wraparound move [up down]
  • {#}*, {#}<^A> β€” Regex search, set keyword to # words from cursor
  • <^]> β€” Filesystem regex search forward based on directory listing in b-2. Sets global line mark 0 for <^P> fallback
  • {#}<^]> β€” Filesystem regex search forward, set keyword to # words from cursor
  • [#]<^P> β€” Filesystem regex search backward
  • <^Z> β€” Suspend vi
  • <^L> β€” Force redraw whole screen and update terminal dimensions
  • Z{<Any>} β€” Force exit and clean terminal, force quit in an & macro
  • Zz β€” Force exit and submit history command, force quit in an & macro
  • ZZ β€” Exit and write unsaved changes to a file
  • <ESC ^C> β€” Cancel any pending input or command

🌸 VI REGIONS 🌸

okay so regions are vi normal commands that define [h v]range for vi motions. Commands that say they "move" define a region, bestie. πŸ’–

Here’s the whole region squad, no cap:

j + <^M> <Enter> - k h l f F t T , ; B E b e W w ( ) { } [ ] ^ 0 $
        <Space> <^H> <Backspace> % ' ` G H L M / ? n N * <^A>

All regions. ✨

πŸ¦‹ VI MOTIONS πŸ¦‹

Motions are vi normal commands that run in a [h v]range. Commands that say they "region" consume a region. Motions can be prefixed or suffixed by [#]. πŸ’…

All motions:

<^W> > < ! c d y g~ gu gU

Special motions that can use [#] as number of lines:

dd yy cc g~~ guu gUU >> << <^W><^W> !!

Examples, because we love a demo:

  • 3d/int β€” delete text until the 3rd instance of the "int" keyword.
  • 3dw β€” delete 3 words (prefix [#]).
  • d3w β€” delete 3 words (suffix [#]).
  • "ayl β€” yank a character into the <a> register.
  • "Ayw β€” append a word to the <a> register.

πŸŽ€ VI/EX INSERT πŸŽ€

These keys go feral in insert mode:

  • <^H>, <Backspace> β€” delete a character, reset ex mode when empty.
  • <^U> β€” delete until the <^X> mark or all.
  • <^W> β€” delete a word.
  • <^T> β€” increase indent.
  • <^D> β€” decrease indent.
  • <^]> β€” select paste register from 0-9 registers in a loop.
  • <^\>{<arg>} β€” select paste register arg. <^\> selects the default register.
  • <^P> β€” paste a register.
  • <^X> β€” mark autocomplete and <^U> starting position. <^X> resets the mark.
  • <^G> β€” index current buffer for autocomplete.
  • <^Y> β€” reset all indexed autocomplete data.
  • <^R> β€” loop through autocomplete options backward.
  • <^N> β€” loop through autocomplete options forward.
  • <^B> β€” print autocomplete options when in vi insert.
  • <^B> β€” recurse into b-1 history buffer when in ex prompt. Insert current line into ex prompt on exit.
  • <^A> β€” loop through lines in a history buffer b-1.
  • <^Z> β€” suspend vi/ex.
  • <^L> β€” redraw screen in vi mode, clean terminal in ex.
  • <^O> β€” switch between vi and ex modes recursively.
  • <^E> β€” switch to English keymap.
  • <^F> β€” switch to alternative keymap.
  • <^V>{<arg>} β€” read a literal character arg.
  • <^K>{arg} β€” read a digraph sequence arg.
  • <^C>, <ESC> β€” exit insert mode in vi, reset in ex.
  • <^M>, <Enter> β€” insert <Newline> in vi, submit command in ex.

πŸ’œ EX πŸ’œ

Ex is a powerful line editor for Unix systems, initially developed by Bill Joy in 1976. This essential tool is literally the backbone of vi, letting it execute commands, macros, and even transform into a purely command-line interface (CLI) when desired. πŸ‘‘

✨ EX PARSING ✨

Parsing follows this structure:

[<sep>][prefix][cmd][<pad>][args]

Ex commands are initiated and separated by <sep>. Fields can be padded by <Space> or <Tab>. [prefix] is a field consumed by [cmd], which accepts [range] structure characters. Padding inside [prefix] structure is collapsed, except within nested structures. There can only be one pad in between [cmd] and [args].

To avoid ambiguity in scripts, it is recommended to always use a pad between [cmd] and [args]. πŸ’–

Examples:

  • :evi.c β€” evaluates to ":e vi.c".
  • :efbc β€” evaluates to ":ef bc", not ":e fbc".
  • :e vi.c β€” edit " vi.c". <pad> is required.

πŸ”₯ EX ESCAPES πŸ”₯

Special characters in [args] will become regular when escaped with <\>.

  • ( ^ ] - β€” specials in regex "[]" expression.
  • ( ) { + * ? ^ $ [ ] | \ . < > β€” specials in regex.
  • \ : % ! β€” specials in ex.

🌈 EX EXPANSION 🌈

<%> in [args] expands to current buffer pathname or any buffer pathname when followed by a corresponding buffer number. "%#" expands to last swapped buffer pathname. "%@" expands to register specified. πŸ¦‹

Examples:

  • :!cp % %_:e %_ β€” make a copy of the current file and edit it.
  • :&i% β€” insert current buffer pathname.
  • :!echo "%@a" β€” echo the value of <a> register.

<!> in [args] starts and optionally ends a block containing external commands. This block executes and expands to stdout and stderr produced.

Examples:

  • :%s/int/!printf "%s" $RANDOM! β€” substitute "int" with the value of $RANDOM.
  • :&i!ls β€” insert output of ls shell command.
  • :;c!ls!<^V><ESC> β€” insert output of ls more efficiently.

⭐ EX RANGES ⭐

Some ex commands can be prefixed with ranges. [range] prefix implements vertical and horizontal ranges. [vrange] prefix implements vertical range and horizontal position. Without [cmd], [vrange] replaces [prefix].

Structures:

  • [% |][, ;][#][. $ ' > <][- + * / %][0-9] β€” all ranges structure.
  • {|}{cmd}[|] β€” ex command structure.
  • {>}[regex][>] β€” search forward structure.
  • {<}[regex][<] β€” search backward structure.
  • '{<mark>} β€” mark structure.

Character meanings:

  • % β€” range from first to last line.
  • | β€” begin ex command structure.
  • , β€” vertical range separator.
  • ; β€” horizontal range separator.
  • # β€” rebase to previous value in range structure.
  • . β€” current position.
  • $ β€” last line of a buffer or end of line.
  • ' β€” begin mark structure.
  • > β€” begin search forward structure.
  • < β€” begin search backward structure.
  • - β€” subtract following number.
  • + β€” add following number.
  • * β€” multiply by the following number.
  • / β€” divide by the following number.
  • % β€” modulo by the following number.
  • 0-9 β€” number or position.

Examples, aka the range vibe check:

  • :1,5p β€” print lines 1,5.
  • :.-5,.+5p β€” print 5 lines around current position.
  • :>int>p β€” print first occurrence of "int".
  • :<int<p β€” print first occurrence of "int" in reverse.
  • :.,>int>p β€” print until "int" is found.
  • :<int<,.p β€” print until "int" is found in reverse.
  • :> β€” search using previously set regex keyword.
  • :'d,'ap β€” print lines from mark <d> to mark <a>.
  • :%p β€” print all lines in a buffer.
  • :$p β€” print last line in a buffer.
  • :$*50/100+1 β€” goto 50% of the file.
  • :;50 β€” goto character offset 50.
  • :10;50 β€” goto line 10 character offset 50.
  • :10;.+5 β€” goto line 10 +5 character offset.
  • :'a;'a β€” goto line mark <a> offset mark <a>.
  • :;$ β€” goto end of line.
  • :5;>int> β€” search for "int" on line 5.
  • :.;<int< β€” search for "int" in reverse on the current line.
  • :;5;+10= β€” +10 is relative to the initial current offset, not 5.
  • :;5;#+10= β€” +10 is relative to 5.
  • :;>int>+3;#>>p β€” print text enclosed by "int" on the current line.
  • :|grp1|;>(a)+>+1|grp|;#>>p β€” print text enclosed by a non-deterministic pattern "a+".

πŸ‘‘ EX COMMANDS πŸ‘‘

  • [range]f>[regex], [vrange]f<[regex] β€” ranged search.
    • Horizontal range performs a scoped multi-line search forward.
    • In multi-line mode, <Newline> is a regular character.
    • Returns error if target not found.
    • Examples:
      • no range given, current line only: :f>int
      • reverse: :f<int
      • range given: :10,100f>int
      • search for "int" followed by "void" on next line: :%;0f>int.void
  • [range]f+[regex], [vrange]f-[regex] β€” incrementing ranged search.
    • Equivalent to the :f> and :f< commands, except subsequent commands within range move to the next match just like vi normal [#]n or [#]N commands.
  • [vrange]f[regex] β€” fuzzy search prompt.
    • Enters a fuzzy search prompt with a list of matches.
    • Matches are prefixed by selection numbers and line numbers.
    • Match can be selected using numbers 0-9 if there are less than 10 matches.
    • <^C> or <ESC> to abort.
    • <^M> or <Enter> to input a selection number larger than 9.
    • No range evaluates to <%> range.
    • No range displays a maximum of xrows * 3 matches.
  • [vrange]g[<Any>][regex][<Any>][cmd] β€” global command.
    • Execute an ex command on a range of lines that matches an enclosed regex.
    • Returns error if no line matches the pattern.
    • No range evaluates to <%> range or <.> range when nested.
    • Examples:
      • remove all empty lines: :g/^$/d
      • print lines matching previously set regex: :g//p
    • Multiple ex commands can be chained in one global command. To chain commands, the ex separator <:> must be escaped once.
    • Example: print and append lines matching "int" to register <a>: :g/int/p\:ya ax
    • It is possible to nest global commands inside of global commands. The first global command will not be executed on lines that were changed by a nested global command.
    • Example: nested global command β€” append "has a semicolon" to all lines that contain "int" and end with <;>: :g/int/g/;$/& A has a semicolon
    • Example: extract/print data enclosed in "()":
:grp1:err4:g/./;0\:;>(\().+\)>;#>.+(\))>+1p:err1:grp
  • [vrange]g![<Any>][regex][<Any>][cmd] β€” inverted global command.
  • [range]re[regex] β€” set regex keyword.
    • Range escapes its text and uses it as the regex.
    • Examples:
      • set keyword to "int": :re int
      • set keyword from lines 1 to 3: :1,3re
  • [vrange]i[str] β€” enter ex insert mode before specified position.
    • [str] specifies initial input into the insertion buffer.
    • [str] in raw ex mode is injected directly and bypasses the interactive mode.
    • Examples:
      • insert "hello" in vi/ex: :i hello<^M><ESC>
      • insert "hello" in vi/ex trimming <Newline>: :i hello<^M><^M><Backspace>
      • discard changes in vi/ex: :i hello<^M><^C>
      • immediately insert "hello": :i hello<^V><ESC>
      • insert "hello" in raw ex mode: i hello
      • insert two lines in raw ex mode: i hello<^M>world
      • insert "hello" in raw ex mode (interactive): i<^M>hello<^M>.<^M>
  • [vrange]a[str] β€” enter ex insert mode after specified position.
  • [range]c[str] β€” enter ex change mode.
    • In combination with seq and lim ex options, this command is optimal for modifying very long lines.
    • Max insertion buffer is ~1.33GB on a 32 bit build.
    • Max addressable line is 2.0GB on a 32 bit build.

For :c, the example set is giving exactly this energy:

  • Example: replace the current line with "hello":
:c hello<^M><ESC>
  • Example: replace lines 1-5 with "hello":
:1,5c hello<^M><ESC>
  • Example: insert "hello" at the current character offset:
:;c hello<^M><ESC>
  • Example: insert "hello" at the end of the line:
:;$c hello<^M><ESC>
  • Example: insert "hello" at line 5 and at the end of the line:
:5;$c hello<^M><ESC>
  • Example: delete 5 characters from the current position:
:;;+5c<^M><ESC>

[range]d β€” Delete line(s)

  • Example: delete from the current position to the start of the buffer:
:1,.;0;d
  • Example: delete from the current position to the end of the buffer:
:.,$;;$d

[vrange]j[any] β€” Join line(s)

Any argument activates padding mode. It lowkey returns an error if there is literally nothing to join.

  • Example: join all lines:
:%-1j
  • Example: join all lines with space padding:
:%-1j x

[vrange]s[<Any>][regex][<Any>][str][<Any>][<g>] β€” Substitute

Find and replace text in a range of lines that matches an enclosed regex with an enclosed replacement string. Returns an error if no substitution is performed. Periodt.

  • Example: global replacement:
:%s/term1/term2/g
  • Example: replace matching previously set regex:
:%s//term2/g

Substitution backreference inserts the text of the matched group specified by \x, where x is the group number. Obsessed.

  • Example: substitution backreference:
this is an example text for subs and has int or void
:%s/(int)|(void)/pre\0after
this is an example text for subs and has preintafter or void
:%s/(int)|(void)/pre\2after/g
this is an example text for subs and has prepreafterafter or prevoidafter

ud β€” Undo

Returns an error if there is nothing to undo.

rd β€” Redo

Returns an error if there is nothing to redo.

[range]p[str] β€” Print line(s) from a buffer

No range prints a line based on the value of the left ex option. Argument prints the evaluated argument.

  • Example: utilize character offset ranges:
:1,10;5;5p
  • Example: interleaved character offset ranges:
:1;5,10;5p
  • Example: print the current line from offset 5 to 10:
:.;5;10p
  • Example: print buffer pathname:
:p %

[range]=[<0-3 Any>][any] β€” Print range numbers

Argument 0-3 selects a specific number. Any or any extra argument circumvents the buffer range check. No argument prints all range numbers.

  • Example: print the current character offset only:
:;= 2
  • Example: print the value of mark <a>:
:'a=
  • Example: calculate 75 - 100:
:,75-100=1p

=?[any] β€” Print last error

Any argument forwards the error status.

[range]m{marks} β€” Set line mark(s)

Valid marks: <a-z ' [ ] *>`

  • Example: store ranges in <a> and <s> for later reuse in :f>:
:>top>,#>bottom>m as:'a,'sf>middle

[range]ya[<reg>][any] β€” Yank into a register

Argument value range is 0x1-0xff and 0x0 without. Any extra argument appends to the register. The default register cannot be appended to.

  • Example: append to register <1>:
:ya 1x
  • Example: yank into <a> using ranges:
:1,5;5;5ya a

ya![<reg>] β€” Free a register

[range]pu[<reg>][any][\!{cmd}] β€” Paste or pipe a register

  • Example: paste <a> at line 1, character offset 5:
:1;5pu a
  • Example: copy the default register to the X11 clipboard:
:pu \!xclip -selection clipboard
  • Example: copy register <a> to the X11 clipboard:
:pu a \!xclip -selection clipboard

[0-255]reg[str] β€” Print registers or put into a register

Prefix selects a register, and the argument replaces its contents.

  • Example: shift the printing position to view offscreen register parts:
:1left:reg
  • Example: put "hello" into register <1>:
:49reg hello

[0-255]reg+[str] β€” Print registers or append to a register

e[path] β€” Open a file at a path

No argument opens the "unnamed" buffer. Files larger than 2.0GB are truncated on a 32 bit build. Line breaking happens until the first null terminator or EOF.

e![path] β€” Force open a file at a path

No argument re-reads the current buffer from the filesystem.

[vrange]ef[regex] β€” Open file using fuzzy search prompt

Requires the b-2 buffer to have a directory listing backfilled.

  • Example: backfill b-2 using :fd:
:fd
  • Example: backfill b-2 using find:
:b-2:%!find .

Prompt behavior is equivalent to the :f command.

  • Example: enter prompt and print the entire listing:
:ef
  • Example: search for a pathname containing "v":
:ef v

[vrange]ef![regex] β€” Forced version of :ef

[range]r[path] / [range]r[\!{cmd}] β€” Read a file or a pipe

Range is computed on a target. No range evaluates to <%> range. No argument evaluates to the current buffer path.

  • Example: read a file:
:r vi.c
  • Example: pipe in all data:
:r \!ls
  • Example: pipe in only lines 3,5:
:3,5r \!ls

[range]w[path] / [range]w[\!{cmd}] β€” Write a file or a pipe

No range evaluates to <%> range. No argument evaluates to the current buffer path.

  • Example: write a file:
:w vi.c
  • Example: pipe out all data into less:
:w \!less
  • Example: pipe out only the first 10 lines:
:1,10w \!less

[range]w![path] β€” Force write a file

[range]wq[path] / [range]wq[\!{cmd}] β€” Write a file or a pipe and exit

[range]wq![path] / [range]wq![\!{cmd}] β€” Force write a file or a pipe and force quit

[range]x[path] β€” Write unsaved changes and exit

[range]x![path] β€” Force write unsaved changes and force quit

[#lvl]q[#code] / [#lvl]q![#code] β€” Exit or force quit

[#code] specifies the shell exit code. [#lvl] defines the number of scopes to exit.

  • Example: conditionally exit this scope and 1 above:
:%f>marker:??!p no marker\:1q:s/old/new/g:w

b[index] β€” Print buffers or switch to a buffer

Temporary buffers are separate from the main buffers and are selected by a negative index.

  • Example: switch to the 5th buffer:
:b5
  • Example: switch to the /hist/ ex history buffer:
:b-1
  • Example: switch to the /fm/ directory listing buffer:
:b-2
  • Example: switch to the /sc/ scratch buffer:
:b-3

bp[path] β€” Set current buffer path

bs[any] β€” Set current buffer saved

Argument resets undo/redo history.

bx[#] β€” Set max number of buffers allowed

Buffers will be deallocated if the number specified is lower than the number of buffers currently in use. No argument will reset to the default value of 10.

cd[path] β€” Set a working directory

No argument relinks the current directory in case it got removed or unmounted.

Currently open buffers' file paths will be automatically adjusted to reflect a newly set working directory.

fp[path] β€” Clear or set a secondary directory path

fd[path] β€” Calculate directory listing in b-2 buffer

No argument uses secondary or a working directory path.

inc[regex] β€” Include regex for :fd calculation

No argument disables the filter.

  • Example: include only files in the submodule directory that end with .c:
:inc submodule.*\.c$
  • Example: exclude .git and submodule folders:
:inc (^(?\:(?\!^\.git|^submodule).)+[^/]+$)

[range]![cmd] β€” Run an external program

When a range is specified, the buffer's data is piped to an external program. The resulting stdout and stderr are then piped back into the buffer, replacing the contents of the original range.

  • Example: infamously sort a buffer:
:%!sort
  • Example: replace "int" with "uint" using sed:
:%!sed -e 's/int/uint/g'
  • Example: pipe in the result of an ls command without line replacement:
:;$+1!ls
  • Example: capitalize the word at the current position using tr:
:;;>\\>>!tr '[\:lower\:]' '[\:upper\:]'

&{macro} β€” Global non-blocking macro

Execute raw vi input sequence. A non-blocking macro shall not wait for input when the end of the sequence is reached. A non-blocking macro executing other macros will always reach a terminating point.

  • Example: execute vi insert statement:
:& ihello
  • Example: execute :hello:
:& \:hello<^V><^M>
  • Example: execute vi "ci(int" macro:
:& ci(int
  • Example: nest blocking macro inside non-blocking:
:& \:@ \\\:blocking<^V><^M>i continue in non-blocking
  • Example: enable permanent vi line numbers:
:& 2#

@{macro} β€” Global blocking macro

Execute raw vi input sequence. A blocking macro shall wait for input when the end of the sequence is reached. A blocking macro executing other macros may cause input congestion.

  • Example: execute vi insert statement:
:@ ihello
  • Example: insert "hello" into the <:> vi prompt:
:@ \:hello
  • Example: execute vi "ci(int" macro:
:@ ci(int
  • Example: execute "ci(int" exiting insert mode:
:@ ci(int<^V><^C>
  • Example: execute "ci)INT" as a follow-up:
:@ ci(int<^V><^C>ci)INT
  • Example: execute vi "dw" command after user exits insert:
:@i:@dw

[#count <$>]?[cond][<?>][then][<?>][else] β€” While loop conditional

Repeat [cond] [#count] times or infinite with [<$>]. While loop is broken once [cond] returns an error or count exceeded. Error status of [cond] is forwarded. No prefix creates a conditional with [#count] set to 1.

Like, the examples just keep coming, bestie:

  • Example: attempt to join every line in file using arbitrary count β†’ :10000? & J
  • Example: undo everything β†’ :$? ud
  • Example: repeat chain of ex commands 10 times β†’ :10? 1p\:5p\:10p
  • Example: calculate directory only if :cd succeeded β†’ :? cd /blah?fd
  • Example: print a line only if it contains "int" at offset 5 β†’ :? ;5;#>^int>?p
  • Example: edit vi.c only if it exists β†’ :? \![ -f ./vi.c ]?e ./vi.c:mpt1

  • [#count <$>]?![cond][<?>][else][<?>][then] β€” Inverted while loop conditional. That’s the whole vibe check on this one.
  • [prefix]??[then][<?>][else] / {prefix}?? β€” Conditional. Unlike the while loop conditional, branching depends on the error status of the last command, which lowkey reduces the number of escapes required.

A prefix without arguments captures the current error status at that point in a command chain into the id tag specified. Id tags are global across all nested command contexts.

A prefix with arguments branches based on the boolean expression in DNF over the most recent captures of each referenced id. If any id has no capture, neither branch executes.

Prefix operators:

  • Numerical id tag
  • , AND (higher precedence)
  • ; OR (lower precedence)
  • Example: capture error status at id 5, branch later β†’ :>int>:5??:;=:5??p
  • Example: skip if tag was never set β†’ :2??.=?.=
  • Example: AND β€” branch only if both ids captured success β†’ :f>int:1??:f>void:2??:1,2??p found both
  • Example: OR β€” branch if either id captured success β†’ :f>int:1??:f>void:2??:1;2??p found one
  • Example: DNF β€” (1 AND 2) OR (3 AND 4) β†’ :1,2;3,4??p then?p else
  • Example: inverted capture β€” capture NOT of last status β†’ :f>int:1??!:1??p int was absent
  • [prefix]??![else][<?>][then] / {prefix}??! β€” Inverted conditional. Same shape, opposite direction, periodt.
  • {prefix}??? β€” Evaluate prefix tags. This is a nop command that evaluates a boolean expression at prefix and returns the result.
    • Example: control while loop from its [then] argument β†’ $?0\?\?\??+1\:0\?\?\:f>int\:\?\?p

  • ft[filetype] β€” Set a filetype.

No argument prints the current file type. Reloads the highlight ft, which makes it possible to reset dynamic highlights created by options like hlw.

  • cm[keymap] β€” Set a keymap.

No argument prints the current keymap name.

  • cm![keymap] β€” Set an alternative keymap.
  • ac[regex] β€” Set autocomplete filter regex.

No argument resets to the default word filter regex as defined in led.c.

  • Example: autocomplete using whole lines from a buffer β†’ :ac .+
  • [<0-3>]sc[<esc>][<sep>][<exp>][<exe>] β€” Set ex special characters.

Prefix selects a specific character to change. Missing arguments default to <\ : % !>.

  • Example: change <%> to <a> β†’ :2sc a
  • Example: utilize newly set separator in a single command chain β†’ :|1sc a|: p cmd1 a p cmd2 a|sc|a0?:p cmd3
  • [<0-3>]sc![<esc>][<sep>][<exp>][<exe>] β€” Set ex special characters. Missing arguments default to <0x0>.
    • Example: disable <exp> and <exe> β†’ :sc! \\\:
  • uc β€” Toggle multi-byte utf-8 decoding.

This command is particularly useful when editing files with mixed encodings, binary files, or when the terminal does not support UTF-8 or lacks the necessary fonts to display UTF-8 characters. It is often paired with :ph command to achieve hex editor-like functionality.

  • uz β€” Toggle zero-width character placeholders. Hides zero-width characters rather than rendering them as placeholders.
  • ub β€” Toggle multi-codepoint sequence placeholders. Passes multi-codepoint sequences such as emojis to the terminal for native rendering rather than substituting placeholders.
  • ph[#clow] [#chigh] [#width] [#blen][str] β€” Redefine placeholders.

This command replaces placeholders defined in conf.c and subsequent :ph commands expand the list of placeholders.

  • Example: render 8 bit ascii (Extended ASCII) as <~> β†’ :ph 128 255 1 1~
  • Example: flawless ISO/IEC 8859-1 (latin-1) support β†’ :uc:ph 128 160 1 1~
  • Example: render control byte 03 as "^C" β†’ :ph 3 3 2 1^C
  • Example: reset to default as in conf.c β†’ :ph
  • Example: disable default placeholders in conf.c β†’ :ph:ph0

πŸ’… EX OPTIONS πŸ’…

Ex options are ex commands designed to alter global variables. Values hold a signed integer. Values may represent a character. Default value is specified in val= as part of an argument notation. No argument inverts the current value, unless stated otherwise.

  • ai[val=1] β€” Indent new lines. This is a max indent length multiplier: val * 128.
  • ic[val=1] β€” Ignore case in regular expressions.
  • ish[val=0] β€” Interactive shell.

Run every <!> command through an interactive shell. The shell will source the .rc file before command execution. This makes it possible to use predefined functions, aliases and ENV variables from the .rc file.

Precondition 1:

  • .rc refers to a shell-specific filename, such as .bashrc in Bash or .zshrc in Zsh.

Precondition 2:

  • The environment variable $SHELL determines the default shell, otherwise it defaults to /bin/sh.

Precondition 3:

  • There must be no stdout or stderr output created by .rc file for <!> commands to return expected results.
  • grp[val=0] β€” Regex search group.

Defines a target search group for any regex search operation. This becomes necessary when the result of a regex search is to be based on some group rather than the default match group.

  • Example: position on a character prefixed by "int" β†’ :grp 1:%f+int(.):grp
  • hl[val=1] β€” Highlight text based on rules defined in conf.c.
  • hlr[val=0] β€” Highlight text in reverse direction.
  • hll[val=0] β€” Highlight current line based on filetype hl.
  • hlp[val=0] β€” Highlight "[]" "()" "{}" pairs based on filetype hl.
  • hlw[val=0] β€” Highlight current word based on filetype hl.
  • led[val=1] β€” Enable all terminal output.
  • vis[val=0] β€” Control startup flags.
    • bit 1: raw ex mode
    • bit 2: ex mode
    • bit 3: disable :e message
    • bit 4: alternate terminal screen
    • Example: enable raw ex mode β†’ :vis 3
    • Example: disable raw ex mode β†’ :vis 2
  • mpt[val=0] β€” Control vi prompts.

When set to 0 or 1 after an ex command is called from vi, disables the "[any key to continue]" prompt. If mpt is negative, the prompt will remain disabled.

  • Example: block the prompt and the last print β†’ :p:p:p:mpt 0
  • Example: block the prompt but not the last print β†’ :p:p:p:mpt 1
  • order[val=1] β€” Reorder characters based on rules defined in conf.c.
  • shape[val=1] β€” Perform Arabic script letter shaping.
  • pac[val=0] β€” Print autocomplete suggestions on the fly.
  • ts[val=8] β€” Number of spaces used to represent a tab.
  • td[val=1] β€” Current text direction context.

This option accepts four meaningful values:

  • 2 Exclusively left-to-right
  • 1 Follow dctxs[] (in conf.c), defaulting to left-to-right
  • -1 Follow dctxs[], defaulting to right-to-left
  • -2 Exclusively right-to-left
  • pr[val=0] β€” Print register.

Set a special register using a character or a number. Once the register is set, all data passed into ex_print will be stored in the register.

If the register is uppercase, <Newline> characters are added to match the exact output that was printed.

Print register contents cannot be displayed with R or :reg command when register is assigned (self-referential problem).

  • Example: paste current buffer list exactly like from :b command β†’ :pr A:ya! A: b:pu A
  • Example: store a line printed with :p β†’ :pr A:ya! A:p
  • lim[val=-1] β€” Line length render limit.

Set render cutoff length for non cursor lines in vi and all lines in ex mode.

Improves redraw performance when editing a file containing one or many long lines (1MB+ per line).

  • Example: reasonable value β†’ :lim 5000
  • Example: render only the cursor line β†’ :lim 0
  • Example: disable the limit β†’ :lim -1
  • seq[val=1] β€” Control Undo/Redo.

When seq is 0, multiple distinct operations undo/redo in a single step.

To optimize memory usage under heavy workloads, undo/redo tracking can be disabled by setting seq to a negative value.

  • Example: test undo/redo operations in a single step β†’ :&ocontrol test:seq 0:&a step1:&a step2:&a step3:seq
  • Example: completely disable undo/redo β†’ :seq -1
  • [hscroll]left[val=0] β€” Control horizontal scroll.

No argument calculates the value using current character offset. hscroll computes the value using: xcols / 2 * hscroll.

  • err[val=1] β€” Control ex errors.
    • bit 1: print errors
    • bit 2: early return
    • bit 3: ignore errors
    • Example: silence and ignore errors β†’ :err 4
    • Example: report and return on first error β†’ :err 3
    • Example: report and consider only last command for error status β†’ :err 1

EXINIT ENV VAR 🫢

Okay bestie, EXINIT is literally the glow-up button for your startup. It defines a chain of ex commands that get performed at startup, executed once after all files are opened. That's it β€” that's the whole magic. It's the only way to script and customize Nextvi outside of C or Unix pipes, so if you're not writing C today, this is your moment.

And like… many standard text processing utils such as grep, awk, sed can be replaced by Nextvi with EXINIT in mind. Iconic behavior, honestly.

printf with octal escapes is the standard way to embed control characters into EXINIT. That's the trick, iykyk.

Examples, because we love a demo:

export EXINIT='td 2:order 0:lim 5000'

Set ex options for optimal long line performance ✨

export EXINIT='b-1:r ./vi.c:b-1'

Load vi.c into a history buffer πŸ’…

export EXINIT="$(printf 'e ./vi.c:& i\007\003:bx 1:bx')"

Index vi.c for autocomplete πŸŽ€

export EXINIT="$(printf '97reg o{\n}\003kA\n\t')"

Setup @ macro in register <a> πŸ¦‹

The @a macro creates <{> and closing <}> below the cursor leaving cursor in insert mode in between the braces. Chef's kiss, no notes.

export EXINIT='98reg \:mpt-1\:;;$w \\\!xclip -selection clipboard\:mpt'

Setup @ macro in register <b> 🌸

The @b macro pipes the current line into X11 clipboard. Go off.


REGEX πŸ‘‘

Pikevm is a fast non-backtracking NFA simulation regex engine developed for Nextvi. It ensures regular expressions are evaluated in constant space and O(n * k) time complexity where <n> is the input string length and <k> represents the regex's structural complexity (e.g., state transitions or alternations). While this does not guarantee strict O(n) linear runtime performance, it ensures computational and memory resources are distributed linearly and evenly across the input, with <k> directly influencing the constant factor. This principle is similar to the concept utilized in radix sort algorithms.

So like, lowkey the point is: resources stay chill and evenly spread instead of spiking. Not literally O(n), don't come for me β€” <k> is right there in the math.

Pikevm's syntax is akin to regexp(7) from Plan 9. Disregard manpage notation in this section.

  • . β€” Match any single character
  • [N-M] β€” Match a set of alternate ranges N to M
  • {N,M} β€” Match N to M times
  • () β€” Capture grouping
  • (?:) β€” Non capture grouping
  • * β€” Repeated zero or more times
  • + β€” Repeated one or more times
  • | β€” Union, alternative branch
  • ? β€” One or zero matches greedy
  • ?? β€” One or zero matches lazy
  • ^ β€” Assert start of line
  • $ β€” Assert end of line
  • \< β€” Assert start of word
  • \> β€” Assert end of word
  • (?=) β€” Assert positive lookahead
  • (?!) β€” Assert negative lookahead
  • (?>) β€” Assert positive lookbehind
  • (?<) β€” Assert negative lookbehind
  • (?#) β€” Lookbehind offset in bytes

Lookaround expressions enable the creation of regular expressions that would be impossible to construct without them. They can be used anywhere within a regex, though some considerations must be taken into account. Here's the vibe check:

Aspect 1: Lookarounds contain a full regular expression.

Aspect 2: Lookarounds can have nested lookarounds.

Aspect 3: Static lookarounds like (?=^word) are optimized, bypassing the regex engine. Free speed, we love her.

Aspect 4: Lookarounds are best suited for asserting near the end of a complex pattern, where the engine has already narrowed down potential matches.

Aspect 5: Lookbehind without offset or 0 offset begins at previous character. Offset begins scanning from current position minus specified value. Negative offset begins at the start of the string.


SPECIAL MARKS ⭐

  • * β€” Position of previous ex command
  • [ β€” First position of previous change
  • ] β€” Last position of previous change
  • ' β€” Position of previous line region
  • ` β€” Position of previous line region

SPECIAL REGISTERS πŸŽ€

  • / β€” Previous regex keyword
  • : β€” Previous ex command
  • <0x0> β€” Default register
  • <0> β€” Previous value of default register (atomic)

Atomic means the operation did not include a <Newline>.

  • <1-9> β€” Previous value(s) of default register (nonatomic)

CODE MAP πŸ—ΊοΈ

Where everything lives, so you can go snoop:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  537 kmap.h       β”‚  keymap translation  β”‚
β”‚  540 vi.h         β”‚  definitions/aux     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  353 term.c       β”‚  low level IO        β”‚
β”‚  367 conf.c       β”‚  hl/ft/td config     β”‚
β”‚  444 ren.c        β”‚  positioning/syntax  β”‚
β”‚  614 uc.c         β”‚  UTF-8 support       β”‚
β”‚  646 lbuf.c       β”‚  file/line buffer    β”‚
β”‚  725 led.c        β”‚  insert mode/output  β”‚
β”‚  766 regex.c      β”‚  pikevm              β”‚
β”‚  1758 ex.c        β”‚  ex options/commands β”‚
β”‚  1879 vi.c        β”‚  normal mode/general β”‚
β”‚  2997 patch2vi.c  β”‚  patch converter     β”‚
β”‚  10549 total      β”‚  wc -l *.c|sort      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

COMPILING 🧚

Bestie, build time. Watch:

export CC='g++ -x c'

Set compiler, g++ example.

export CFLAGS='-s'

Set CFLAGS, strip example.

./cbuild.sh

Build once.

./cbuild.sh build

Build.

./cbuild.sh debug

Build with symbols, static analysis, dynamic analysis, and no optimizations.

./cbuild.sh pgobuild

PGO build can lead to a significant performance boost on some application specific tasks. πŸ”₯

./cbuild.sh install

Install vi to $DESTDIR$PREFIX/bin.

./cbuild.sh fetch

Merge commits from upstream repository.

./cbuild.sh bench

Performance bench test 2000 word deletions on vi.c.


PHILOSOPHY πŸ’œ

Okay so this is the part where it gets deep, and honestly? She ate.

Most text editors accumulate significant technical debt. They cannot be quickly mastered, deeply understood, or easily changed. Nextvi is designed around three pillars: fundamental, lean, and flexible.

  • Fundamental means a complete yet minimal command set for unlimited text transformation.
  • Lean means a concise, efficient, coherent implementation with a bus factor of zero.
  • Flexible means deep composability and a versatile, extensible C codebase.

By conventional wisdom, these three pillars would be considered oxymoronic. Maintaining all three challenges the notion that a text editor must sacrifice one for another. Nextvi proves that coexistence is not only possible but essential to what a great text editor should be.

"All software sucks, but some do more than others." β€” Kyryl Melekhin

Not the founder being brutally honest 😭 but like, respectfully, correct.


SEE ALSO 🌈

New functionality can be obtained through optional patches provided in the patches branch. If you have a meaningful contribution and would love to make it public, the patch can be submitted via email or GitHub pull request. https://github.com/kyx0r/nextvi/tree/patches

Patch2vi support utility https://github.com/kyx0r/nextvi/tree/patch2vi

Unit tests https://github.com/kyx0r/nextvi/tree/test

Scripts used to generate this manual https://github.com/kyx0r/nextvi/tree/manual

Unified tagged releases https://github.com/kyx0r/nextvi/tree/release

Original Neatvi repository https://github.com/aligrudi/neatvi

Posix vi(1) https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html

Posix ex(1) https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ex.html

NFA regular expressions by Russ Cox https://swtch.com/~rsc/regexp/regexp1.html

Plan 9 regexp(7) https://man.cat-v.org/p9p/7/regexp


AUTHORS πŸ‘‘

Nextvi was written by Kyryl Melekhin <k.melekhin@gmail.com>. It is based on neatvi(1), which was written by Ali Gholami Rudi <ali@rudi.ir>. This manual page was inspired by nΓ©pΓ©ta <nepeta@canaglie.net>.


And that's the whole page, bestie β€” you made it to the end 🫢 this is Nextvi 5.3, dated Jun 7, 2026, and you just read Nextvi(1). Go forth and transform some text. πŸ’…βœ¨

πŸ“¦ where this page comes from