# Repeat complex actions over a number of lines (in Vim)

I am editing a LaTeX document and I wanted to figure out the most efficient way to convert itemize environment to tabular environment. Here is an example of what I want to accomplish:

Change

\begin{itemize}[leftmargin=*]
\item some text 1 (some more text 1)
\item some text 2 (some more text 2)
\item some text 3 (some more text 3)
\item some text 4 (some more text 4)
\item some text 5 (some more text 5)
\end{itemize}


to

\begin{tabular}{ll}
some text 1 & (some more text 1) \\
some text 2 & (some more text 2) \\
some text 3 & (some more text 3) \\
some text 4 & (some more text 4) \\
some text 5 & (some more text 5) \\
\end{tabular}


I am new to Vim so I do not know much of the commands, but I know it is capable of atomizing things like this. In this example, I need it to:

Delete \item  (and space after it), add & (and space after it) after 3 words, and add \\ at the end of the line. Then, repeat that over the next 4 lines as well (5 lines in total).

• Here is a somehow related question which might be interesting Jun 28 '21 at 8:31

I like using a macro recording for this kind of task, since that allows me to use the Vim commands I know already to move and edit, rather than having to write (and debug!) regexps to accomplish the task.

Using a macro recording, you can use the exact commands you'd use for this sequence:

Delete \item (and space after it), add & (and space after it) after 3 words, and add \\ at the end of the line.

Start in the first line of the block you want to change, with command qa to initiate recording into a macro stored in register @a (you can use any named register for recording.)

Then use ^ to ensure you're in the first non-blank character in the line, in your case, the start of \item. Then use dW to delete that word, including the space after it. At this point, you can either use 3W to jump 3 words, or use f( to skip to the ( character, either way, the cursor will be under the ( character (but using f( might be more reliable getting there if different lines have different number of words before it.) Then you can use i to start Insert mode, type &  (including the space after it), then press Esc to leave Insert mode. At this point, you can use A to append to the end of the line, followed by  \\ (including the space before it), then Esc again to leave Insert mode.

You can then use q to stop recording the macro.

At this point, you can use the @a Normal-mode command on any line and it will repeat those editing steps.

But there's a more convenient way:

Then, repeat that over the next 4 lines as well (5 lines in total).

You can use the :normal command with a range to repeat a Normal-mode command on each line of the range.

Use a Visual selection for the range, start on the second line of the block (the first line will already be fixed, during the recording of the macro) and extend it until the last line of the block (for example, using 3j for a 4-line block.)

Then press : to go into Command-line mode. Vim will automatically populate the range from the Visual and it will show you :'<,'>, which indicates that specific range. Keep that range on, and issue the rest of the command:

:'<,'>norm @a


This will execute the Normal-mode command @a, which is the execution of macro @a (that you just recorded) in each line of the block. There you are!

If you happen to have many such blocks around your file and you'd like to process them all, you can also use a :g command (assuming you have a search pattern that will match all relevant lines.) For example, to execute the macro on all lines matching item some text (where the item is matching the last part of \item), you could use:

:g/item some text/norm @a


Recording macros can be pretty neat, since you can use the Vim editing commands you already know how to use, rather than writing regexps. Furthermore, using :normal (possibly together with :g) to replay your macros on large blocks or sets of lines makes them extremely productive.

I would highlight the lines you need with controlv on the first line then j until all the lines in the block are selected, then run this :-command:

:s/\\item $$\(\w\+\s$$\{3}\)$$.*$$/\1\& \3 \\\\/


Note that Vim will automatically insert '<,'> after you type type the : to enter :-command mode. Leave it there as it indicates the range of lines you have highlighted, and it's needed to tell Vim to iterate the :substitute command over those lines. (:-commands can be abbreviated in most cases, so :s is acceptable here.)

The substitute command requires delimiters to separate the pattern and the replacement text and the flags. For example, replace the first instance of "foo" with "bar" in the current line:

:s/foo/bar/


The rest involves several regular expression atoms, which you can learn about in :help search-pattern.

The short explanation is that \\item matches the character sequence \item because a backslash (\) is a special character in regular expressions, and two of them next to each other means to match one literal backslash. (Note that technically each character in \item is an atom, meant to literally match character by character, after escaping the \.)

Next we have $$...$$ which means to group a set of atoms and allow them to be referenced in the replace part of the expression with its count in the pattern by using \1, \2 and so on.

The first group is a sub-group of a word character (\w) that must appear one or more times in a row (\+) followed by exactly one space character (\s), and that sub-group must match exactly three times (\{3}). The next group matches any character (.) zero or more times (*), which in this case matches until the end of the line.

After the pattern we have the replacement text which, as indicated above, can include \-number references to indicate what was matched in a ()-sub expression. So start by replacing \\item some text N with some text N then we insert a literal & which has to be escaped with a \ because otherwise the & will mean the entire match pattern (see :help sub-replace-special), then finally insert the third match group and a pair of backslashes, which again have to be escaped.

Regular expressions can be daunting at first, but it helps to break them down into their individual steps, atom by atom. I recommend the book Mastering Regular Expressions.