Vim syntax highlighting for a match on several lines - regex

I am implementing on vim a syntax file to highlight a hierarchy like this
| text at level 1
| | text at level 2
| | text at level 2
| | | text at level 3
| text at level 1
For example I use
syn match myMatch +^\(| \)\++
to highlight the level indicators. However, I would also like to highlight wrong patterns like these
| text at level 1
| | | text at level 3
which increment the levels by more than one. I have written the following syntax match
syn match myWrongMatch +^\(\(| \)*\)\(\n\|[^|].*\n\)\1\(| \)\{2,}+
It may not be optimal but it does the job. The problem is that the matches are checked by vim on the line which is being edited so that if I fix the error by removing a level on the second line it will stop highlighting the second line but still highlight the first one until I also edit it (like remove and rewrite a character).
This problem is that I can only match a line using the next line information but not the opposite. Since this doesn't seem possible with a regex match, I would like to know if it is possible to ask vim to check for matches in both the currently edited line and the previous one (or a broader context) ? Another solution may be to implement this by a region which checks the context but I have been unsuccessful with that so far.
EDIT: The answer is actually in the vim help at :syn-sync-linebreaks (thanks Herbert Sitz for pointing me to the right section).
When using a pattern that matches multiple lines, a change in one line may
cause a pattern to no longer match in a previous line. This means has to
start above where the change was made. How many lines can be specified with
the "linebreaks" argument. For example, when a pattern may include one line
break use this:
:syntax sync linebreaks=1
The result is that redrawing always starts at least one line before where a
change was made. The default value for "linebreaks" is zero. Usually the
value for "minlines" is bigger than "linebreaks".
This works perfectly.

You can try automating a syntax sync operation. For example, try putting this in the InsertEnter autocmd, which will sync syntax whenever you exit insert mode:
au InsertLeave * syntax synch minlines=50
Not a perfect solution. Maybe adding more autocmds would help. It depends partially on what your documents are going to look like, how big they're going to be, how you're editing.
For help read more about syntax syncing: :h syn-sync
This isn't typical use of syncing, since main purpose, as I understand it, is to automatically search around edited lines when they're in syntax regions. You're not using regions, so you need to initiate sync using the autocmd. Maybe you could define a region just for purpose of making sure syntax sync reevaluates syntax over group of lines--without needing the autocmd--even if the region is not going to be used for highlighting.

Related

How can I define RegEx to remove a particular part in a specified line of code?

I am attempting to remove .nc1 at the end of a line. I receive .nc1 in batches as a steel fabricator. We run into issues with our files where, line 5 in the example below, has an unnecessary .nc1 extension at the end. Problem I have, is that I cannot simply replace the value as it appears in line 2 as well.
In the example photo I have attached, I am looking to remove line 5 .nc1 extension and keep line 2 as is, .nc1 extension removal will be applied in a batch editing to all of my .nc1 files via find/replace.
ST
** BB233.nc1
F88
BB233
BB233.nc1
1000
A992
1
W21X201
Change to this
ST
** BB233.nc1
F88
BB233
BB233
1000
A992
1
W21X201
I was looking into Positive and/or Negative lookahead/lookbehind but didnt have much luck in making it work. I am a novice/lack thereof when it comes to using RegEx.
Match .nc1 only at the end of lines starting with whitespace, capturing the part you want to keep and putting it back, effectively deleting .nc1
Search: ^(\s+.*)\.nc1$
Replace: $1

mIRC Search for multiple words in text file

I am trying to search a text file that will return a result if more than one word is found in that line. I don't see this explained in the documentation and I have tried various loops with no success.
What I would like to do is something similar to this:
$read(name.txt, s, word1|word2|word3)
or even something like this:
$read(name.txt, w, word1*|*word2*|*word3)
I don't know RegEx that well so I'm assuming this can be done with that but I don't know how to do that.
The documentation in the client self is good but I also recommend this site: http://en.wikichip.org/wiki/mirc. And with your problem there is a nice article : http://en.wikichip.org/wiki/mirc/text_files
All the info is taken from there. So credits to wikichip.
alias testForString {
while ($read(file.txt, nw, *test*, $calc($readn + 1))) {
var %line = $v1
; you can add your own words in the regex, seperate them with a pipe (|)
noop $regex(%line,/(word1|word2|word3|test)/))
echo -a Amount of results: $regml(0)
}
}
$readn is an identifier that returns the line that $read() matched. It is used to start searching for the pattern on the next line. Which is in this case test.
In the code above, $readn starts at 0. We use $calc() to start at line 1. Every match $read() will start searching on the next line. When no more matches are after the line specified $read will return $null - terminating the loop.
The w switch is used to use a wildcard in your search
The n switch prevents evaluating the text it reads as if it was mSL code. In almost EVERY case you must use the n switch. Except if you really need it. Improper use of the $read() identifier without the 'n' switch could leave your script highly vulnerable.
The result is stored in a variable named %line to use it later in case you need it.
After that we use a noop to execute a regex to match your needs. In this case you can use $regml(0) to find the amount of matches which are specified in your regex search. Using an if-statement you can see if there are two or more matches.
Hope you find this helpful, if there's anything unclear, I will try to explain it better.
EDIT
#cp022
I can't comment, so I'll post my comment here, so how does that help in any way to read content from a text file?

Notepad++ - Selecting or Highlighting multiple sections of repeated text IN 1 LINE

I have a text file in Notepad++ that contains about 66,000 words all in 1 line, and it is a set of 200 "lines" of output that are all unique and placed in 1 line in the basic JSON form {output:[{output1},{output2},...}]}.
There is a set of characters matching the RegEx expression "id":.........,"kind":"track" that occurs about 285 times in total, and I am trying to either single them out, or copy all of them at once.
Basically, without some super complicated RegEx terms, I am stuck because I can't figure out how to highlight all of them at once, and also the Remove Unbookmarked Lines feature does not apply because this is all in one line. I have only managed to be able to Mark every single occurrence.
So does this require a large number of steps to get the file into multiple lines and work from there, or is there something else I am missing?
Edit: I have come up with a set of Macro schemes that make the process of doing this manually work much faster. It's another alternative but still takes a few steps and quite some time.
Edit 2: I intended there to be an answer for actually just highlighting the different sections all at once, but I guess that it not possible. The answer here turns out to be more useful in my case, allowing me to have a list of IDs without everything else.
You seem to already have a regex which matches single instances of your pattern, so assuming it works and that we must use Notepad++ for this:
Replace .*?("id":.........,"kind":"track").*?(?="id".........,"kind":"track"|$) with \1.
If this textfile is valid JSON, this opens you up to other, non-notepad++ options, like using Python with the json module.
Edited to remove unnecessary steps

Vim: How to apply external command only to lines matching pattern

Two of my favorite Vim features are the ability to apply standard operators to lines matching a regex, and the ability to filter a selection or range of lines through an external command. But can these two ideas be combined?
For example, I have a text file that I use as a lab notebook, with notes from different dates separated by a line of dashes. I can do something like delete all the dash-lines with something like :% g/^-/d. But let's say I wanted to resize all the actual text lines, without touching those dash lines.
For a single paragraph, this would be something like {!}fmt. But how can this be applied to all the non-dash paragraphs? When I try what seems the logical thing, and just chain these two together with :% v/^-/!fmt, that doesn't work. (In fact, it seems to crash Vim...)
Is there a way to connect these two ideas, and only pass lines (not) matching a pattern into an external command like fmt?
Consider how the :global command works.
:global (and :v) make two passes through the buffer,
first marking each line that matches,
then executing the given command on the marked lines.
Thus if you can come up with a command – be it an Ex command or a command-line tool – and an associated range that can be applied to each matching line (and range), you have a winner.
For example, assuming that your text is soft-wrapped and your paragraphs are simply lines that don't begin with minus, here's how to reformat the paragraphs:
:v/^-/.!fmt -72
Here we used the range . "current line" and thus filtered every matching line through fmt. More complicated ranges work, too. For instance, if your text were hard-wrapped and paragraphs were defined as "from a line beginning with minus, up until the next blank line" you could instead use this:
:g/^-/.,'}!fmt -72
Help topics:
:h multi-repeat
:h :range!
:h :range
One way to do it may be applying the command to the lines matching the pattern 'not containing only dashes'
The solution I would try the is something like (not tested):
:g/\v^(-+)#!/normal V!fmt
EDIT I was doing some experiments and I think a recurvie macro should work for you
first of all set nowrapscan:
set nowrapscan
To prevent the recursive macro executing more than you want.
Then you make a search:
/\v^(-+)#!
Test if pressing n and p works with your pattern and tune it up if needed
After that, start recording the macro
qqn:.!awk '{print $2}'^M$
In this case I use awk as an example .! means filter current line with an external program
Then to make the macro recursive just append the string '#q' to the register #q
let #q .= '#q'
And move to the beggining of the buffer to apply the recursive macro and make the modifications:
gg#q
Then you are done. Hope this helps

Vim different textwidth for multiline C comments?

In our C++ code base we keep 99 column lines but 79-some-odd column multiline comments. Is there a good strategy to do this automagically? I assume the modes are already known because of smart comment line-joining and leading * insertion.
Apparently both code and comments use the same textwidth option. As far as I can see, the only trick is to set this option dynamically:
:autocmd CursorMoved,CursorMovedI * :if match(getline(.), '^\s*\*') == 0 | :setlocal textwidth=79 | :else | :setlocal textwidth=99 | :endif
Here the critical part is detecting when we are in a comment. If you only format comments this way:
/*
* my comment
*/
my regex should work... unless you have lines in the code starting with * (which I guess can happen in C, less frequently in C++). If you use comments like this:
// comment line 1
// comment line 2
the regex is even simpler to write. If you want to cover all possible situations, including corner cases, well... I guess the best thing would be to define a separate detection function and call that from the :autocmd instead of match().
I came across this same problem and think that I have found a suitable solution.
What I wanted my comments to word wrap so that when I'm typing I don't have to worry about formating text. This works well with comment text. But I wasn't comfortable with having vim format my code. So I wanted vim to highlight every thing in red after x column.
To do this with only cpp code you would add the following to your ~/.vim/ftdetect/cpp.vim file.
set textwidth=79
match ErrorMsg '\%>99v.\+'
note: You may have to create the file and folders if they don't exist.
If you have problems with this make sure that you have formatoptions set to:
formatoptions=croql
You can see this by running :set formatoptions inside of vim.