vim: matter is exec or normal command? - vim

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>

Related

Vimscript - split function call to multiple lines

I'm trying to write a vimscript that converts a line like this:
myFuncCall(param1, param2, param3="hey", param4)
To:
myFuncCall(param1,
param2,
param3="hey",
param4
)
While maintaining and adding indentation. So far I have:
function SplitParamLines() abort
let f_line_num = line(".")
let indent_length = indent(f_line_num)
echom indent_length
echom f_line_num
.s/\s*,/,\r/g
nohlsearch
0t)a<cr>
endfunction
How do I indent lines using vimscript?
Why does the last line produces this?:
Error detected while processing function SplitParamLines:
line 7:
E14: Invalid address
So there are 2 options which I chose to use both:
Using this incredible plugin - Splitjoin.vim
Using this simple function:
" Every parameter in its own line
function SplitParamLines() abort
let f_line_num = line(".")
let indent_length = indent(f_line_num)
exe ".s/\s*,/,\r" . repeat(" ", indent_length + &shiftwidth - 1) . "/g"
nohlsearch
" execute "normal! 0t)a\<cr>\<esc>"
endfunction
nnoremap <silent> <leader>( :call SplitParamLines()<cr>
Although not perfect, it works :)
If you want to execute something that you would have typed, you need :normal. If there are special characters, then you'll also need :exe and to escape these special characters. IOW
:exe "normal! 0t)i\<cr>"

Vim: Make function with count only operate once

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>

Vim doesn't indent when inserting carriage return from a function?

So I'm trying to write a function that makes inserting semi colons a bit more pleasant:
inoremap <leader>; <esc>:call InsertSemiColin()<cr>
Basically it checks to see if I'm standing at the end of the current line, if so I auto-format the code, insert the semi-colon at the end and break down to the next line (carriage return)
fun! InsertSemiColin()
if (!IsEOL()) | exec "normal! a;" | return | endif
exec "normal! \<esc>:OmniSharpCodeFormat\<cr>A;\<cr>"
endf:
fun! IsEOL()
" col number == length of current line?
return col('.') == strlen(getline(line('.'))) " or just getline('.')
endf
Expectation:
Result:
To try it out on your own, you can remove the code-formatting and just do:
exec "normal! a;\<cr>"
My indentation settings:
set smartindent
set tabstop=4
set shiftwidth=4
filetype plugin indent on
The weird thing is that if I don't insert the carriage return from a function, it works as expected!
inoremap <leader>; ;<cr>
Why is this happening? and how can I get the result I'm expecting?
Very frustrating, any help would be appreciated!
I would avoid leaving and re-entering insert mode for this via :help :map-expr:
inoremap <expr> <leader>; ';' . (IsEOL() ? '<esc>:OmniSharpCodeFormat<cr>A<cr>' : '')
For this to work, you need to change the comparison in the IsEOL() function:
fun! IsEOL()
" col number == length of current line?
return col('.') > strlen(getline(line('.'))) " or just getline('.')
endf
This also fixes the indent problem.

Vim: how to make a macro/command that will wait for a key and use it?

For example, I want to temporarily map to fxsj. That is, when I press q, VIM will perform fxsqj. When I press k, VIM will perform fxskj. And so on.
You can use getchar(), for example:
nnoremap <F2> :call Fun()<CR>
function! Fun()
let c = nr2char(getchar())
if c=='q' || c=='k'
exec 'normal fxs'.c.'j'
endif
endfunction

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