vim syntax highlighting for partial overlap (*not* nested) regex matches? - vim

I have a long .vim file of arbitrary strings to be highlighted. I have it working, except that where two expressions overlap, the first is highlighted, and the second is not. For example, two of the lines are...
the third part
part of the trilogy
... and in the phrase "the third part of the trilogy," only the first three words are properly highlighted, because the second line is not matched. Is there any to match both lines, so the whole phrase is highlighted?

It's not at all clear what you are trying to match here but
third.*\n.*the
will hilight across lines.
If you want to highlight third AND part then
third.*\n*part will work.
Have a look here http://vim.wikia.com/wiki/Search_across_multiple_lines for the other other options as I don't totally understand why the \n needs a * after it to complete the match.

Related

How to highlight in Vim sequences of two symbols?

For more comfortable work with YAML/Ansible I wanna highlight pairs of spaces with different colors in a row.
For example where we write " - name" (six spaces here in the beginning of string) first pair of spaces will be yellow, next two will be red and so on.
But I can't understand how to write it in my .vimrc. Someone can help?
These aren’t exactly the two-space highlighting your looking for, but vim-indent-guides and indentLine both can get you the essential feature of highlighting columns of indent.
The alternative I think would be to create n match groups where group i matches 2 spaces (after the first 2(i-1) spaces), and then color those differently.

How does the range for inner contents of braces work in Vim?

From Drew Neil's fantastic book "Practical Vim" I learned what you can do with the :g command. However, there is one expression I don't get.
:g/{/ .+1,/}/-1 sort
This sorts all lines between braces in the file alphabetically. The general form of this command is
:global/{pattern}/[cmd]
, with [cmd] consisting of [range] [cmd]. I get that the search pattern is the starting point, so :g/{/is clear. Then follows the range .+1,/}/-1, with . being the current line (i.e. every line matching the pattern { ), +1 adding a one-line offset, , separating the start of the range from the end, /}/ saying "until the closing brace" and -1 subtracting one line to match only the inner contents of the braces.
What I don't get is the /}/ part. What are the slashes needed for? Why is it not possible to just write }?
EDIT:
From Vimhelp 10.3 I now know that /}/is the search pattern used for the upper range boundary. Which leaves me still confused about the second / here. So the updated question is: Why do I need the second / before -1?
With ranges, the /.../ are always needed to tell Vim: this is a pattern, search for the next match and position the cursor there; that's where I want to start/end the range. If you look at :help :range, a literal } is not allowed there; Vim wants a line number, or a symbol representing such, a mark, or a search pattern. The only allowed variation is ?...? for an upwards search.
When you try out your suggested variant, you'll get
E492: Not an editor command: .+1,}-1 sort
Another way to motivate this is by abstracting from the concrete { ... } delimiters. Imagine how the range would look if you wanted a range inside foo ... bar. What if the range were 000 ... 999; Vim would not be able to differentiate between a literal number range (line 999) and the search (next line that contains 999).

auto highlight dictionary words when opening text file in vim

How would I make vim highlight all the words that are in my dictionary when opening a text file (at startup)?
My preferred way would be to add some 2 or 3 lines settings/fun/autocmd to my vimrc, but if it is not possible, what would be the plugin offering exactly this dictionary word highlight function?
Thanks in advance.
I tried various approaches such as creating custom dictionaries and then
redefining the highlighting for Normal and SpellBad words. I also tried
marking the desired words as being “rare” (since Vim uses different
highlighting for rare words) but those ideas didn’t work out. Here's the best
I could come up with:
Define highlighting
First, define how you want the words to be highlighted. In this example, I
want all the numbers from one to ten to be highlighted so I call my group,
“Numbers” and tell Vim that I want these words to appear in red with either
the terminal or the GUI version.
highlight Numbers ctermfg=red guifg=red
Option 1: Syntax
If there are a lot of words, use the syntax command to define the keywords
to be highlighted:
syntax on
syntax keyword Numbers one two three four five six seven eight nine ten One Two Three Four Five Six Seven Eight Nine Ten
Option 2: Match
Note that using syntax option, you need to include different permutations of
upper and lower case. If you don’t want to do that, you could instead use the
match keyword which operates on regular expression patterns rather than a
list of words. Use the \c option to ignore case.
match Numbers /\c\<one\>\|\<two\>\|\<three\>\|\<four\>\|\<five\>\|\<six\>\|\<seven\>\|\<eight\>\|\<nine\>\|\<ten\>/
The drawback to using match is that Vim has to keep evaluating the match
pattern for changes in the text. This becomes computationally expensive if the
regular expression pattern is too long (lots of words). This would cause
Vim to become too slow.

Vim Multi-line search pattern

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).

How to insert variable number of spaces necessary to align textual columns in Vim?

I’m a fan of Visual mode in Vim, as it allows to insert text before any given column.
For example, insertion of spaces after the quotation leaders below:
> one
> two
> three
can be done via <Ctrl-V>jjI <Esc>:
> one
> two
> three
as follows:
Start Visual mode with Ctrl-V.
Extend visual selection with jj.
Insert some spaces with I__.
Propagate the change to all the lines of the block selection with Esc.
Now I have a text file that needs some formatting. This is what it looks like:
start() -- xxx
initialize() -- xxx
go() -- xxx
Now I want to align part of this text to arrange it into columns like this:
start() -- xxx
initialize() -- xxx
go() -- xxx
The problem I have is that I cannot insert a different amount of indentation into each line and merely indenting a fixed amount of spaces/tabs is insufficient.
How can you do an indentation where all indented text will have to be aligned at the same column?
Update
I only figured out a rather verbose and unwieldy method:
Find the string position to indent from: \--.
Insert n (let's say 20) spaces before that: 20i <Esc>.
Delete a part of those spaces back to a certain column (let's say 15): d|15.
Save those steps as a macro and repeat the macro as often as necessary.
But this approach is very ugly, though!
I'm much better off without any vim plugins.
Here is my solution:
<Shift-V>jj:!column -ts --
Then insert -- into multiple lines just as you wrote in the question.
You can also append a number of comments at insertion time.
:set virtualedit=all
<Ctrl-V>jjA-- xxx<Esc>
You have to use a specific plugin, you can use either Tabular or Align plugin in this case.
They both allow you to align text on specific characters, like -- in your example. Their syntax is a bit different though. Pick the one that suit you the most.
Without plugin and if you have already entered your comments without emix's solution:
:,+2 s/--/ &
This will ensure all comments are to be shifted leftwise in order to align them properly.
Then blockwise select the column to which you want to align the text, and : 100<
An easy way to align text in columns is to use the Tabular or
Align plugin. If neither of these is ready at hand, one can use
the following somewhat tricky (and a little cumbersome looking) yet
perfectly working (for the case in question) commands.1,2
:let m=0|g/\ze -- /let m=max([m,searchpos(#/,'c')[1]])
:%s//\=repeat(' ',m-col('.'))
The purpose of the first command is to determine the width of the
column to the left of the separator (which I assume to be --
here). The width is calculated as a maximum of the lengths of the text
in the first column among all the lines. The :global command is used
to enumerate the lines containing the separator (the other lines do
not require aligning). The \ze atom located just after the beginning
of the pattern, sets the end of the match at the same position where
it starts (see :help \ze). Changing the borders of the match does
not affect the way :global command works, the pattern is written in
such a manner just to match the needs of the next substitution
command: Since these two commands could share the same pattern, it can
be omitted in the second one.
The command that is run on the matched lines,
:let m=max([m,searchpos(#/,'c')[1]])
calls the searchpos() function to search for the pattern used in the
parent :global command, and to get the column position of the match.
The pattern is referred to as #/ using the last search pattern
register (see :help "/). This takes advantage of the fact that the
:global command updates the / register as soon as it starts
executing. The c flag passed as the second argument in the
searchpos() call allows the match at the first character of a line
(:global positions the cursor at the very beginning of the line to
execute a command on), because it could be that there is no text to
the left of the separator. The searchpos() function returns a list,
the first element of which is the line number of the matched position,
and the second one is the column position. If the command is run on
a line, the line matches the pattern of the containing :global
command. As searchpos() is to look for the same pattern, there is
definitely a match on that line. Therefore, only the column starting
the match is in interest, so it gets extracted from the returning list
by the [1] subscript. This very position equals to the width of the
text in the first column of the line, plus one. Hence, the m variable
is set to the maximum of its current value and that column position.
The second command,
:%s//\=repeat(' ',m-col('.'))
pads the first occurrence of the separator on all of the lines that
contain it, with the number of spaces that is missing to make the text
before the separator to take m characters, minus one. This command
is a global substitution replacing an empty interval just before the
separator (see the comment about the :global command above) with the
result of evaluation of the expression (see :help sub-replace-\=)
repeat(' ',m-col('.'))
The repeat() function repeats its first argument (as string) the
number of times given in the second argument. Since on every
substitution the cursor is moved to the start of the pattern match,
m-col('.') equals exactly to the number of spaces needed to shift
the separator to the right to align columns (col('.') returns the
current column position of the cursor).
1 Below is a one-line version of this pair of commands.
:let m=0|exe'g/\ze -- /let m=max([m,searchpos(#/,"c")[1]])'|%s//\=repeat(' ',m-col('.'))
2 In previous revisions of the answer the commands used
to be as follows.
:let p=[0]|%s/^\ze\(.*\) -- /\=map(p,'max([v:val,len(submatch(1))+1])')[1:0]/
:exe'%s/\ze\%<'.p[0].'c -- /\=repeat(" ",'.p[0].'-col("."))'
Those who are interested in these particular commands can find their
detailed description in this answer’s edit history.
This is a modification on Benoit's answer that has two steps.
First step, block select text search and replace -- with lots of spaces.
'<,'>s/--/ --/
Now all the comments should have lots of spaces, but still be uneven.
Second step, block select the text again and use another regex to match all the characters you want to keep (say the first 20 characters or so) plus all the spaces following, and to replace it with a copy of those first 20 characters:
'<,'>s/\(.\{20}\)\s*/\1/
Not quite as easy as Benoit's, but I couldn't figure out how to make his second step work.

Resources