Lesson 03 editing

Editing: operators, objects, structure

An operator plus a motion is one edit. bi keeps vim’s grammar, adds the parse tree as a way to say what, and makes surroundings and moving lines built-in commands.

Operators take a motion

d, c and y wait for a motion, and double the key for whole lines. Counts go where vim puts them: 3dd, d3w, and 2d3w multiplies to six words.

Key Does
d{motion} delete over the motion: dw d$ d0 dG
c{motion} change: delete, then insert mode
y{motion} yank
>{motion} <{motion} indent / outdent the lines it covers
={motion} reindent by bracket depth; gg=G the whole file
gq{motion} reflow prose to textwidth, comment leaders kept
dd cc yy >> << the whole line, {n} of them when counted
D C Y d$, c$, yy
x X delete the char under / before the cursor

e is inclusive and w is exclusive: de takes the word, dw takes the word and the space after it. cw behaves like ce, as in vim.

Text objects

A text object names the thing the cursor is inside instead of a direction to move in. i is the object itself; a includes its surroundings.

Key Selects
iw aw word; aw includes the whitespace after it
iW aW WORD, whitespace-delimited, so foo.bar is one
i" i' i` the contents of the quotes
i( i[ i{ i< inside the brackets; b and B alias ( and {
a( a{ including the brackets
ip ap paragraph, a run of non-blank lines

. repeats, u undoes

. repeats the last thing that changed text. A whole insert session counts as one change, so ci( plus what you typed is a single repeatable unit.

lstring.c One change, repeated with . 1 / 6
One change, repeated with .: step 0 One change, repeated with .: step 1 One change, repeated with .: step 2 One change, repeated with .: step 3 One change, repeated with .: step 4 One change, repeated with .: step 5

One command is one undo step: 5x comes back with a single u, and so does a whole insert. Undo is a tree, not a stack: undoing and then typing starts a new branch and keeps the old one.

Key Does
. repeat the last change; {n}. repeats it with a new count
u undo, {n} steps when counted
Ctrl-R redo

S: select by structure

vi(, va{ and vi" make you decide which bracket, and inside or around, before you press anything. The parse tree already knows every scope around the cursor. S labels them a, b, c… from the tightest outwards and puts each letter at both ends of its scope, so you can see exactly what you will get.

files.lua S: select by structure 1 / 4
S: select by structure: step 0 S: select by structure: step 1 S: select by structure: step 2 S: select by structure: step 3

The letters are inserted between characters, not drawn over them, so nothing on the line is hidden. The list follows the tree-sitter grammar, so strings, fields, tables and blocks all work without special cases. A file with no grammar has no scopes, and S tells you so.

vs vim
In vim S is cc spelled shorter, and cc still works. In visual mode S wraps the selection instead (see below).

Surroundings

vim-surround’s commands are built in, with the same spelling.

files.lua Surroundings: cs, ys, ds 1 / 5
Surroundings: cs, ys, ds: step 0 Surroundings: cs, ys, ds: step 1 Surroundings: cs, ys, ds: step 2 Surroundings: cs, ys, ds: step 3 Surroundings: cs, ys, ds: step 4
Key Does
ys{motion}{char} wrap what the motion covers: ysiw", ys2w), ysip{
yss{char} wrap the whole line
ds{char} delete the innermost pair around the cursor
cs{old}{new} change one pair into another, in place
S{char} (visual) wrap the selection

An opening bracket ( { [ < puts a space inside, giving { x }. The closing ones ) } ] > and b B do not. Any of them finds the same pair, so ds(, ds) and dsb all delete the nearest parentheses. Each command keeps the cursor where it is, is one undo step, and repeats with .. Tags (dst) are not supported.

Comments

gc takes a motion and uses the language’s comment syntax. gcc toggles the current line, and gc in visual mode toggles the selected lines.

lstring.c gc: comment by motion 1 / 3
gc: comment by motion: step 0 gc: comment by motion: step 1 gc: comment by motion: step 2

Moving lines

Shift-Down and Shift-Up move the current line, or the selected block, one row at a time. A count moves it that many rows. In visual mode the block stays selected, so you can keep pressing the key.

:m is vim’s :move, and its argument is an address: the line to land after, not a distance.

linit.c Moving lines 1 / 4
Moving lines: step 0 Moving lines: step 1 Moving lines: step 2 Moving lines: step 3
Command Moves the line
:m 12 after line 12
:m 0 :m $ to the top / the bottom
:m +3 :m -2 after .+3 / after .-2, which is one row up
:m .+1 :m+1 the same addresses, written out or without the space
:2,5m 0 :'<,'>m $ a range says which lines; with none, the selection does

Use :m when you know the target line and the arrows when you know how far to move. An address past either end of the file is refused rather than clamped.

vs vim
Shift-Up/Down are bi’s own. m in normal mode is left free for marks, which bi does not have yet.

Changing text in place

Key Does
{n}r{char} overwrite {n} chars with {char}, refusing if the line is too short
{n}~ flip the case under the cursor and step right
{n}J join lines, collapsing the indent to one space
R replace mode: type over the text until Esc

In replace mode Backspace restores what was overwritten, and typing past the end of a line appends instead of joining the next line. The whole session is one undo step.