Multiple search and replace in one line - vim

If I do something like:
:%s/aaa/bbb/ | %s/111/222/
and the first search and replace doesn't find any matches, the second search and replace won't be executed. Is there any way to tell vim to carry on even when a command "failed"?

Try
:%s/aaa/bbb/e | %s/111/222/e
and read
:help :s_flags
especially the entry under [e]:
When the search pattern fails, do not issue an error message and, in
particular, continue in maps as if no error occurred. This is most
useful to prevent the "No match" error from breaking a mapping. Vim
does not suppress the following error messages, however:
Regular expressions can't be delimited by letters
\ should be followed by /, ? or &
No previous substitute regular expression
Trailing characters
Interrupted

Related

Multiple possible find-replace in Gvim

I want to create a command or function to combine multiple finds and replaces. I have tried following command:
command MyFR %s/first/1st/g | %s/second/2nd/g | %s/third/3rd/g
It works but stops midway if no 'first' or 'second' is found. The error is:
E486: Pattern not found: <pattern>
How can I make this command work for 'second' and 'third' to be replaced, even if there is no 'first' in text? Thanks for your help.
You could add the e flag to each of your substitution command, which is described in :h :s_flags:
[e] When the search pattern fails, do not issue an error message and, in
particular, continue in maps as if no error occurred. This is most
useful to prevent the "No match" error from breaking a mapping. Vim
does not suppress the following error messages, however:
Regular expressions can't be delimited by letters
\ should be followed by /, ? or &
No previous substitute regular expression
Trailing characters
Interrupted
It would give:
com! MyFR %s/first/1st/ge | %s/second/2nd/ge | %s/third/3rd/ge
Another solution would be to merge all substitutions into a single one:
com! MyFR %s/\vfirst|second|third/\={'first': '1st', 'second': '2nd', 'third': '3rd'}[tolower(submatch(0))]/g
This time, in the replacement part, instead of using a literal string, you would use an expression (see :h s/\=). Here, the expression is a given value of a dictionary.
The keys of the dictionary are all your possible matched texts, and the values are their replacements.
The value you retrieve from the dictionary is tolower(submatch(0)) which evaluates into the matched text (see :h submatch()), normalized in its lowercase version (all uppercase characters are turned into their lowercase counterpart through tolower()).

What's the meaning of some advanced patterns in vim errorformat? (%s, %+, %\\#=)

I tried reading :help errorformat and googling (mostly stackoverflow), but can't understand some of the patterns mentioned there:
%s - "specifies the text to search for to locate the error line. [...]"
um, first of all, trying to understand the sentence at all, where do I put the "text to search", after the %s? before it? or, I don't know, does it maybe taint the whole pattern? WTF?
secondly, what does this pattern actually do, how does it differ from regular text in a pattern, like some kinda set efm+=,foobar? the "foobar" here is for me also "text to search for"... :/
%+ - e.g. I I've seen something like that used in one question: %+C%.%#
does it mean the whole line will be appended to a %m used in an earlier/later multiline pattern? if yes, then what if there was not %.%# (== regexp .*), but, let's say, %+Ccont.: %.%# - would something like that work to capture only stuff after a cont.: string into the %m?
also, what's the difference between %C%.%# and %+C%.%# and %+G?
also, what's the difference between %A and %+A, or %E vs. %+E?
finally, an example for Python in :help errorformat-multi-line ends with the following characters: %\\#=%m -- WTF does the %\\#= mean?
I'd be very grateful for some help understanding this stuff.
Ah, errorformat, the feature everybody loves to hate. :)
Some meta first.
Some Vim commands (such as :make and :cgetexpr) take the output of a compiler and parse it into a quickfix list. errorformat is a string that describes how this parsing is done. It's a list of patterns, each pattern being a sort of hybrid between a regexp and a scanf(3) format. Some of these patterns match single lines in the compiler's output, others try to match multiple lines (%E, %A, %C etc.), others keep various states (%D, %X), others change the way parsing proceeds (%>), while yet others simply produce messages in the qflist (%G), or ignore lines in the input (%-G). Not all combinations make sense, and it's quite likely you won't figure out all details until you look at Vim' sources. shrug
You probably want to write errorformats using let &erf='...' rather than set erf=.... The syntax is much more human-friendly.
You can experiment with errorformat using cgetexpr. cgetexpr expects a list, which it interprets as the lines in the compiler's output. The result is a qflist (or a syntax error).
qflists are lists of errors, each error being a Vim "dictionary". See :help getqflist() for the (simplified) format.
Errors can identify a place in a file, they can be simple messages (if essential data that identifies a place is missing), and they can be valid or invalid (the invalid ones are essentially the leftovers from parsing).
You can display the current qflist with something like :echomsg string(getqflist()), or you can see it in a nice window with :copen (some important details are not shown in the window though). :cc will take you to the place of the first error (assuming the first error in qflist actually refers to an error in a file).
Now to answer your questions.
um, first of all, trying to understand the sentence at all, where do I put the "text to search", after the %s? before it?
You don't. %s reads a line from the compiler's output and translates it to pattern in the qflist. That's all it does. To see it at work, create a file efm.vim with this content:
let &errorformat ='%f:%s:%m'
cgetexpr ['efm.vim:" bar:baz']
echomsg string(getqflist())
copen
cc
" bar baz
" bar
" foo bar
Then run :so%, and try to understand what's going on. %f:%s:%m looks for three fields: a filename, the %s thing, and the message. The input line is efm.vim:" bar:baz, which is parsed into filename efm.vim (that is, current file), pattern ^\V" bar\$, and message baz. When you run :cc Vim tries to find a line matching ^\V" bar\$, and sends you there. That's the next-to-last line in the current file.
secondly, what does this pattern actually do, how does it differ from regular text in a pattern, like some kinda set efm+=,foobar?
set efm+=foobar %m will look for a line in the compiler's output starting with foobar, then assign the rest of the line to the message field in the corresponding error.
%s reads a line from the compiler's output and translates it to a pattern field in the corresponding error.
%+ - e.g. I I've seen something like that used in one question: %+C%.%#
does it mean the whole line will be appended to a %m used in an earlier/later multiline pattern?
Yes, it appends the content of the line matched by %+C to the message produced by an earlier (not later) multiline pattern (%A, %E, %W, or %I).
if yes, then what if there was not %.%# (== regexp .*), but, let's say, %+Ccont.: %.%# - would something like that work to capture only stuff after a cont.: string into the %m?
No. With %+Ccont.: %.%# only the lines matching the regexp ^cont\.: .*$ are considered, the lines not matching it are ignored. Then the entire line is appended to the previous %m, not just the part that follows cont.:.
also, what's the difference between %C%.%# and %+C%.%# and %+G?
%Chead %m trail matches ^head .* trail$, then appends only the middle part to the previous %m (it discards head and trail).
%+Chead %m trail matches ^head .* trail$, then appends the entire line to the previous %m (including head and trail).
%+Gfoo matches a line starting with foo and simply adds the entire line as a message in the qflist (that is, an error that only has a message field).
also, what's the difference between %A and %+A, or %E vs. %+E?
%A and %E start multiline patterns. %+ seems to mean "add the entire line being parsed to message, regardless of the position of %m".
finally, an example for Python in :help errorformat-multi-line ends with the following characters: %\\#=%m -- WTF does the %\\#= mean?
%\\#= translates to the regexp qualifier \#=, "matches preceding atom with zero width".

Vim errorformat: include part of expression in message string

With vim's errorformat syntax, is there any way to use part of the message in filtering results?
As an example, some linker errors don't have anything explicit to distinguish them as an error on the line, other than the error itself:
/path/to/foo.cpp:42: undefined reference to 'UnimplementedFunction'
or
/path/to/foo.cpp:43: multiple definition of 'MultiplyDefinedFunction'
Using an errorformat of:
set efm=%f:%l:\ %m
would catch and display both of these correctly, but will falsely match many other cases (any line that starts with "[string]:[number]: ").
Or, explicitly specifying them both:
set efm=
set efm+=%f:%l:\ undefined\ reference\ to\ %m
set efm+=%f:%l:\ multiple\ definition\ of\ %m
removes the false positives, but the 'message' becomes far less useful -- the actual error is no longer included (just whatever is after it).
Is there anything in the syntax I'm missing to deal with this situation?
Ideally I'd like to be able to say something along the lines of:
set efm+=%f:%l:\ %{StartMessage}undefined\ reference\ to\ %*\\S%{EndMessage}
set efm+=%f:%l:\ %{StartMessage}multiple\ definition\ of\ %*\\S%{EndMessage}
... where everything matched between StartMessage and EndMessage is used as the error's message.
The errorformat can also use vim's regular expression syntax (albeit in a rather awkward way) which gives us a solution to the problem. We can use a non-capturing group and a zero-width assertion to require the presence of these signaling phrases without consuming them. This then allows the %m to pick them up. As plain regular expression syntax this zero-width assertion looks like:
\%(undefined reference\|multiple definition\)\#=
But in order to use it in efm we need to replace \ by %\ and % by %% and for use in a :set line we need to escape the backslashes, spaces and vertical bar so we finally have:
:set efm=%f:%l:\ %\\%%(undefined\ reference%\\\|multiple\ definitions%\\)%\\#=%m
With that the error file
/path/to/foo.cpp:42: undefined reference to 'UnimplementedFunction'
/path/to/foo.cpp:43: multiple definition of 'MultiplyDefinedFunction'
notafile:123: just some other text
comes out as the following in :copen:
/path/to/foo.cpp|42| undefined reference to 'UnimplementedFunction'
/path/to/foo.cpp|43| multiple definition of 'MultiplyDefinedFunction'
|| notafile:123: just some other text
I've been using sed to rewrite the output in cases like this where I want to get some arbitrary output that's not nessicarily homogenous into the quickfix window.
You could write make.sh that fires off make (or whatever your're using to build) and trims off stuff you're not concerned with:
make | sed '/undefined reference\|multiple definition/!d'
(Deletes lines not containing 'undefined reference' or 'multiple definition')
If that's going to get too unweildly because of the number of error strings you care about, you could do the inverse and just kill stuff you don't care about:
make | sed 's/some garbage\|other useless message//'
then :set makeprg=make.sh in vim

About a key binding to remove trailing spaces and save

I have ths line in my .vimrc:
map gu :%s/\s\+$//<enter> :w<enter>
to remove trailing spaces and saving the file in the same time.
When there are trailing spaces in the file, it works OK (it removes the spaces and saves the file), but when there are not trailing spaces it doesn't save the file, it just says Pattern not found: \s+$.
NOTE: I found this, but I'm curious about the way I'm asking.
Add an e to the search flags to suppress error messages:
map gu :%s/\s\+$//e<enter> :w<enter>
From :help substitute:
[e] When the search pattern fails, do not issue an error message and, in
particular, continue in maps as if no error occurred. This is most
useful to prevent the "No match" error from breaking a mapping. Vim
does not suppress the following error messages, however:
Regular expressions can't be delimited by letters
\ should be followed by /, ? or &
No previous substitute regular expression
Trailing characters
Interrupted

Find first non-matching line in VIM

It happens sometimes that I have to look into various log and trace files on Windows and generally I use for the purpose VIM.
My problem though is that I still can't find any analog of grep -v inside of VIM: find in the buffer a line not matching given regular expression. E.g. log file is filled with lines which somewhere in a middle contain phrase all is ok and I need to find first line which doesn't contain all is ok.
I can write a custom function for that, yet at the moment that seems to be an overkill and likely to be slower than a native solution.
Is there any easy way to do it in VIM?
I believe if you simply want to have your cursor end up at the first non-matching line you can use visual as the command in your global command. So:
:v/pattern/visual
will leave your cursor at the first non-matching line. Or:
:g/pattern/visual
will leave your cursor at the first matching line.
you can use negative look-behind operator #<!
e.g. to find all lines not containing "a", use /\v^.+(^.*a.*$)#<!$
(\v just causes some operators like ( and #<! not to must have been backslash escaped)
the simpler method is to delete all lines matching or not matching the pattern (:g/PATTERN/d or :g!/PATTERN/d respectively)
I'm often in your case, so to "clean" the logs files I use :
:g/all is ok/d
Your grep -v can be achieved with
:v/error/d
Which will remove all lines which does not contain error.
It's probably already too late, but I think that this should be said somewhere.
Vim (since version about 7.4) comes with a plugin called LogiPat, which makes searching for lines which don't contain some string really easy. So using this plugin finding the lines not containing all is ok is done like this:
:LogiPat !"all is ok"
And then you can jump between the matching (or in this case not matching) lines with n and N.
You can also use logical operations like & and | to join different strings in one pattern:
:LP !("foo"|"bar")&"baz"
LP is shorthand for LogiPat, and this command will search for lines that contain the word baz and don't contain neither foo nor bar.
I just managed a somewhat klutzy procedure using the "g" command:
:%g!/search/p
This says to print out the non-matching lines... not sure if that worked, but it did end up with the cursor positioned on the first non-matching line.
(substitute some other string for "search", of course)
You can search with following line and press n to jump to the first non-matching line
^\(.*all is ok\)\#!.*$
Breakdown of operators:
^ -> means start of the line
\( and \) -> To match a whole string multiple times, it must be grouped into one item. This is done by putting "\(" before it and "\)" after it.
\#! -> Matches with zero width if the preceding atom does NOT match at the current position.
.* -> Matches any character repeated 1 or more times
$ -> end of the line
Here is sample animation how it works. For simplicity I searched for word apple.
You can iterate through the non-matches using g and a null substitution:
:g!/pattern/s/^//c
If you reply "n" each time you wont even mark the file as changed.
You need ctrl-C to escape from the circle (or keep going to bottom of file).

Resources