preventing trailing whitespace when using vim abbreviations - vim

I am a new user of vim (gvim in windows), and have found abbreviations a nice time saver - however they would be even better if i could stop the trailing whitespace at times.
I have some directories that i use a lot, and so i added some abbreviation/path pairs to my _vimrc:
:ab diR1 C:/dirA/dira/dir1/
:ab diR2 C:/dirA/dirb/dir2/
etc ...
Now when i type diR1 <space> i get C:/dirA/dira/dir1/[]| where the whitespace is represented by [] and the cursor is the | character. I would like to get rid of the [] == whitespace.
This is a minor complaint: however you seem to be able to customise everthing else in Vim so i figured i'd ask -- is it possible to avoid the trailing whitespace when one uses abbreviations in vim?
An alternate tool used within Vim is a good answer - my objective is to save re-typing frequently used directory structures, but to have the cursor handy as i would almost always add something to the end, such as myFile.txt.
The trailing white space (doubtless due to the fact that the space triggered the abbreviation) which i backspace over before adding myFile.txt to the end is less annoying than typing the whole thing over and over, but it would be ideal if i could avoid doing so ...

pb2q answer is exactly what you want in your current scenario, but does not fully answer the question presented in the title. This exact problem is addressed in the vim help file. See :helpgrep Eatchar. The example it gives is this:
You can even do more complicated things. For example, to consume the space
typed after an abbreviation: >
func Eatchar(pat)
let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c
endfunc
iabbr <silent> if if ()<Left><C-R>=Eatchar('\s')<CR>
You would put the Eatchar function in your ~/.vimrc file and then use like so in your abbreviations:
iabbr <silent> diR1 C:/dirA/dira/dir1/<c-r>=Eatchar('\m\s\<bar>/')<cr>
This would "eat" any trailing white space character or a slash. Note that I used iabbr instead of just abbr, because it is rare to actually want abbreviations to expand in command line mode. You must be careful with abbreviations in command line mode as they will expand in unexpected places such as searches and input() commands.
For more information see:
:h abbreviations
:helpgrep Eatchar
:h :helpgrep

This is possible, without more customization than just abbrev.
The abbreviation is being triggered by the space character, as you know. The space is a non-keyword character, and remains after the abbreviation is expanded.
But there are other ways to trigger the expansion, such as other non-keyword characters, including /. So if you instead define your abbreviations like this:
:ab diR1 C:/dirA/dira/dir1
That is, without the trailing path separator, then you can type diR1/, have the abbreviation expand for you because of the slash /, and continue typing, appending to your path with a file name.
Alternately, you can force abbreviation expansion using Ctrl-]. That is, type the abbreviation: diR1, with no following space or other non-keyword character, and then type Ctrl-]. The abbreviation will be expanded and you'll remain in insert mode, and can append your file name to the expanded path.
Check out :help abbreviations, there may be something else useful for you there, including more complicated constructions for always consuming e.g. the space character that triggered the abbreviation.

Instead of abbreviations, you could use mappings. They're expanded as soon as you have typed the last character of the mapping, so there won't be a trailing space:
:inoremap diR1 c:/dirA/dira/dir1
The downside for this approach is that the letters you type while a mapping could be expanded are not displayed until the mapping is finished. This takes some using used to.

Related

Using "register recall" to search and replace doesn't work when register contains newline character

I've been using the answer to Using visual selection or register for search and replace as follows:
v visually select
y yank
:%s/
Ctrl+r
"
This works fine in most cases. However, if newline characters are part of the visual selection I have to manually replace ^M with \n, first. What am I doing wrong?
What am I doing wrong?
Nothing. It's just Vim being well optimised for some workflows and not for others.
The linked thread actually contains some of the ingredients of the solution to the problem, namely that, after yanking, multiline text needs a bit of massaging if we want to use it for something else than p or P. The massaging is needed because newlines are stored as control characters in the buffer and that's what you get when you yank. But regular expressions don't really like control characters so literal ^#s must be changed into \ns and other characters must be escaped, too, like . or [, because they have a special meaning.
The solution to this problem is thus to write a function that:
escapes what needs to be escaped,
transforms newlines into \ns.
Here is one way to do it (with more intermediary steps than I would do in real life but that's better for demonstration):
function! EscapeRegister(reg)
let raw_text = getreg(a:reg)
let escaped_text = escape(raw_text, '\/.*$^~[]')
return substitute(escaped_text, "\n", '\\n', "g")
endfunction
which you would use like so:
v<motion>
y
:%s/
<C-r>=EscapeRegister(e)<CR>
/foo/g
<CR>
Feel free to call your function ER() or whatever for saving typing.
See :help getreg(), :help escape(), :help substiute(), :help #=.
The function above is fairly low-level and could be composed with other things to make higher-level tools that, for example, could handle everything in the macro above in a couple of keystrokes.

Vim split function arguments

I found that I often do refactor:
def function(param1, param2, (...), paramK):
to
def function(
param1,
param2,
(...),
paramK,
):
And tried to write mapping for it.
I started with
command! Split normal! qqqf(a<cr><esc>qqf s<cr><esc>#qq#qf)i<cr><esc>
qqq - reset content of macro q.
f(a<cr><esc> - find bracket, make newline and return to normal mode.
qq - start to record macro q.
f s<cr><esc> - change space to newline.
#q - run macro recursively.
q#q - end recording and run.
f)i<cr><esc> - add last newline before closing bracket.
My idea was, that macro will fail when it won't find space, but something is wrong with that. It raised some questions.
1) How can I check if some motion succeed? e.g. How to check if there is a space in current line?
2) Is there better idea to achieve what I want? Maybe some plugin or clear function?
3) What is wrong with my idea? When I run this combination from hand it works, but while calling :Split it doesn't.
Regarding why it doesn't work quite like it does when you type it manually:
When you type normal! <esc>, Vim parses this as "type the letters <, e, s, c, >". You might be able to insert a literal escape key there by typing <c-v><esc>, but that can look a bit weird in the configuration. Instead, a better way is to use the :exe command (:help :exe):
exe "normal! \<esc>"
The \<esc> gets interpolated by the string to be a literal escape key. So, the exe "normal! ..." gets translated to an invocation to normal! with the keys you're looking for. You also need to escape <cr> the same way. I'd also use \<space> instead of , I'm not entirely sure if a normal space is going to work here. After that, hopefully, you should get the same results as when you type it manually.
As for the actual problem you're trying to solve, I do have a plugin for that: splitjoin. By default, it splits your example like this:
def function(param1,
param2,
(...),
paramK):
pass
But there's a setting you can change to adjust it to your liking. Alternatively, from Jordan Running's link, it seems you could also use the argwrap plugin, which might be more reliable for argument-splitting in particular (splitjoin handles a wider variety of cases, but maybe doesn't do as good with arguments? Not sure.)
Answers:
I don't understand the question. You can search for spaces with f or with the / syntax. Why do you want to do this?
Yes. See below.
The vimrc syntax is super different from normal vim syntax. I don't know why and I don't fully understand it.
Code:
nnoremap <C-r> :s/, */,\r /g<cr>:s/)/\r)<cr>:nohl<cr>
remaps ctrl+r to search for a comma followed by 0 or more spaces and replace that with a comma, newline, and tab. then searches for ) and replaces that with newline and ). then undoes the highlighting it just did.
To enter a literal tab instead of 4 spaces, you'll have to type CtrlVtab in place of the 4 spaces you see in the command

How to set the regex to "w", "e" and "b" keys in Vim?

Is there a way to define what vim should recognize as a word? I want to assign \w+ regex to all word key motions such as "w", "e", "b"... I can do it for normal mode like this:
nnoremap <silent> w :call search('\w\+')<CR>
but it doesn't work for visual mode and operator-pending modes. It would be great if there is a setting to do this.
From what I understand from your question, and the discussion on Kaz' answer, I think what you're looking for is Kana's smartword plugin: http://www.vim.org/scripts/script.php?script_id=2470.
You're going to have to check the documentation with :help smartword, but the short version is, if you install the plugin and map the built-in word mappings to the plugin ones...
map w <Plug>(smartword-w)
map b <Plug>(smartword-b)
map e <Plug>(smartword-e)
map ge <Plug>(smartword-ge)
... then the built-ins will skip any non-word characters along the way. I've been using this for many, many years, and I'm quite happy with the way it behaves.
For the lower-case motions like w and e, the definition of a "word" is controlled by the :iskeyword option; see the :help iskeyword on that. Basically it consists of a list of characters given as individual characters or ranges, separated by commas. I think that various syntax files tweak this for different languages, so that these commands move by identifiers. It doesn't look like you can specify a regular expression for this.
The upper-case motions like W and E don't look like they can be reprogrammed. Their definition of a "WORD" is nonblank characters separated by whitespace, plus that each empty line counts as a "WORD".
However, unfortunately, it looks like there is a behavior in effect whereby the iskeyword characters simply separate the input into sequences of iskeyword characters, non-iskeyword characters and whitespace. When the w and related commands are used, they skip whitespace, but visit both the iskeyword tokens and the non-iskeyword tokens.
Remapping using :map to just a / or ? keystroke sequence works in both visual and command mode:
:map w /\<\w/^M
:map b ?\</\w^M
:map e /\w\>^M
It works in both because the / and ? searches work in both modes. Of course, it's an ugly solution because it clobbers the current search pattern and if you have :set hls (highlightsearch) on, the tips/tails of the words highlight.
The above searches are not very satisfactory because of the way the anchoring operators are behaving. For instance, I can't get them to "land" on the a in something like {abc or (abc.
The following mappings work a better. Each triggers several searches. The /. and ?. searches are used as a trick to go to the
next or previous character, such that if we are on the last character of a line, we go to the first one on the next line and vice versa.
:map b ?\w^M?\W^M/.^M
:map w /\W^M/\w^M
:map e /\w^M/\W^M?.^M
There are still some quirks. For instance, a list of words like:
abc
def
ghi
contains no match for the non-word class \W. The matching has to include line endings. Moving forward, an improvement to the w one in this regard is to add a match for the line ending like this:
:map w /\(\W\\|$\)^M/\w^M
Note the double backslash before the pipe! The rightmost backslash escapes the pipe so the processing of the :map command doesn't treat it as a command delimiter. Then we are left with \| which is the regex operator for branching. In a similar vein, the other two mappings can be improved; I'm leaving that as an exercise.

vi replaces with empty when searching

In vi (from cygwin), when I do searching:
:%s/something
It just replaces the something with empty string like
:%s/something// .
I've googled for a while but nothing really mentions this. Is there anything I should add to the .vimrc or .exrc to make this work?
Thanks!
In vi and vim, when you search for a pattern, you can search it again by simply typing /. It is understood that the previous pattern has to be used when no pattern is specified for searching.
(Though, you can press n for finding next occurence)
Same way, when you give a source (pattern) and leave the replacement in substitute command, it assumes that the replacement is empty and hence the given pattern is replaced with no characters (in other words, the pattern is removed)
In your case, you should understand that % stand for whole file(buffer) and s for substitute. To search, you can simply use /, followed by a pattern. To substitute , you will use :s. You need not confuse searching and substituting. Hence, no need for such settings in ~/.exrc. Also, remember that / is enough to search the whole buffer and % isnt necessary with /. / searches the entire buffer implicitly.
You may also want to look at :g/Pattern/. Learn more about it by searching :help global or :help :g in command line.
The format of a substitution in vim is as follows:
:[range]s[ubstitute]/{pattern}/{string}/[flags] [count]
In your case you have omitted the string from the substitution command and here what vim documentation stated about it:
If the {string} is omitted the substitute is done as if it's empty.
Thus the matched pattern is deleted. The separator after {pattern}
can also be left out then. Example: >
:%s/TESTING This deletes "TESTING" from all lines, but only one per line.
For compatibility with Vi these two exceptions are allowed:
"/{string}/" and "\?{string}?" do the same as "//{string}/r".
"\&{string}&" does the same as "//{string}/".
E146
Instead of the '/' which surrounds the pattern and replacement string, you can
use any other single-byte character, but not an alphanumeric
character, '\', '"' or '|'. This is useful if you want to include a
'/' in the search pattern or replacement string. Example: >
:s+/+//+
In other words :%s/something and :%s;something or :%s,something have all the same behavior because the / ; and , in the last examples are considered only as SIMPLE SEPARATOR

How to quickly change variable names in Vim?

I am using Vim to read through a lot of C and Perl code containing many single letter variable names.
It would be nice to have some command to change the name of a variable to something more meaningful while I’m in the process of reading the code, so that I could read the rest of it faster.
Is there some command in Vim which could let me do this quickly?
I don’t think regexes would work because:
the same single letter name might have different purposes in different scoping blocks; and
the same combination of letters could be part of another longer variable name, a string literal, or a comment.
Are there any known solutions?
The following is how to rename a variable which is defined in the current scope {}.
Move your cursor to the variable usage. Press gd. Which means - move cursor to the definition.
Now Press [{ - this will bring you to the scope begin.
Press V - will turn on Visual Line selection.
Press % - will jump to the opposite } thus will select the whole scope.
Press :s/ - start of the substitute command.
<C-R>/ - will insert pattern that match variable name (that name you were on before pressing gd).
/newname/gc<CR> - will initiate search and replace with confirmation on every match.
Now you have to record a macros or even better - map a key.
Here are the final mappings:
" For local replace
nnoremap gr gd[{V%::s/<C-R>///gc<left><left><left>
" For global replace
nnoremap gR gD:%s/<C-R>///gc<left><left><left>
Put this to your .vimrc or just execute.
After this pressing gr on the local variable will bring you to :s command where you simply should enter new_variable_name and press Enter.
I know it's an old question, and #mykola-golubyev's way obviously IS the best answer for the particular case in the OP question (which, I assume is going through obfuscated code where you're likely to have multiple blocks with same var names); but with the question name like that many people coming here from google searches probably look for less situation-specific ways to rename variables in VIM -- and those can be more concise
I'm surprised no one suggested this way:
* :s// NEWNAME /gc
The * is the same as gn - search the next occurrence of the word under the cursor AND make it the last searched pattern; you can then omit the search pattern in the substitute command and VIM will assume that last one is the pattern to search for.
For small amounts of var copies, here's an even quicker one:
* cw NEWNAME <esc> then repeat n. for other occurrences
* is search for occurrences, cw is change word, n goes to the next occurrence of the last searched term and . repeats the last command (which is now change word to NEWNAME)
(Credits for me knowing all this go to #doomedbunnies on Reddit)
Another cool trick: (credits to #nobe4)
* cgn NEWNAME <esc> then repeat . for other occurrences
cgn is "change whatever is the result of (find next occurrence)". Now that this is the last command, you don't need the n to go to the next occurrence, so fewer strokes again, and, more importantly, no need to alternate n and .. But, obviously, this one has the drawback of not having a way to skip an occurrence.
Here are some benefits of these over other similar approaches, or language-specific plugins with refactoring support:
no command mapping, no fiddling with .vimrc(or init.vim), so you can use it in any VIM copy you come across (e.g. a quick task on some VPS or your friend's machine where configuring VIM your way would defeat the purpose of 'quick')
using * or gn for word selection is very quick -- just one keystroke (well, let's say 1.5)
using * or gn makes sure you don't get any matches inside other words, just as :%s/<C-R>//gc does. Beats typing the :%s/\<OLDNAME\>/NEWNAME/gc by hand: I personally tend to forget to use the \< things to limit matches to whole words only.
Not using a scope will only result in a few extra strokes of n to skip unwanted matches -- probably even fewer than the extra strokes needed to limit the scope to a certain code block. Under normal circumstances, your variables are most likely somewhat localised to a certain code block anyway.
AFAIK, there is no actual refactoring support in VIM. When doing a rename with the intent of a refactor I usually take the following precautions:
Limit the scope of the change my using marks.
When entering the regex, bracket the name with \< and >. This will make it match an entire word which reduces the types of incorrect renames that will occur.
Don't do a multiline replace to reduce chances of a bad replace
Look through the code diff carefully if it's anything other than a small change.
My end change looks something like this
:'a,'bs/\<foo\>/bar
I would love to be wrong about there not being a refactoring tool for VIM but I haven't seen it.
Put this in your .vimrc
" Function to rename the variable under the cursor
function! Rnvar()
let word_to_replace = expand("<cword>")
let replacement = input("new name: ")
execute '%s/\(\W\)' . word_to_replace . '\(\W\)/\1' . replacement . '\2/gc'
endfunction
Call it with :call Rnvar()
expand("<cword>") gets the word under the cursor. The search string uses % for file-scope, and the \(\W\) patterns look for non-word characters at the boundary of the word to replace, and save them in variables \1 and \2 so as to be re-inserted in the replacement pattern.
You could use the 'c' modifier in the global search and replace that would ask you for confirmation for each replace. It would take longer but it might work for a non-humongous code file:
%s/\$var/\$foo/gc
The c stands for confirm.
In c, you may be able to make some progress using cscope. It makes an attempt at understanding syntax, so would have a chance of knowing when the letter was a variable.
If this is across multiple files, you may consider taking a look at sed. Use find to grab your files and xargs plus sed for a replace. Say you want to replace a with a_better_name in all files matching *.c, you could do
find . -name "*.c" | xargs sed -i -e 's/a/a_better_name/g'
Bear in mind that this will replace ALL occurrences of a, so you may want a more robust regex.

Resources