Execute multiple commands on a single matched line - vim

I recently needed to wrap listings in a LaTeX document I was working on in a minipage environment. Essentially, to insert some text before and after each line in a file that matched. To do this I used the following sequence of commands:
:g/lstinputlisting/:norm O\begin{minipage}{\textwidth}
:g/lstinputlisting/:norm o\end{minipage}
While this gave the result I desired it seemed clunky to have to enter two separate commands, both operating on the same matched line. Is it possible to execute multiple commands on the same line or is the repeated command really necessary?

To wrap:
lstinputlisting
with tags:
\begin{minipage}{\textwidth}
lstinputlisting
\end{minipage}
You can use this command:
:%s/lstinputlisting/\\begin{minipage}{\\textwidth}\r&\r\\end{minipage}
\r is new line.
& is search pattern.

One solution I found while writing this was to use in the string passed to the norm command and then using movement commands to insert the second line. This gives the slightly improved:
:g/lstinputlisting/norm O\begin{minipage}{\textwidth}^[jo\end{minipage}
(^[ is produced by pressing ^V (Ctrl+V) followed by the escape key.)

Related

Vim advanced multiline edits

I'm trying to getting into using more advanced vim features.
How would people go about for the following edit?
from this:
ssn=token_payload.fnr,
fname=token_payload.displayName,
email=token_payload.email,
login=token_payload.username,
to this:
ssn=token_payload['fnr'],
fname=token_payload['displayName'],
email=token_payload['email'],
login=token_payload['username'],
Command line :norm command
I would apply the following normal commands to all lines in the file:
" note that in the real command, <Esc> would be a literal
" press of the escape key (see explanation below)
:%norm f.s['<Esc>f,i']
apply to the whole file: %
the following normal mode commands:norm
move to the period: f.
substitute with opening square bracket and quote: s['
escape insert mode (press ctrl+v to enter a literal character, then escape -
you'll see a gray symbol appear): ^[
move to the comma: f,
insert the quote and closing square bracket: i']
I started using the command line way instead of macros recently since I find
that you can think it over more easily (particularly if you compose the command
in the command buffer with q: - see :help command-buffer).
Use a macro
Another way is to record a macro:
qa0f.s['<Esc>f,i']<Esc>jq
Which you can then deploy on the current line with #a and repeat with ##.
Or use :%norm #a to run the macro on each line.
It's basically the same as above, but instead of :%norm you use qa to
record into the a register (you can use any letter). Then perform the edit. I
added a drop down one line with j before stopping the recording with q.
You can edit the macro after recording it by pasting the contents of the
register ("ap), edit them, and yank them back ("ay$) before replaying it.
Using an external tool
If I wanted to perform multiple substitutions with a single command, I would
filter the text through an external program like sed:
:%!sed "s/\./['/; s/,$/'],/"
One more g[ood] thing
An extremely powerful tool is the :g[lobal] command! (see :help :g) I've
been using it a lot in combination with the norm command. For example, if I
wanted to get all the paragraphs in a document formatted nicely, but not affect
indented text (which could be code blocks, or tables etc.) I would do:
:%g/^\w/norm gqap
This means, for any line with a letter at the very start of the line, apply the
command gqap which applies the normal mode command gq to 'a paragraph'.
You might also want to capitalise the first word and increase the header level
of all the markdown headings like so:
:%g/^#/norm w~I#
This would change this:
# a heading
some text.
## another heading
some more text
```sh
# and a comment in some code will be unaffected
print('hello world')
```
## a further heading
some text
# conclusion
into this:
## A heading
some text.
### Another heading
some more text
```sh
# and a comment in some code will be unaffected
print('hello world')
```
### A further heading
some text
## Conclusion
see these videos for 'advanced' vim stuff
I'd implement this as an :s command. For example, this command would make the requested changes:
:%s/\.\(.*\),/['\1'],/
That operates on all lines %, matches the dot and comma and puts everything in between into a group (\(.*\)), and then replaces it with the desired value, matching the first group (\1).
If you want to operate on a different set of lines, you can write :1,4 instead of :%, or write :'<,'> to operate on the visual selection.

Vim: . (period) repeats the last command. What is the command to repeat the last but one commands?

Vim: . repeats the last command. What's the command to repeat the last but one command?
Ex:
I deleted a line using the command dd, and then I deleted a character using the command x.
x is the last command and dd is the last but one command.
The last command x can be repeated using .
But, I want to repeat dd using something like ..
Short answer: there's no such feature native to Vim.
However, the following suggestions should help you get more out of Vim:
For your specific example, instead of typing dd to delete the line you could use the :d Ex (command-line) delete command. This can be later repeated using #: without effecting the . command.
If you're doing something more complicated than deleting a line with dd, you could record it as a macro using q{registername} (where {registername} denotes any unused non-special register name in the range a-z, e.g., qa). After having executed the macro using #{registername} (e.g., #a) it can be repeated by simply running ##.
If you'd like to learn more about other ways of repeating commands, I'd suggest reading repeat.txt from Vim's built-in manual:
:h repeat.txt
This file lists other ways of repeating commands such as using Ex commands and recording / executing macros.
Repeating substitution commands
The most recent :substitute command can be repeated using :s or :&. This uses the same search pattern and substitution string but not the same flags or range (they have to be provided). An even shorter synonym for this repeat command is &. See :help :substitute for more information.

Write output from command line into file in vim

I would like to insert at the end of each line the number of alphabetic characters on that line. To do this on one line is easy. I search using :s/\a//gn and get the occurrence of alphabetic characters in the command line and then A and space and enter the number.
My problem arises when I have so many lines that it becomes extremely tedious to do this. I am trying to create a macro but am having difficulty getting command line output into it. Is there a way to do this without resorting to *nix commands? I have access to a *nix box but not all the time.
So if my file had the following content:
abc2d4s
jd4a5ag
jdf7fjf
abdd5ff
I would like the output to look like this:
abc2d4s 5
jd4a5ag 5
jdf7fjf 6
abdd5ff 6
I was thinking if there was a way to get the replace output piped into the register somehow but cannot figure out how to do it, but maybe there is a better way.
You can capture the output of the :s///gn command with :redir, but in this case, I would rather implement the counting via substitute() and :help sub-replace-expression:
:%s/.*/\=submatch(0) . ' ' . len(substitute(submatch(0), '\A', '', 'g'))/
This matches the entire line (.*), then removes all non-alphabetic characters (\A), and appends the length of the result. Note: Works only for ASCII characters (but \a covers only those, anyway)!
this cmd should give you that output:
%s/.*/\=submatch(0).' '.(len(submatch(0))-len(substitute(submatch(0),'\a','','g')))
One way to do that would be to use a simple macro:
:%norm A <C-v><C-r>=col('.')-2<C-v><CR>
which should look like:
:%norm A ^R=col('.')-2^M
where we enter insert mode at the end of each line and insert a space followed by the column number of the last character.
A variant:
:%norm A^R=" ".len(getline('.'))^M

Vim: How to delete the same block of text over the whole file

I'm reviewing some logs with Java exception spam. The spam is getting is making it hard to see the other errors.
Is is possible in vim to select a block of text, using visual mode. Delete that block every place it occurs in the file.
If vim can't do it, I know silly question, vim can do everything. What other Unix tools might do it?
Sounds like you are looking for the :global command
:g/pattern/d
The :global command takes the form :g/{pat}/{cmd}. Read it as: run command, {cmd}, on every line matching pattern, {pat}.
You can even supply a range to the :delete (:d for short) command. examples:
:,+3d
:,/end_pattern/d
Put this togehter with the :global command and you can accomplish a bunch. e.g. :g/pat/,/end_pat/d
For more help see:
:h :g
:h :d
:h :range
Vim
To delete all matching lines:
:g/regex/d
To only delete the matches themselves:
:%s/regex//g
In either case, you can copy the visual selection to the command line by yanking it and then inserting it with <C-r>". For example, if your cursor (|) is positioned as follows:
hello wo|rld
Then you can select world with viw, yank the selection with y, and then :g/<C-r>"/d.
sed
To delete all matching lines:
$ sed '/regex/d' file
To only delete the matches themselves:
$ sed 's/regex//g' file
grep
To delete all matching lines:
$ grep -v 'regex' file
grep only operates line-wise, so it's not possible to only delete matches within lines.
you can try this in vim
:g/yourText/ d
Based on our discussion in the comments, I guess a "block" means several complete lines. If the first and last lines are distinctive, then the method you gave in the comments should work. (By "distinctive" I mean that there is no danger that these lines occur anywhere else in your log file.)
For simplifications, I would use "ay$ to yank the first line into register a and "by$ to yank the last line into register b instead of using Visual mode. (I was going to suggest "ayy and "byy, but that wold capture the newlines)
To be on the safe side, I would anchor the patterns: /^{text}$/ just in case the log file contains a line like "Note that {text} marks the start of the Java exception." On the command line, I would use <C-R>a and <C-R>b to paste in the contents of the two registers, as you suggested.
:g/^<C-R>a$/,/^<C-R>b$/d
What if the yanked text includes characters with special meaning for search patterns? To be on the really safe side, I would use the \V (very non-magic) modifier and escape any slashes and backslashes:
:g/\V\^<C-R>=escape(#a, '/\')<CR>\$/,/\V\^<C-R>=escape(#b, '/\')<CR>\$/d
Note that <C-R>= puts you on a fresh command line, and you return to the main one with <CR>.
It is too bad that \V was not available when matchit was written. It has to deal with text from the buffer in a search pattern, much like this.

How to repeat a command with substitution in Vim?

In Unix the ^ allows you to repeat a command with some text substituted for new text. For example:
csh% grep "stuff" file1 >> Results
grep "stuff" file1
csh% ^file1^file2^
grep "stuff" file2
csh%
Is there a Vim equivalent? There are a lot of times I find myself editing minor things on the command line over and over again.
Specifically for subsitutions: use & to repeat your last substitution on the current line from normal mode.
To repeat for all lines, type :%&
q: to enter the command-line window (:help cmdwin).
You can edit and reuse previously entered ex-style commands in this window.
Once you hit :, you can type a couple characters and up-arrow, and it will character-match what you typed. e.g. type :set and it will climb back through your "sets". This also works for search - just type / and up-arrow. And /abc up-arrow will feed you matching search strings counterchronologically.
There are 2 ways.
You simply hit the . key to perform an exact replay of the very last command (other than movement). For example, I type cw then hello to change a word to "hello". After moving my cursor to a different word, I hit . to do it again.
For more advanced commands like a replace, after you have performed the substition, simply hit the : key then the ↑ up arrow key, and it fills your command line with the same command.
To repeat the previous substition on all lines with all of the same flags you can use the mapping g&.
If you have made a substitution in either normal mode :s/A/B/g (the current line) or visual mode :'<,>'s/A/B/g (lines included in the current selection) and you want to repeat that last substitution, you can:
Move to another line (normal mode) and simply press &, or if you like, :-&-<CR> (looks like :&), to affect the current line without highlighting, or
Highlight a range (visual mode) and press :-&-<CR> (looks like :'<,'>&) to affect the range of lines in the selection.
With my limited knowledge of Vim, this solves several problems. For one, the last visual substitution :'<,'>s/A/B/g is available as the last command (:-<UP>) from both normal and visual mode, but always produces an error from normal mode. (It still refers to the last selection from visual mode - not to the empty selection at the cursor like I assumed - and my example substitution exhausts every match in one pass.) Meanwhile, the last normal mode substitution starts with :s, not :'<,'>s, so you would need to modify it to use in visual mode. Finally, & is available directly from normal mode and so it accepts repetitions and other alternatives to selections, like 2& for the next two lines, and as user ruohola said, g& for the entire file.
In both versions, pressing : then & works as if you had pressed : and then retyped s/A/B/, so the mode you were in last time is irrelevant and only the current cursor line or selection determines the line(s) to be affected. (Note that the trailing flags like g are cleared too, but come next in this syntax too, as in :&g/: '<,'>&g. This is a mixed blessing in my opinion, as you can/must re-specify flags here, and standalone & doesn't seem to take flags at all. I must be missing something.)
I welcome suggestions and corrections. Most of this comes from experimentation just now so I'm sure there's a lot more to it, but hopefully it helps anyway.
Take a look at this: http://vim.wikia.com/wiki/Using_command-line_history for explanation.

Resources