Cursor position in VIM abbreviation - vim

is there some way to tell VIM place the cursor at some position after abbreviation expansion?
Say you have something like this in .gvimrc
iabberv <? <?=|?>
and you want to place cursor where pipe character is automatically.

A quick solution that I'd use in this case is to insert some key presses to the abbreviation:
iabbrev <? <?=?><Left><Left>
Would place the cursor two places left, after the =.
In this manner, you can use various movement keys such as <End>, <Home>, or even return to normal mode with <Esc> and command usual normal-mode commands. The example would then be
iabbrev <? <?=?><Esc>hha
and it does the same as the first example. If you expand an abbreviation with space, it will have one extra space. which you can get rid of by using a <BS> (backspace) in the abbreviation. Or expand with <C-]> which will leave no space.
Correction: since the abbreviation is first expanded, and after that the space inserted, you need a small function found in the help (map.txt):
func Eatchar(pat)
let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c
endfunc
This is best put in .vimrc. Now the following abbreviation will work fully as intented:
:iabbrev <silent> <? <?=?><Left><Left><C-R>=Eatchar('\s')<CR>
It is a bit messy, but has an external function call that removes the white space and it should work well.

What you want are snippets. I use snipmate for that.

It can be done with lh-map-tools:
"
" in {rtp}/ftpluvin/vim/vim_snippets.vim
inoreab <buffer> <silent> if
\ <C-R>=InsertSeq('if', 'if!cursorhere!\nendif!mark!')<CR>
Other plugins offer a similar feature.

Related

Dot-repeatable mapping to insert a space from normal mode and move the cursor along

I have a mapping nnoremap <leader>l i<space><esc> that inserts a space while staying in a normal mode, and the mapping is dot-repeatable. However, the cursor stays where it was and the space "expands" to the right of the cursor.
I want to have a mapping that does the same but moves the cursor along, e.g.
nnoremap <leader>h i<space><esc>l
I love breaking sequences of characters with a space from normal mode and I want to decide at will if the cursor should stay or move (using either h to move the cursor along or l to move the text to the left while leaving the cursor where it is).
None of the tricks with i or a or <C-o> seems to work and this is an expected behavior.
Is there any clever hack to accomplish dot-repeatability using nnoremap only? It should also work if I am at the end of the line.
I can probably re-phrase it like this: I want to have functionality similar to X and x that deletes a single character before or at the cursor, but instead of deleting a character I want to insert a <space> before or at the cursor (not after the cursor as with a). And it should be dot-repeatable and take counts. Preferably a mapping that does not use more than a single line in the .vimrc and does not require any plugins. And I don't want to change the behaviour of <esc> (or i, whichever is responsible for moving the cursor when one leaves the insert mode) that moves a cursor one character to the left.
Edit 1:
Dot-repeatable nnoremap <leader>l i<space><esc> does this:
<leader>l...
AAAAAAAAAA[B]BBBBBBBB
AAAAAAAAAA[ ]BBBBBBBBB
AAAAAAAAAA[ ] BBBBBBBBB
AAAAAAAAAA[ ] BBBBBBBBBB
And I want a dot-repeatable <leader>h that would do this:
<leader>h...
AAAAAAAAAA[B]BBBBBBBB
AAAAAAAAAA [B]BBBBBBBB
AAAAAAAAAA [B]BBBBBBBB
AAAAAAAAAA [B]BBBBBBBBB
Edit 2:
The workaround mentioned in reply by #romainl does the trick:
function! s:insspace(...)
if a:0
" perform operation
execute 'normal' v:count1.'i '."\<esc>".'l'
else
" set up
let &operatorfunc = matchstr(expand('<sfile>'), '[^. ]*$')
return "g#\<space>"
endif
endfunction
nnoremap <silent><expr> <leader>h <sid>insspace()
Can someone explain how it works? I am a beginner in vim...
Given the string below and assuming the cursor is on the -:
lorem-ipsum
^
There are various easy and repeatable ways to insert a space after the - and leave the cursor on the space:
a <Esc>
s<C-r>" <Esc>
lorem- ipsum
^
The two macros above…
are "dot-repeatable", because there is no motion involved,
leave the cursor on the space because, after an insertion, Vim places the cursor on the last inserted character.
But this is precisely the latter behaviour that prevents us from doing the same in the other direction without a motion either before or after the insertion.
Without motion, the operation is "dot-repeatable" but the cursor is left on the -:
i <Esc>
a <Esc>
s <C-r>"<Esc>
lorem -ipsum
^
With motion, the cursor is left on the space but the operation is not "dot-repeatable":
i <Esc><Left>
<Left>a <Esc>
s <C-r>"<Esc><Left>
lorem -ipsum
^
The workaround is a bit contrived but nifty.
Maybe I did not understand your question well enough, but for me this does the trick:
:nnoremap <leader>h i<space><esc>w
Rationale: Insert space, after <ESC> go one left, so the cursor stays on the space, use w to get to the next word boundary.
Is that what you want? I can now do 5\h and it stays at the current position while inserting spaces to the left.

Highlight f key searches in vim

Often when I'm using f to search for characters in the current line, I'll run into more occurrences of the character than I expected so highlighting each search match would be nice.
In the example below, let's say I'm starting at the beginning of the line and am trying to get to e in vowels. It would be helpful to highlight each of those occurrences so that I could get some context on the number of times to press ; after the initial search
# Here is a comment with a lot of vowels and I have passed it now
How does Vim's current implementation of f know how to wait for only a single character instead of a newline?
I would prefer to overwrite the builtin f functionality so I'm using a remap similar to this, but one of the problems is that it expects me to press enter at the end.
nnoremap f :call HighlightFSearches(input(''))<CR>
Currently have some issues with implementing my function HighlightFSearches as well, but one problem at a time.
Also, not really looking for a plugin and yes I know I can just do a search instead of using f but my brain seems to prefer going with f first in a lot of cases.
Update
Here's my final solution with much thanks to #filbranden below!
function! HighlightFSearches(cmd)
" Get extra character for the command.
let char = nr2char(getchar())
if char ==# ''
" Skip special keys: arrows, backspace...
return ''
endif
" Highlight 'char' on the current line.
let match_str = 'match Visual "\%' . line('.') . 'l' . char . '"'
execute match_str
" Finally, execute the original command with char appended to it
return a:cmd.char
endfunction
" highlight searches using 'f'
nnoremap <expr> f highlighting#HighlightFSearches('f')
nnoremap f<bs> <nop>
vnoremap <expr> f highlighting#HighlightFSearches('f')
vnoremap f<bs> <nop>
" highlight searches using 'F'
nnoremap <expr> F highlighting#HighlightFSearches('F')
nnoremap F<bs> <nop>
vnoremap <expr> F highlighting#HighlightFSearches('F')
vnoremap F<bs> <nop>
Note that I chose the Highlight Group used for visual selects. You could choose a different one or make your own too
The short answer is that you should use getchar() to get a single character from the user.
The long answer is that this gets somewhat complicated pretty quickly, since you need to deal with special keys and corner cases while handling getchar().
Note that getchar() may return a number (for a normal keypress, which you can convert to a character with nr2char()), or a string, starting with a special 0x80 byte for special keys (backspace, arrows, etc.)
A simplistic approach (but somewhat effective) is that running nr2char() on the strings returned for the special keys will return an empty string, so we can use that to skip those.
The next advice is that you can use <expr> in your mappings to return the new command as a string. That, together with non-recursive mappings, allow you to return the actual f command itself at the end of the function, so that part of emulating it is taken care of!
Finally, one more trick you might want to use is to create a "dummy" mapping for f followed by an invalid character. The fact that such a 2-character mapping exists makes it so that your f mapping won't trigger until a second character has been entered, and this will prevent Vim from moving the cursor to the last line while waiting for a character, making the f emulation more seamless.
Putting it all together:
function! HighlightFSearches(cmd)
" Get extra character for the command.
let char = nr2char(getchar())
if char ==# ''
" Skip special keys: arrows, backspace...
return ''
endif
" Here you'll want to highlight "char"
" on the current line.
" Finally, execute the original command.
return a:cmd.char
endfunction
nnoremap <expr> f HighlightFSearches('f')
nnoremap f<bs> <nop>
The function is written in a way that you can easily reuse it for F, t and T.
For highlighting the matches, you can either use :match (or :2match, :3match) or maybe you could set #/ and let 'hlsearch' do the highlighting...
You'll probably want to anchor the regexp on the current line, so only those matches are highlighted, see :help /\%l for what you can use for that.
Finally, you'll probably want to clear the highlighting if you move to a different line. Take a look at the CursorMoved event of autocmd for that purpose.
There are quite a few details to iron out, but hopefully this will clarify how to emulate the command part of getting the character to search for.
The short and sweet answer is to substitute input() for getchar()

How to come back to INSERT mode with the cursor at the same place from SELECT mode and (insert) SELECT mode? [duplicate]

Is it possible to cancel the said behavior?
A task for extra credit: Figure out a way to force Vim to refresh the cursor position immediately after exiting Insert mode.
Although I would not recommend changing the default cursor mechanics,
one way of achieving the behavior in question is to use the following
Insert-mode mapping.
:inoremap <silent> <Esc> <Esc>`^
Here the Esc key is overloaded in Insert mode to additionally
run the `^ command which moves the cursor to the position where it
had been the last time Insert mode was left. Since in this mapping it
is executed immediately after leaving Insert mode with Esc,
the cursor is left one character to the right as compared to its
position with default behavior.
Unlike some other workarounds, this one does not require Vim to be
compiled with the +ex_extra feature.
Although there are tricks to deal with this (such as the ESC mappings mentioned in the previous two posts), there's no consistent way to do this. The reason is that there is no way to determine the method that was used to enter insert mode. Specifically, given the string abcDefg with the cursor on the D:
If you press i, the insert mode location will be between the c and D. A normal ESC will put the cursor on c; <C-O>:stopinsert<CR> (or the backtick method) will put the cursor on D.
If you press a, the insert mode location will be between the D and e. A normal ESC will put the cursor on D; <C-O>:stopinsert<CR> will put the cursor on e.
If you REALLY want to do this, you could fudge it with something like this:
let insert_command = "inoremap <ESC> <C-O>:stopinsert<CR>"
let append_command = "iunmap <ESC>"
nnoremap i :exe insert_command<CR>i
nnoremap a :exe append_command<CR>a
BUT: remember that this will only deal with i and a as methods of entry: if you use visual block mode, I, or A or whatever, you'll need to come up with new commands to match (and there are a lot of them). Therefore, I'd strongly recommend you don't do this.
Personally, I'd recommend getting used to the default behaviour. You can easily make it logical for i OR logical for a. If you change the default to logical for i at the expense of logical for a, you'll just get confused when you use a standard vi/vim install.
Based on Nathan Neff's comment, the best approach I've found is
autocmd InsertLeave * :normal! `^
set virtualedit=onemore
autocmd moves the cursor back to where it was when insert mode ended (i.e. one forward compared to the default).
virtualedit makes it act consistently at end of line (so it can be one forward of the last character on the line).
(Edited: normal! to avoid recursive mappings)
inoremap <silent> <Esc> <C-O>:stopinsert<CR>
in your .vimrc.
I do believe the proper way to do this is
au InsertLeave * call cursor([getpos('.')[1], getpos('.')[2]+1])
There is an approach from the Vim Tips wiki that has worked well for me for...I don't know how many years:
" Leave insert mode to the *right* of the final location of the insertion
" pointer
" From http://vim.wikia.com/wiki/Prevent_escape_from_moving_the_cursor_one_character_to_the_left
let CursorColumnI = 0 "the cursor column position in INSERT
autocmd InsertEnter * let CursorColumnI = col('.')
autocmd CursorMovedI * let CursorColumnI = col('.')
autocmd InsertLeave * if col('.') != CursorColumnI | call cursor(0, col('.')+1) | endif
What about:
:imap <Esc> <Esc><Right>

Automatically insert a matching brace in Vim

I spend way too much time fumbling around because Vim doesn't handle closing braces like most IDEs do. Here's what I want to happen:
Type this:
if( whatever )
{ <CR>
and get this:
if( whatever )
{
|
}
where <CR> mean hit the ENTER key and | is the position of the cursor. This is what Eclipse does. It's what Visual Studio does. And it's what I want Vim to do.
I've seen a few plugins, tried a few, and none of them seem to give me this behavior. Surely I can't be the first programmer to want this.
In VimL, you can map the { to do exactly as you wish:
inoremap { {<CR>}<Esc>ko
depending on your autoindent setup, you may want to add a <BS> after <CR>.
For a more complete solution, I'd suggest you take a look at Luc Hermitte's vim plugins. They've never failed me so far.
No need for plugin. Much cleaner and flexible solution:
inoremap { {}<Esc>ha
inoremap ( ()<Esc>ha
inoremap [ []<Esc>ha
inoremap " ""<Esc>ha
inoremap ' ''<Esc>ha
inoremap ` ``<Esc>ha
Details about vim and above mapping.
Mapping:- a mapping is a way to define a shortcut for a sequence of keystrokes.
keystroke:- a keystroke refers to a single key press on the keyboard.
About above mappings.
The :inoremap command is used to create a mapping that works in insert mode. Whenever you type { in insert mode, it will be replaced with {}. The <Esc> is used to exit insert mode, and the ha moves the cursor back to the position after the opening curly brace. Like that these all mapping works.
Using AutoClose with the following works correctly.
inoremap {<CR> {<CR>}<C-o>O
This is true for my system at least (Unix terminal on Mac OS X).
A solution for braces, brackets and parenthesis with tab in between.
" Automatically closing braces
inoremap {<CR> {<CR>}<Esc>ko<tab>
inoremap [<CR> [<CR>]<Esc>ko<tab>
inoremap (<CR> (<CR>)<Esc>ko<tab>
Result:
function() {
|
}
Here is what I have in my vimrc:
let s:pairs={
\'<': '>',
\'{': '}',
\'[': ']',
\'(': ')',
\'«': '»',
\'„': '“',
\'“': '”',
\'‘': '’',
\}
call map(copy(s:pairs), 'extend(s:pairs, {v:val : v:key}, "keep")')
function! InsertPair(left, ...)
let rlist=reverse(map(split(a:left, '\zs'), 'get(s:pairs, v:val, v:val)'))
let opts=get(a:000, 0, {})
let start = get(opts, 'start', '')
let lmiddle = get(opts, 'lmiddle', '')
let rmiddle = get(opts, 'rmiddle', '')
let end = get(opts, 'end', '')
let prefix = get(opts, 'prefix', '')
let start.=prefix
let rmiddle.=prefix
let left=start.a:left.lmiddle
let right=rmiddle.join(rlist, '').end
let moves=repeat("\<Left>", len(split(right, '\zs')))
return left.right.moves
endfunction
noremap! <expr> ,f InsertPair('{')
noremap! <expr> ,h InsertPair('[')
noremap! <expr> ,s InsertPair('(')
noremap! <expr> ,u InsertPair('<')
And, for some filetypes:
inoremap {<CR> {<C-o>o}<C-o>O
// I know that InsertPair function is trivial, but it saves time because with it I can define both command and normal mode mappings with one command without having to write lots of <Left>s.
Put the following in your .vimrc file:
inoremap { {}<ESC>ha
Whenever you press { in insert mode, {} is generated and puts your cursor on the right brace, so that you can start typing between them straight away. By putting the curly braces in sequence rather than on different lines, you can put tabs in front of } manually. That way you never have the wrong amount of tabs in front of it.
Perhaps someone can figure out how to count the amount of tabs the cursor is on, and then generate an equal amount of tabs in front of the } on a new line.
inoremap ( ()<ESC>i
inoremap " ""<ESC>i
inoremap ' ''<ESC>i
inoremap { {<Cr>}<Esc>O
For anyone that runs across this like I did, and was looking for something more recently updated than AutoClose: delimitMate I have found to be, not only a preferable solution to AutoClose, behavior wise, but also in active development. According to vim.org, AutoClose has not been updated since 2009.
I have tried different plugins but I found most accurate and most easy to use auto-pairs. It is really intuitive and when you install it you get what you've expected out of the box.
I've always preferred something like what sublime text does where it appends the closing brace as the next character, so I added the following to my .vimrc:
inoremap ( ()<ESC>hli
which moves the cursor to between the two braces.
As you'll see in the wikia tip: there are many solutions to this recurrent question (I even have mine).
That is if you limit yourself to bracket pairs. Here you are in the context of a control statement. You're thus more likely to find snippet systems that will not expect you to type the ") {" when typing an "if" statement. Vim shortcut tend to be shorter from what I read in your question. Here again there are a lot of choices, you'll find most likely snipmate, and may be my C&C++ suite.
Insert this into your ~/.vimrc if you have auto-indent enabled:
inoremap {<CR> {<CR>}<Esc>ko
inoremap [<CR> [<CR>]<Esc>ko
inoremap (<CR> (<CR>)<Esc>ko
and if not
inoremap {<CR> {<CR>}<Esc>ko<tab>
inoremap [<CR> [<CR>]<Esc>ko<tab>
inoremap (<CR> (<CR>)<Esc>ko<tab>
Then you can map a key (in my case the key is ä, this can be replaced with anything you want)...
map ä A<space>{<CR>
...to automatically do all of this for you, if you are anywhere in the line on key press.
example ('|' symbolizes where your cursor is):
int main(int a|rgc)
When you press the key now (in my case ä in command mode), the result will be this:
int main(int argc) {
|
}
delimitMate has a setting for this.
Vim patch 7.4.849 added a binding to allow for cursor movements without restarting the undo sequence. Once updated to >= 7.4.849 then something like this works great.
inoremap ( ()<C-G>U<Left>
Note that I grabbed that straight from the documentation included in the patch. Best simple solution for this feature yet.
commit for patch 7.4.849:
https://github.com/vim/vim/commit/8b5f65a527c353b9942e362e719687c3a7592309
mailing list thread: http://vim.1045645.n5.nabble.com/Any-automatic-bracket-insertion-plugins-not-breaking-undo-td5723654.html
Install and use Vim script AutoClose as recommended in the article titled Automatically append closing characters.
Just a note to #Bob.
Karl Guertin's AutoClose has a function named ``double brace'', that is, you can type curly brace twice, as below.
int func_name(void) {{ ==> Type `{' twice here.
would result in:
int func_name(void) {
| ==> Cursor here.
}
Then, you can type a single Tab, to get indented according to your `shiftwidth' setting, then type.
If you type {} and hit alti you will be in between the braces in INSERT mode (at least in a terminal). Then you can hit ENTER followed by altshifto to insert the line break. You could also just do {<CR>} and altshifto.
This may not be fully automatic, but I consider it semi-auto. It removes the need for more plugins, and is useful info to know for other use cases. For example, I use altshifto all the time to insert blank lines without having to explicitly leave INSERT mode, and alti for getting inside () etc.
You do not need a special plugin to do this - but it is a two-step process.
First, add the following to your .vimrc to eat the triggering character:
" eat characters after abbreviation
function! Eatchar(pat)
let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c
endfunction
and then add this abbreviation to your .vimrc:
inoreabbr <silent> { {
\<cr><space><space>
\<cr><esc>0i}<esc>k$i<c-r>=Eatchar('\m\s\<bar>\r')<cr>
The \ at the start of lines two and three is just a line continuation character. You could have done this all on one line, however and i added it so that i could spread the abbreviation out in a way that mirrors the output you're looking for -- just so things are a little more intuitive.
My solution:
inoremap <expr> <CR> InsertMapForEnter()
function! InsertMapForEnter()
if pumvisible()
return "\<C-y>"
elseif strcharpart(getline('.'),getpos('.')[2]-1,1) == '}'
return "\<CR>\<Esc>O"
elseif strcharpart(getline('.'),getpos('.')[2]-1,2) == '</'
return "\<CR>\<Esc>O"
else
return "\<CR>"
endif
endfunction
Explaination:
The code above first check if you are using Enter to do confirm a code completion, if not it will indent the {|} when you type Enter. Also, it provides html tags auto indent.
Examples:
if( whatever ){|}
press Enter and you will get
if( whatever )
{
|
}
This also works for html file. See the following example
<html>|<html>
press Enter and you will get
<html>
|
</html>
This works great!
Put this in your .vimrc.
inoremap { {^M}<C-o>dd<C-o>k<C-o>]p<C-o>O
This matches the indenting level to the indenting level of the first {.

How to prevent the cursor from moving back one character on leaving Insert mode in Vim?

Is it possible to cancel the said behavior?
A task for extra credit: Figure out a way to force Vim to refresh the cursor position immediately after exiting Insert mode.
Although I would not recommend changing the default cursor mechanics,
one way of achieving the behavior in question is to use the following
Insert-mode mapping.
:inoremap <silent> <Esc> <Esc>`^
Here the Esc key is overloaded in Insert mode to additionally
run the `^ command which moves the cursor to the position where it
had been the last time Insert mode was left. Since in this mapping it
is executed immediately after leaving Insert mode with Esc,
the cursor is left one character to the right as compared to its
position with default behavior.
Unlike some other workarounds, this one does not require Vim to be
compiled with the +ex_extra feature.
Although there are tricks to deal with this (such as the ESC mappings mentioned in the previous two posts), there's no consistent way to do this. The reason is that there is no way to determine the method that was used to enter insert mode. Specifically, given the string abcDefg with the cursor on the D:
If you press i, the insert mode location will be between the c and D. A normal ESC will put the cursor on c; <C-O>:stopinsert<CR> (or the backtick method) will put the cursor on D.
If you press a, the insert mode location will be between the D and e. A normal ESC will put the cursor on D; <C-O>:stopinsert<CR> will put the cursor on e.
If you REALLY want to do this, you could fudge it with something like this:
let insert_command = "inoremap <ESC> <C-O>:stopinsert<CR>"
let append_command = "iunmap <ESC>"
nnoremap i :exe insert_command<CR>i
nnoremap a :exe append_command<CR>a
BUT: remember that this will only deal with i and a as methods of entry: if you use visual block mode, I, or A or whatever, you'll need to come up with new commands to match (and there are a lot of them). Therefore, I'd strongly recommend you don't do this.
Personally, I'd recommend getting used to the default behaviour. You can easily make it logical for i OR logical for a. If you change the default to logical for i at the expense of logical for a, you'll just get confused when you use a standard vi/vim install.
Based on Nathan Neff's comment, the best approach I've found is
autocmd InsertLeave * :normal! `^
set virtualedit=onemore
autocmd moves the cursor back to where it was when insert mode ended (i.e. one forward compared to the default).
virtualedit makes it act consistently at end of line (so it can be one forward of the last character on the line).
(Edited: normal! to avoid recursive mappings)
inoremap <silent> <Esc> <C-O>:stopinsert<CR>
in your .vimrc.
I do believe the proper way to do this is
au InsertLeave * call cursor([getpos('.')[1], getpos('.')[2]+1])
There is an approach from the Vim Tips wiki that has worked well for me for...I don't know how many years:
" Leave insert mode to the *right* of the final location of the insertion
" pointer
" From http://vim.wikia.com/wiki/Prevent_escape_from_moving_the_cursor_one_character_to_the_left
let CursorColumnI = 0 "the cursor column position in INSERT
autocmd InsertEnter * let CursorColumnI = col('.')
autocmd CursorMovedI * let CursorColumnI = col('.')
autocmd InsertLeave * if col('.') != CursorColumnI | call cursor(0, col('.')+1) | endif
What about:
:imap <Esc> <Esc><Right>

Resources