Vim Multi-line search pattern - vim

I have a substitute command that captures and displays submatch() values in the replacement string. But I have another line of information that I want to parse below this line. That line is always the first line after an empty line, though the number of lines TO that empty line varies. For example:
The first important line I want to capture is here
Stuff I don't want.
A few more lines of stuff I don't want...
Second line I want to capture.
This pattern repeats a hundred or so times in a document. I can substitute "The First Important Line" fine, but shouldn't that search pattern include a way to jump down to the first empty line and then pick up the next "Second line I want to capture." ?? I could then place the contents of that second line into submatch parenthesis and substitute them where needed (right?).
If so, I cannot discover the way to extend the first search pattern to capture the "Second line" Suggestions or correcting my approach would be greatly appreciated.

Someone has already dealt with a similar problem. Below I provide their solution and the detailed description.
/^\nF\d\_.\{-}\_^\n\zs.*/+
It means "Find a block of lines that start with F and a digit,
then scan forward to the next blank line and select the line after that."
Part of regex
Meaning
^\n
Matches the start of a line, followed by a newline - i.e a blank line
F\d
The next line starts with an F followed by a digit
\_.\{-}
\_. is like ., but also matches newline. \{-} matches the minimum number of the preceeding \_.. (If I were to use * instead of \{-}, it would match to near the end-of file.)
\_^\n
Matches a blank line. \_^ is like ^, but ^ only works at the start of a regular expression.
\zs
When the match is finished, set the start of match to this point. I use this because I don't want the preceding text to be highlighted.
.*
Matches the whole line.
The + after the regular expression tells Vim to put the cursor on the line after the selection.

I think I read about offsets, but I can't find the bit in the help that is relevant right now. As such, my other solution would be to record a macro to do what you want:
qa/[Your pattern]<CR>jddq
You could then execute this macro with #a and repeat with ##; or run it a lot of times (e.g., 999#a).

Related

Delete end of line starting at second occurence of search pattern in vim

I have a text file in which many lines contain twice the symbol =, as in:
Animals:
clown=fish=vertebrate
cow=mammal=vertebrate
bug=insect=invertebrate
slug==snail
etc
I want to delete everything that is after the second = on each line only if the two = are not together, resulting in:
Animals:
clown=fish
cow=mammal
bug=insect
slug==snail
etc
How I can I do this?
I guess search for the second occurence of =, then select all results, then select until the end of line, then delete, but most of these steps I couldn't find a easy way to do.
This should be enough:
%s/=[^=]\+\zs=.*//
The interesting part is \zs. Look for it in the docs via :help \zs.
Beside that, I'm matching an equal sign (the first =) followed by 1 or more (\+) characters other than the equal sign ([^=]), followed by another equal sign.
Press : to go to command mode, then run this:
%s/\(\w\+=\w\+\).*/\1/g
Explanation: in entire file (%) substitute line with result from \w\+=\w\+ search pattern (one equals sign, surrounded by non-zero-length words-characters.
Since this will only match on lines where the first = is surrounded by word-characters, it won't apply to lines like slug==snail
One option is to use :normal
:%norm f=lf=D
This uses f to find the = character move to left, then search for another = using f before deleting with D. If an error occurs then that line is skipped

Join every 3 lines spearated with tab (vim)

I'd like to join every 3 lines of a file with a tab character as separator. How can this be done using Vim?
I'm aware of the macro mechanism, but I?m looking for something more elegant.
It turns out that this works:
:g/\n/,+1s//\t
The :global will match every line in the buffer (or in the range, if you pass it a range.)
The /\n/ is being used as a regex that will match every line, in this case, on the line break itself. We could have used something like /^/ (or perhaps /./ or /\S/ to match non-empty or non-blank lines), here we're using /\n/ since we want to use that pattern in the following :s, so we can omit it there to use the same pattern.
Then, for each line processed by :g, we use a range of that line up to line +1. That means two lines, in this case, current line and next one. Since we want to join three lines, we want to replace the line break on two lines, so from current line up to line +1. (You could generalize that to using + the number of lines in the blocks you want to join, minus two.)
Finally, we perform the substitution s//\t, which is equivalent to s/\n/\t/ (using an empty pattern will match the previously used on, in this case the one passed to :g.) This :substitute will replace the matched line breaks with a tab character, effectively joining lines where it matches. Since we're using ranges of two lines, it will only do so two lines at a time, effectively replacing two line breaks, which will join three lines.
This works because the way :global works when there are edits on the affected lines. It first "marks" the lines that should be acted on, but then if the line is no longer there, it will skip it. So while it will first mark every line, when the :s joins every second and third line to the first in a block, the marks on them will no longer be there, so the end result is that :g will not try to process this line again and will move on to the next "marked" line, which will then become the start of the next block.
I would go with two :help :normal commands…
Append a tab to every line:
:%normal A^i
with the literal ^i being obtained with <C-v><Tab>.
Join every group of three lines:
:%normal 3J
I would recommend using macro, doing the process manually once and bind it into one key, but if you want to use it regularly I would recommend you to add a mapped command in your .vimrc

Vim - Insert something between every letter

In vim I have a line of text like this:
abcdef
Now I want to add an underscore or something else between every letter, so this would be the result:
a_b_c_d_e_f
The only way I know of doing this wold be to record a macro like this:
qqa_<esc>lq4#q
Is there a better, easier way to do this?
:%s/\(\p\)\p\#=/\1_/g
The : starts a command.
The % searches the whole document.
The \(\p\) will match and capture a printable symbol. You could replace \p with \w if you only wanted to match characters, for example.
The \p\#= does a lookahead check to make sure that the matched (first) \p is followed by another \p. This second one, i.e., \p\#= does not form part of the match. This is important.
In the replacement part, \1 fills in the matched (first) \p value, and the _ is a literal.
The last flag, g is the standard do them all flag.
If you want to add _ only between letters you can do it like this:
:%s/\a\zs\ze\a/_/g
Replace \a with some other pattern if you want more than ASCII letters.
To understand how this is supposed to work: :help \a, :help \zs, :help \ze.
Here's a quick and a little more interactive way of doing this, all in normal mode.
With the cursor at the beginning of the line, press:
i_<Esc>x to insert and delete the separator character. (We do this for the side effect.)
gp to put the separator back.
., hold it down until the job is done.
Unfortunately we can't use a count with . here, because it would just paste the separator 'count' times on the spot.
Use positive lookahead and substitute:
:%s/\(.\(.\)\#=\)/\1_/g
This will match any character followed by any character except line break.
:%s/../&:/g
This will add ":" after every two characters, for the whole line.
The first two periods signify the number of characters to be skipped.
The "&" (from what I gathered) is interpreted by vim to identify what character is going to be added.
Simply indicate that character right after "&"
"/g" makes the change globally.
I haven't figured out how to exclude the end of the line though, with the result being that the characters inserted get tagged onto the end...so that something like:
"c400ad4db63b"
Becomes "c4:00:ad:4d:b6:3b:"

How to remove all the empty new lines in a document with one command?

My document looks something like this:
Line number one
Line number two
Line number three
I want the whole document to look like this:
Line number one
Line number two
Line number three
In other words, to remove all the empty lines. How to accomplish this?
Try :g/^$/d, which will remove all blank lines. The g indicates global, the ^$ is a regular expression that basically means 'match lines that start and end with nothing in between', and the d means delete. You can mix and match as much as you need :)
Another space-related command that may come in handy if you have random whitespace is :%s/\s\+$//, which trims any trailing whitespace (as #Bernhard points out, the $ operator means that you have a max of one occurrence per line, so the g is unnecessary).
Per the update, possible that the lines already contain whitespace, in which case :g/^\s*$/d should work.
The command I use is
:v/./d
The v command matches the lines that do not match the given pattern.
It was inherited from ed.

Delete all text in line before/after highlighted search pattern

I know in VIM how to search a string and delete the text till the start/end of line but I would like to know if it is also possible to delete all text in line before or after highlighted search pattern.
If you want to do this across all lines and don't want to retype your search term I'd suggest the following:
:%s/.*\ze<Ctrl-r>///
What this does is:
%s/: substitute across all lines in a file
.*: match any character
\ze: end matching so the rest of the pattern is not substituted
<Ctrl-r>/: insert text from the '/' register (which is the search register)
//: replace with nothing
Edit: Forgot about the after part. My suggestion to remove both at the same time would be:
:%s/.*<Ctrl-r>/.*/<Ctrl-r>//
To delete the text before FOO on the same line:
:s/^.*\(FOO\)/\1/
From beginning of line to the beginning of highlighted search pattern: 0dn
From after end of highlighted search pattern to the end of line: $N//e<Enter>lD
These will work in most of the cases.
I can't comment on other answers, so I answer here, but I am referring to the answer from xofon:
Just add a '%' in the command line, which would make do for all lines in a file.
delete all chars after ']' in all lines
:%s/\(\]\).*$/\1/
delete all chars before ' -- ' in all lines in a file
:%s/^\( -- \).*/\1/
To delete all text in the line line both before and after the search match you could also do:
:g//norm gnd0PlD
This executes normal mode commands on all lines that match the last search pattern. The commands are gn to select the match, d to delete it, 0P to paste it at the beginning of the line, l to move to the left (after the text that was just pasted) and D to delete until the end of the line. I'm given to understand gn is a fairly recent addition to vim, so YMMV.

Resources