What's wrong with this Vim mapping? - regex

Inside a function I use to initialise some TeX related settings I have the following mapping defined:
vmap <buffer> ucm :s/^\% //<CR>:nohlsearch<CR>
I expected it to allow me to easily uncomment visually selected lines. The analogous:
vmap <buffer> cm :s/^/\% /<CR>:nohlsearch<CR>
does a pretty nice job in commenting. Also analogous mappings for other languages, which use a #, and not a % work just fine. Those last ones look like this:
vmap <buffer> cm :s/^/# /<CR>:nohlsearch<CR>
vmap <buffer> ucm :s/^# //<CR>:nohlsearch<CR>
A sequence of V10jcmV10kucm is supposed to leave the code intact.
So now: What am I doing wrong?

You are adding unecessary stuff.
:s/^/% <CR>
and
:s/^% /<CR>
should work for commenting and uncommenting respectively.
The third / is used to add options such as /c for "confirm" or /g for "global". If you don't use those options you don't need this / at all.
In your "uncomment" substitution you are escaping % but % in itself has no special meaning for Vim's regex flavour. Not only Vim is certainly not going to match it if it's escaped but \%<something> is used for a bunch of atoms like \%d. So your pattern fails because Vim stumbles upon your \% expecting the rest of the atom and getting "nothing".

Related

What is the best way to specify a wildcard or regex in a "test" statement in configure.ac?

I am writing a configure.ac script for gnu autotools. In my code I have some if test statements where I want to set flags based on the compiler name. My original test looked like this:
if test "x$NETCDF_FC" = xifort; then
but sometimes the compiler name is more complicated (e.g., mpifort, mpiifort, path prepended, etc...), and so I want to check if the string ifort is contained anywhere within the variable $NETCDF_FC.
As far as I can understand, to set up a comparison using a wildcard or regex, I cannot use test but instead need to use the double brackets [[ ]]. But when configure.ac is parsed by autoconf to create configure, square brackets are treated like quotes and so one level of them is stripped from the output. The only solution I could get to work is to use triple brackets in my configure.ac, like this:
if [[[ $NETCDF_FC =~ ifort ]]]; then
Am I doing this correctly? Would this be considered best practices for configure.ac or is there another way?
Use a case statement. Either directly as shell code:
case "$NETCDF_FC" in
*ifort*)
do_whatever
;;
*)
do_something_else
;;
esac
or as m4sh code:
AS_CASE([$NETCDF_FC],
[*ifort*], [do_whatever],
[do_something_else])
I would not want to rely on a shell capable of interpreting [[ ]] or [[[ ]]] being present at configure runtime (you need to escape those a bit with [] to have the double or triple brackets make it into configure).
If you need a character class within a case pattern (such as e.g. *[a-z]ifort*), I would advise you to check the generated configure file for the case statement patterns which actually end up being used until you have enough [] quotes added around the pattern in the source configure.ac file.
Note that the explicit case statements often contain # ( shell comments at the end of the lines directly before the ) patterns to avoid editors becoming confused about non-matching opening/closing parentheses.

Vim automatic hard wrapping long comments in C++?

To reproduce my issue:
$ vim -u NONE test.cpp
:set nocompatible
:set tw=20
:set fo=croql
Now type in the following text:
/*
test test test test test test test test test
*/
Notice that there is no leading asterisk on the line containing the tests. Vim will insert this by default, remove it.
Vim should be auto-wrapping this, but it doesn't.
How can I make Vim wrap automatically in comments, and only in comments? :set fo+=t works, but then everything gets wrapped and I do not want automatic hard wrapping for code.
With my OnSyntaxChange plugin, you can change the 'fo' option value depending on whether the cursor inside a comment or not:
call OnSyntaxChange#Install('Comment', '^Comment$', 0, 'a')
autocmd User SyntaxCommentEnterA setlocal fo+=t
autocmd User SyntaxCommentLeaveA setlocal fo-=t
It's also available on GitHub.
Sorry, but Vim by itself cannot be configured to automatically wrap text in multi-line comments, without some character at the beginning of the line. The 'comments' option controls how Vim recognizes comments for automatic wrapping. Multi-line comments must have a "s", "e", and "m" part in the 'comments' option to be recognized; :help format-comments says: "Three-piece comments must have a middle string because otherwise Vim can't recognize the middle lines."
Nevertheless, you can still manually reformat such lines with the gq operator (shorthand gqq for a single line) and Vim should mostly do what you want. If it is slightly off, you can mess with the 'formatexpr' option.
You're probably better off just using the leading * in multi-line comments, or using //-style comments.
There are plugins available that use syntax-defined regions in clever ways which may allow you to work around these limitations. See Ingo's answer.
I'm using VIM 8.2 and it CAN wrap comments out of the box.
Short answer how
set tw=80
set fo=croaq
Check your comments option with :set comments?. I must contains /*, * and */ sequences.
Long answer
You can read details in :help formatoptions, :help fo-table and :help comments.
The tw=80 option is for limiting line width.
The formatoptions uses next flags here:
The c flag is to apply wrapping only for comments
The r and o flag is for auto-inserting the comment-leader on new lines (* symbol for /* styled comment in C-like languages).
The a will auto-format your paragraphs while you are typing.
The q option will handle the comment leader the correct way.
For some types of buffers you can apply formatoptions for all text like next:
"Limit line width for git commit messages to 72 characters
autocmd FileType gitcommit setlocal tw=72
"Auto format all text for git commit messages and markdown files
autocmd FileType gitcommit,markdown setlocal formatoptions+=t
Here
The t flag is to apply wrapping for text (additionally to c)
The comments option will help vim to detect comments correctly.

Find/Replace string that doesn't contain quotes

I have inherited a rather large/ugly php codebase (language is unimportant, this is a generic vim question) , where nothing is quoted properly (old php doesn't mind, but new php versions throw warnings).
I'd like to turn $something[somekey] into $something['somekey'], only if its not already quoted or contain the character $
I was trying to build a regular expression to quote the keys, but just cant seem to be able get it to cooperate.
This is what i have so far, which doesn't work but maybe will help explain my question better. And to show that i have actually tried.
:%s/\v\$(.{-})\[(['"$]#<!.{-})\]/$\1['\2']/
My goal is to have something like this:
$something[somekey] = $something['somekey']
$somethingelse[someotherthing] = $something['someotherthing']
$another['key'] = $another['key'] (is ignored)
$yetanother["keykey"] = $yetanother["keykey"] (is ignored)
$derp[$herp] = $derp[$herp] (is ignored)
$array[3] = $array[3] (is ignored)
These can appear anywhere in text, even multiple on the same line, and even touching each other like $something[key]$something[key2], which i would like to be replaced with $something['key']$something['key2']
Another problem, there seems to be random javascript arrays in some files.. which have [] square brackets. So the regex needs to check to see if it starts with $ and text before the brackets.
Im probably asking for the impossible, but any help on this would be great before i go insane editing each file one by one manually.
EDIT: forgot that keys can be numeric, and shouldn't be quoted.
I tried the following, which processed everything from your question correctly:
:%s/\[\(\I\i*\)\]/['\1']/g
Or, with optional white spaces inside the parens:
:%s/\[\s*\(\I\i*\)\s*\]/['\1']/g
And also checking for $identifier before the parens:
:%s/\(\$\i\+\)\[\s*\(\I\i*\)\s*\]/\1['\2']/g

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

To comment out matches in Vim - independent on comment leader?

I want to comment out lines in some code I have. I have different kinds of codes, and they use different comment leaders. E.g. in latex: '%', in Fortran 90: '!' and in python: '#'. I want to do a substitute command that looks something like this:
:g/<search-string>/s/^/<add-comment-leader-here>/
If this is possible, I could also make a command in Vim that automatically commented out the selected text. Something like this:
vmap <z> :'<,'>s/^/<add-comment-leader-here>/
Any ideas are welcome! :)
If you haven't seen it already, you may be interested in the NERD Commenter Vim plugin.
Check out Enhanced Commentify: I think this does what you want: it determines the comment leader based on the file type.
If you want to do it yourself, the easiest way would be to define a mapping that uses exec to build a command and include a variable that is set in your ~/.vim/after/ftplugin/c.vim and other ftplugin files. Alternatively, just add the same mapping (with a different leader) to each ftplugin file.