how to set vim map not change jumplist? - vim

[first line]
int test() {
a
b
c
d
{
e
f
g
}
}
The cursor in currently in line with f.
If I press [[, the cursor will go to [first line].
I want cursor goto int test() {. So I set a mapping: nnoremap [[ ][%.
The map command works good, with one problem: if I press CTRL+O, cursor will go to the last line }.
What I want is the cursor to go back to the line with f.
I tried nnoremap [[ :keepjumps normal ][%<CR>, but it does not work.
How can I implement this?
press [[ make cursor goto line with int test(){.
press CTRL+O make cursor jump back.
My vim version is Vi IMproved 7.4

The way the jump list works is very often misunderstood. A jump, as stored in the jump list, only records the origin of the "jump" you made, not its destination. This means that :help :keepjumps is supposed to be used to prevent a new origin to be added to the jump list.
In order for <C-o> to jump back to the origin f, that origin must be present in the jump list so it's only the origin } that must be left out.
In doing:
nnoremap [[ :keepjumps normal ][%<CR>
you are effectively preventing both the origin f and the origin } to be added to the jump list.
You can fix your mapping by moving ][ out of the :keepjumps command:
nnoremap [[ ][:keepjumps normal %<CR>

Related

Vim how to leave cursor at the end of the line after autocommand

I'm trying to make my own snippets in pure vimscript using autocommands. I want to make it so that after I type a specific character sequence, it is replaced by another character sequence. I made one like this, that replaces "hello" with "bye" when you type it.
function F()
if (strpart (getline('.'), 0, col('.')-1) =~ 'hello$')
execute "normal 5i\<Backspace>"
normal! abye
normal! l
endif
endfunction
autocmd TextChangedI *.tex call F()
And it works fine if there are characters after the cursor. However, if I am writing at the end of the line, after the change the cursor is between 'y' and 'e', because the autocommand calls the function, the cursor is at the end of the line, and then it enters insert mode which starts inserting before the last character.
How can I make it so that the cursor is always left after 'bye'? I don't want to use iabbrev for a couple of reasons, one being that it doesn't expand as soon as I type.
I can do it with the option
set ve+=onemore
but I don't like it's effects on normal writing.
How about the following, which uses setline and cursor functions rather than normal keymaps:
function! F() abort
let [l:line, l:col] = [getline('.'), col('.')]
if strpart(l:line, 0, l:col-1) =~ 'hello$'
let l:left = strpart(l:line, 0, l:col-6)
let l:right = strpart(l:line, l:col-1)
call setline('.', l:left . 'bye' . l:right)
call cursor('.', l:col-2) " 2 = length diff between hello and bye
endif
endfunction
This seems working for me (on Neovim 0.6).

How to make f and t wrap around the current line in vim

Is there a way to make the 'f' and 't' command wrap around the line? For example, if I have
Hello, my name is _intz,
where _ denotes my cursor position, I would like to be able to press fl for vim to place my cursor on the first l on the line.
Similarly, I would ideally like the , and ; commands to also wrap on the current line.
Thank you
No, this is not possible without implementing the feature yourself.
Note that fF are universally expected to mean "next on the line" and tT to mean "previous on the line", both of which being extremely useful in their own right. Instead of changing their meaning, and thus reducing the overall usefulness of Vim, you should consider making new commands.
Something like these quick and dirty mappings:
" move the cursor on first occurrence of character on the line
nnoremap <expr> <key> '0f' . nr2char(getchar())
" move the cursor before first occurrence of character on the line
nnoremap <expr> <key> '0t' . nr2char(getchar())
See :help <expr>, :help nr2char(), :help getchar().
With the help of this answer https://vi.stackexchange.com/questions/29167/determine-if-there-is-a-matching-character-on-the-current-line-past-the-cursor, the following maps <c-f> to allow that gives it the functionality of f with same line wrapping.
function!Neweff()
let character = nr2char(getchar())
let beforejump = getpos('.')
execute 'norm! f'.character.''
let afterjump = getpos('.')
if beforejump == afterjump
let firstcharacter = getline(".")[0]
execute 'norm! 0'
if character !=# firstcharacter
execute 'norm! f'.character.''
endif
endif
endfunction
nnoremap <c-f> :call Neweff()<CR>

Fix cursor position [duplicate]

I'm a C# developer and I'm new to using Vim. I'm curious, what would you put in your .vimrc file so that when you type a curly brace and hit Enter, like this:
public void Foo() {<Enter>
that brace gets put on a new line, and the caret moves to the innards of the braces, like this:
public void Foo()
{
|
}
Any help would be appreciated, thanks!
Your question is very close to auto-close plugin issuse with curly brackets where
inoremap <expr> <cr> getline(".")[col(".")-2:col(".")-1]=="{}" ? "<cr><esc>O" : "<cr>"
answers the question -- this is somewhat what I use in lh-brackets.
Here, it seems you aren't using any bracketing plugin. The text to search becomes '\S\s*{$', and you can insert instead: <BS><cr>{<cr>}<esc>O, or just {<cr>}<esc>O if there is only { on the line. If the cursor is within {}, you should also integrate the previous test.
This becomes:
inoremap <expr> <cr>
\ getline(".") =~ '\S\s*{$' ? "<bs><cr>{<cr>}<esc>O"
\ : getline('.') =~ '^\s*{$' ? "<cr>}<esc>O"
\ : getline(".")[col(".")-2:col(".")-1]=="{}" ? "<cr><esc>O"
\ : "<cr>"
There are a lot of ways to program this. Just as an example I might do this:
inoremap {<Cr> <Esc>:call AutoBracketDrop()<Cr>a
function! AutoBracketDrop()
if col('.') == col('$') - 1
substitute /\s*$//
exec "normal! A\<Cr>{\<Cr>X\<Cr>}\<Esc>k$x"
else
exec "normal! a{\<Cr>\<Esc>"
endif
endfunction
First I add a insert map that will break out of insert and call the AutoBracketDrop function and when finished enter back into insert mode.
The function simply checks to see if the cursor column matches the last column of the line. If so we remove the trailing whitespace and append the test:
cursor is here>
{
X
}
Because the use of <Cr> it should keep the indentation as if you typed them. We have to have a marker X otherwise Vim will remove whitespace from the empty line and continuing with an append (a) will loose the indentation. This way the text ends up as:
cursor was here>
{
X
}
Then we move the cursor up a line, delete the X and exit the function.
In the case of it being in the middle of the line we just simple recreate the characters typed that were used to trigger the mapping.

How do I change the join character in Vim?

In Vim, one can join two lines by typing capital J.
However, these are usually joined by a space.
I seem to remember there was a way to change the character used for the joining by setting some variable, but I can't seem to find it again.
I'd appreciate it if anyone could remind me, or confirm that it can't be done.
When I want to join just a few lines I use a 3 keys combo (normal mode):
Jr,
being , the joining character.
In case I want to join more lines or even join lines in groups, I use the previous combo with a macro.
For example, to transform 3 lines in a 3 columns CSV table, I record this macro (assigned to letter j of course):
qjJr,Jr,jq
So, using #j joins 3 lines using , and goes to the next line.
10#j converts 10 lines.
There isn't a setting that allows you to do this directly, see:
:help J
in particular, the text below the list of commands.
A couple of ways you could do this:
:nnoremap J gJi.<ESC>
" or
let joinchar = ';'
nnoremap J :s/\n/\=joinchar/<CR>
The latter option allows you to change it on the fly by changing the joinchar option.
Try something like this in your .vimrc:
nnoremap Y Jxi*<Esc>
It'll remap Y to join the lines with a *.
From http://vim.wikia.com/wiki/Remap_join_to_merge_comment_lines
put this in your .vimrc:
function! JoinWithLeader(count, leaderText)
let l:linecount = a:count
" default number of lines to join is 2
if l:linecount < 2
let l:linecount = 2
endif
echo l:linecount . " lines joined"
" clear errmsg so we can determine if the search fails
let v:errmsg = ''
" save off the search register to restore it later because we will clobber
" it with a substitute command
let l:savsearch = #/
while l:linecount > 1
" do a J for each line (no mappings)
normal! J
" remove the comment leader from the current cursor position
silent! execute 'substitute/\%#\s*\%('.a:leaderText.'\)\s*/ /'
" check v:errmsg for status of the substitute command
if v:errmsg=~'Pattern not found'
" just means the line wasn't a comment - do nothing
elseif v:errmsg!=''
echo "Problem with leader pattern for JoinWithLeader()!"
else
" a successful substitute will move the cursor to line beginning,
" so move it back
normal! ``
endif
let l:linecount = l:linecount - 1
endwhile
" restore the #/ register
let #/ = l:savsearch
endfunction
nnoremap <space> :<C-U>call JoinWithLeader(v:count, '"')<CR>
This also allows you to remap J to something else.
It will quicker if you replace the end of line with a comma (or join character)
:%s/$/,
and then joining multiple lines either by providing a range, or by selecting lines in visual mode and using the join command
10J
It's mapping. You can read the tutorial in vim wikia :
Mapping keys in vim
Try the command below in command mode, and try to press . This should work :)
:map <space> J

How do I move to the next capital letter?

In vim I can use f followed by a character to go to the next occurrence of that character on the current line. For example, if I have the following (cursor position marked with |):
m|akeBinExprNode = undefined
I can use fB to move to B and dtE to delete until before the E, leaving me with:
make|ExprNode = undefined
I wonder if there's a way to do this that doesn't involve typing the exact character, i.e. some kind of motion that means "go to the next capital letter" and/or "go to right before the next capital letter".
When I searched for that I would be happy to just have the "native" solution: just enter in command mode:
/\u
which stands for "search for an uppercase letter". After that just move between capital letters with n and N (shift + n).
I would recommand the following script : camelcasemotion. It allows you to jump , delete inner 'camel case words', using ,+ normal navigation [w,b,e] etc...
I have found this vim tip for moving within CamelCaseWords that might be useful:
" Use one of the following to define the camel characters.
" Stop on capital letters.
let g:camelchar = "A-Z"
" Also stop on numbers.
let g:camelchar = "A-Z0-9"
" Include '.' for class member, ',' for separator, ';' end-statement,
" and <[< bracket starts and "'` quotes.
let g:camelchar = "A-Z0-9.,;:{([`'\""
nnoremap <silent><C-Left> :<C-u>call search('\C\<\<Bar>\%(^\<Bar>[^'.g:camelchar.']\#<=\)['.g:camelchar.']\<Bar>['.g:camelchar.']\ze\%([^'.g:camelchar.']\&\>\#!\)\<Bar>\%^','bW')<CR>
nnoremap <silent><C-Right> :<C-u>call search('\C\<\<Bar>\%(^\<Bar>[^'.g:camelchar.']\#<=\)['.g:camelchar.']\<Bar>['.g:camelchar.']\ze\%([^'.g:camelchar.']\&\>\#!\)\<Bar>\%$','W')<CR>
inoremap <silent><C-Left> <C-o>:call search('\C\<\<Bar>\%(^\<Bar>[^'.g:camelchar.']\#<=\)['.g:camelchar.']\<Bar>['.g:camelchar.']\ze\%([^'.g:camelchar.']\&\>\#!\)\<Bar>\%^','bW')<CR>
inoremap <silent><C-Right> <C-o>:call search('\C\<\<Bar>\%(^\<Bar>[^'.g:camelchar.']\#<=\)['.g:camelchar.']\<Bar>['.g:camelchar.']\ze\%([^'.g:camelchar.']\&\>\#!\)\<Bar>\%$','W')<CR>
vnoremap <silent><C-Left> :<C-U>call search('\C\<\<Bar>\%(^\<Bar>[^'.g:camelchar.']\#<=\)['.g:camelchar.']\<Bar>['.g:camelchar.']\ze\%([^'.g:camelchar.']\&\>\#!\)\<Bar>\%^','bW')<CR>v`>o
vnoremap <silent><C-Right> <Esc>`>:<C-U>call search('\C\<\<Bar>\%(^\<Bar>[^'.g:camelchar.']\#<=\)['.g:camelchar.']\<Bar>['.g:camelchar.']\ze\%([^'.g:camelchar.']\&\>\#!\)\<Bar>\%$','W')<CR>v`<o
wilhelmtell's answer will work unless 'ignorecase' parameter is set. If 'smartcase' is activated or 'noignorecase' then it is okay.
However a pattern that can replace [A-Z] is \u (see :help /\u or more globally :help pattern). Therefore you can replace your mapping with:
:nnoremap <leader>C /\u<CR>:nohlsearch<CR>
:nmap <leader>C /[A-Z]<CR>:nohlsearch<CR>
Then in normal mode <leader>C (which by default means \C)

Resources