Vim: Make function with count only operate once - vim

I have this function:
function Test()
echom "call " . v:count
endfunction
nnoremap a :call Test()<cr>
If I type, let's say 4a, it will print out
call 4
call 4
call 4
call 4
However, I want it only to be executed once, when I use a count. How can I achieve that?

You need <C-U> before calling the function.
function Test()
echom "call " . v:count
endfunction
nnoremap a :<C-U>call Test()<cr>
<C-U> removes all the characters before the cursor on command line. From help:
c_CTRL-U
CTRL-U Remove all characters between the cursor position and
the beginning of the line. Previous versions of vim
deleted all characters on the line. If that is the
preferred behavior, add the following to your .vimrc:
:cnoremap <C-U> <C-E><C-U>

Related

Move to character and drop into insert mode in VIM

I recently fell in love with the f, F, t, and T commands in vim. Now I find myself frequently wanting to insert something at a position that can easily be navigated to with one of these commands, at least frequently enough that I would want to make the entire action "find character and insert text" repeatable via .. Currently, I can repeat the insert action, but I have to retype the find-character movement at every line where I want to repeat the insert.
Is there a command that combines the actions of these movement commands with the action of dropping into insert mode? Or, if not, is it possible to define such a command in my .vimrc?
First, you can repeat the last f/t/F/T motion via ; (reverse via ,), so you can repeat with two keys: ;.
If that's not good enough, the repeat.vim plugin can be used to build a custom mapping that repeats just like the built-in commands:
"<Leader>it{char} Insert text before the [count]'th occurrence of {char}
" to the right.
"<Leader>if{char} Insert text after the [count]'th occurrence of {char}
" to the right.
" These mappings can be repeated atomically, this is
" faster than ";."
function! s:InsertAtCharPrepare( motion, moveOffMotion, repeatMapping )
augroup InsertAtChar
autocmd!
" Enter insert mode automatically after the f/t motion.
" XXX: :startinsert doesn't work on the first movement somehow, use
" feedkeys() instead.
autocmd CursorMoved <buffer> call feedkeys('a', 'n')
" Prime repeat.vim after insertion is done.
execute printf('autocmd InsertLeave <buffer> %scall repeat#set(%s, %d) | autocmd! InsertAtChar',
\ (v:count1 <= 1 || empty(a:moveOffMotion) ? '' : 'execute "normal!" ' . string(a:moveOffMotion) . '|'),
\ string(a:repeatMapping),
\ v:count1
\)
" Abort in case something unexpected happens.
autocmd WinLeave,BufLeave <buffer> autocmd! InsertAtChar
augroup END
return a:motion
endfunction
function! s:InsertAtCharRepeat( moveOffMotion, repeatMapping )
let l:count = v:count1 " Save the original count to pass this on to repeat.vim.
execute 'normal!' l:count . ';.' . (l:count <= 1 ? '' : a:moveOffMotion)
call repeat#set(a:repeatMapping, l:count)
endfunction
" With "t" and [count] > 1, we need to move off from before {char} (where we're
" left when leaving insert mode) onto {char}, so that a repeat will happen
" before the next occurrence, not on the same again.
nnoremap <silent> <Plug>(InsertUntilCharRepeat) :<C-u>call <SID>InsertAtCharRepeat('l', "\<lt>Plug>(InsertUntilCharRepeat)")<CR>
nnoremap <silent> <Plug>(InsertFromCharRepeat) :<C-u>call <SID>InsertAtCharRepeat('', "\<lt>Plug>(InsertFromCharRepeat)")<CR>
nnoremap <expr> <Leader>it <SID>InsertAtCharPrepare('t', 'l', "\<lt>Plug>(InsertUntilCharRepeat)")
nnoremap <expr> <Leader>if <SID>InsertAtCharPrepare('f', '', "\<lt>Plug>(InsertFromCharRepeat)")

Can Vim apply shiftround to a block as a whole instead of linewise?

I generally like the shiftround option in Vim, but there are a couple of
situations where it doesn't work very well. For instance, take this example:
f(x,
y)
Selecting the two lines and shifting with > and the two lines selected gives
me (and shiftwidth set to 4):
f(x,
y)
When I really wanted:
f(x,
y)
In other words, Vim advanced each line of the block to the next tabstop, when I
really wanted it to insert the same amount of inserted on each line--but I want
the least indented line to end up on the next tabstop.
Is there an easy way to get this behavior in Vim? My goal is to have this work
for < and > (with a visual selection) rather than other workarounds.
Ctrl+V, select vertical column before the text you need to shift (j in your case of just two lines), Shift+I, insert needed number of tabs or spaces, Esc.
You can actually use this method to insert arbitrary text, for example you can insert # or // to comment some code. More info in :help blockwise-operators.
I would solve this via a separate set of mappings that enclose the >> commands with a temporary clearing of 'shiftround'. Here's an implementation:
" g>> Shift [count] lines one 'shiftwidth' rightwards, without
" 'shiftround'.
"{Visual}[count]> Shift the highlighted lines [count] 'shiftwidth'
" rightwards (for {Visual} see |Visual-mode|), without
" 'shiftround'.
" g<<, {Visual}[count]<
function! s:Shift( command )
let s:save_shiftround = &shiftround
set noshiftround
return a:command
endfunction
function! s:RestoreShiftRound()
let &shiftround = s:save_shiftround
return ''
endfunction
nnoremap <expr> <SID>(RestoreShiftRound) <SID>RestoreShiftRound()
nnoremap <expr> <SID>(ShiftRight) <SID>Shift('>>')
xnoremap <expr> <SID>(ShiftRight) <SID>Shift('>')
nnoremap <expr> <SID>(ShiftLeft) <SID>Shift('<<')
xnoremap <expr> <SID>(ShiftLeft) <SID>Shift('<')
nnoremap <silent> <script> <Plug>(ShiftRightNoRound) <SID>(ShiftRight)<SID>(RestoreShiftRound)
xnoremap <silent> <script> <Plug>(ShiftRightNoRoundSelection) <SID>(ShiftRight)<SID>(RestoreShiftRound)
nnoremap <silent> <script> <Plug>(ShiftLeftNoRound) <SID>(ShiftLeft)<SID>(RestoreShiftRound)
xnoremap <silent> <script> <Plug>(ShiftLeftNoRoundSelection) <SID>(ShiftLeft)<SID>(RestoreShiftRound)
nmap g>> <Plug>(ShiftRightNoRound)
xmap g> <Plug>(ShiftRightNoRoundSelection)
nmap g<< <Plug>(ShiftLeftNoRound)
xmap g< <Plug>(ShiftLeftNoRoundSelection)
I didn't cover the >{motion} command, as that would be more complex.

vim: matter is exec or normal command?

source code:
function! myfunc()
execute "normal! i\<c-r>=12+34\<cr>"
endfunc
inoremap <silent><Tab> <C-R>=myfunc()<CR>
i have the above code in my .vimrc, which means if i'm typing <tab> in insert mode and then it will append 46. for example, open a new file, type "hello"(no double quote here) and then press <tab> key, the result should be "hello46". While it doesn't. so why?
This is how you do simple math:
execute "normal! i" . (12 + 34)
But you could use this form:
inoremap <expr> <tab> 12 + 34
edit
Your errors:
Function names must begin with a capital.
<C-r>= is used to insert the result of an expression but your function doesn't return anything: it executes a normal mode command instead.
This version of your snippet works. The function returns something that you can insert with <C-r>= in your mapping:
function! MyFunc()
let myvar = "foo"
return myvar
endfunc
inoremap <silent> <Tab> <C-r>=MyFunc()<CR>

Setting to skip over punctuation when moving forwards and backwards words

When I'm using vim I generally never want to move to a punctuation mark when I press w or b to go forwards or backwards. So I'm wondering if there's a setting or something to change this functionality?
e.g. If I've got some code like
object.method(args)
and my cursor is at the [o] in "object" then I want w to move to the [m] in "method", and another w to move to the [a] in "args". I don't want it to land on the [.] or the [(]. If I've ever wanted to move to a punctuation char I've always used f or F to jump straight to it. I've never personally wanted to move to a punctuation char when I move through words and I just realized this is really bugging me.
I too find that I would like a movement that is more inclusive that w, but not as inclusive as W. In particular, I would like a movement that only considers tokens beginning with alphanumeric characters as significant.
So I came up with the following:
" <SPACE> : forward to next word beginning with alphanumeric char
" <S-SPACE> : backward to prev word beginning with alphanumeric char
" <C-SPACE> : same as above (as <S-SPACE> not available in console Vim
" <BS> : back to prev word ending with alphanumeric char
function! <SID>GotoPattern(pattern, dir) range
let g:_saved_search_reg = #/
let l:flags = "We"
if a:dir == "b"
let l:flags .= "b"
endif
for i in range(v:count1)
call search(a:pattern, l:flags)
endfor
let #/ = g:_saved_search_reg
endfunction
nnoremap <silent> <SPACE> :<C-U>call <SID>GotoPattern('\(^\\|\<\)[A-Za-z0-9_]', 'f')<CR>
vnoremap <silent> <SPACE> :<C-U>let g:_saved_search_reg=#/<CR>gv/\(^\\|\<\)[A-Za-z0-9_]<CR>:<C-U>let #/=g:_saved_search_reg<CR>gv
nnoremap <silent> <S-SPACE> :<C-U>call <SID>GotoPattern('\(^\\|\<\)[A-Za-z0-9_]', 'b')<CR>
vnoremap <silent> <S-SPACE> :<C-U>let g:_saved_search_reg=#/<CR>gv?\(^\\|\<\)[A-Za-z0-9_]<CR>:<C-U>let #/=g:_saved_search_reg<CR>gv
nnoremap <silent> <BS> :call <SID>GotoPattern('[A-Za-z0-9_]\(\>\\|$\)', 'b')<CR>
vnoremap <silent> <BS> :<C-U>let g:_saved_search_reg=#/<CR>gv?[A-Za-z0-9_]\(\>\\|$\)<CR>:<C-U>let #/=g:_saved_search_reg<CR>gv
" Redundant mapping of <C-SPACE> to <S-SPACE> so that
" above mappings are available in console Vim.
"noremap <C-#> <C-B>
if has("gui_running")
map <silent> <C-Space> <S-SPACE>
else
if has("unix")
map <Nul> <S-SPACE>
else
map <C-#> <S-SPACE>
endif
endif
I have had this for a long time now, and I find that I use <SPACE>/<C-SPACE> movements so much more than w and W; it just seems more useful when coding. You can, of course, map the commands to whatever keys you find useful or more appropriate.
Even running the risk of creating a script for something that's built-in (like
I did last time), here is a little function that may help accomplishing
this.
function! JumpToNextWord()
normal w
while strpart(getline('.'), col('.')-1, 1) !~ '\w'
normal w
endwhile
endfunction
Basically, what it does is executing the standard w and repeating it
if the character under the cursor is not in a word character (feel free to
change that pattern.
If you add that and a little map in your .vimrc:
nnoremap <silent> ,w :call JumpToNextWord()<CR>
It should work.

vim function for toggling colorschemes

At the moment I'm using two different keys to toogle the colorscheme
map <F8> :colors wombat256 <cr>
map <F9> :colors dimtag <cr>
I want to achieve a toggle behavior like this
function! ToggleDimTags()
if (g:colors_name == "wombat256")
colors dimtag
else
colors wombat256
endif
endfunction
My problem is that ToogleDimTags() is resetting the cursor position to the first line on every call, which is undesirable. Any suggestions appreciated.
As discussed in the comments, the problem is that your map calling :execute
behaves a little differently, what you probably want is :call instead:
nnoremap <F5> :call ToggleDimTags()
To clarify what #ZyX was saying, :h :exec contains the following text:
:exe :execute
:exe[cute] {expr1} .. Executes the string that results from the evaluation
of {expr1} as an Ex command.
[...]
So what :execute really does is evaluating the expression looking for a string
that will be executed as an Ex command (also called colon commands). In other words:
exec ToggleDimTags() | " <-- ToggleDimTags() is evaluated and returns 0
exec 0
Which is:
:0
Now, :h :call:
:cal :call E107 E117
:[range]cal[l] {name}([arguments])
Call a function. The name of the function and its arguments
are as specified with |:function|. Up to 20 arguments can be
used. **The returned value is discarded**.
[...]
Update
I've been thinking about your function, and using the ternary operator and a bit
of :execute magic, you can simplify it up to a point where you discard the extra
function:
nnoremap <silent> <F9> :exec "color " .
\ ((g:colors_name == "wombat256") ? "dimtag" : "wombat256")<CR>
Here, this nnoremap will not produce output (<silent>) and is based on
:exec, which is followed by this expression:
"color " . ((g:colors_name == "wombat256") ? "dimtag" : "wombat256")
When g:colors_name is set to wombat256, the expression evaluates to:
"color dimtag"
Or, otherwise:
"color wombat256"
Then either one is evaluated by :exec. Of course you can join the lines
(without forgetting to remove the backslash), I did it like this simply to avoid
a too long line.

Resources