Function arguments not evaluating - vim

I'm trying to set up a couple of maps to quickly go through merge conflicts. Here's my code:
func! DiffAccept(w)
diffget a:w
diffupdate
normal ]c
endfunc
noremap dh :exec DiffAccept("//2")<CR>
noremap dl :exec DiffAccept("//3")<CR>
Every time I try to use this I get "No matching buffer for a:w". I'm clearly using this variable wrong, but it acts as expected when I change the line to "echo a:w".

Vim's evaluation rules are different than most programming languages. You need to use :execute in order to evaluate the (function argument) variable; otherwise, it's taken literally (as a buffer name):
execute 'diffget' a:w
PS: Prefer using :normal! (with !); this avoids interference from mappings.

Related

Is it possible to map to execute normal command?

The following command appears to invoke the desired function.
:execute "normal \<Plug>VimwikiAddHeaderLevel<CR>"
However, putting it inside a mapping seems to cause problems.
:nmap <buffer> = execute "normal \<Plug>VimwikiAddHeaderLevel<CR>"
Here's the output when I type =.
E114: Missing quote: "normal \<Plug>VimwikiAddHeaderLevel
E15: Invalid expression: "normal \<Plug>VimwikiAddHeaderLevel
Is there some special syntax that would allow me to perform this mapping?
You don't enter command-line mode (for :execute) from the normal mode mapping; the : is missing.
The <Plug> and <CR> are already evaluated by the mapping; the double quotes do not protect them. The <CR> submits the (incomplete, as Vim hasn't seen the trailing ") command-line, and that causes the E114.
After escaping < as <lt>, you still need another <CR> to conclude the :execute command.
:nmap <buffer> = :execute "normal \<lt>Plug>VimwikiAddHeaderLevel\<lt>CR>"<CR>
As I've noted in your other question, you need a :for loop if you really want to work around the plugin's failure to accept a count. Though it would be possible to do all that inline in the mapping's right-hand side, separating the loop into a separate :function is highly recommended, exactly to avoid such escaping issues. Inside a function, the plugin invocation is a simple
:execute "normal \<Plug>VimwikiAddHeaderLevel"

vim: Bind leader key to open .vimrc in a different path

I have my .vimrc in a different path, that I source from my main ~/.vimrc (so I can share same settings across Windows, bash on Windows, etc).
I'm trying to write something in the .vimrc in question, that would make a hotkey for editing said .vimrc, without hard coding the path.
What I currently have is this:
let g:vimrc_path = expand('<sfile>')
:map <Leader>v exec(":e " + g:vimrc_path + "<CR>")
But this doesn't seem to do anything. I've verified that g:vimrc_path is the right value, and that the <Leader>v ends up being called by subbing in echo messages, but I'm not wrapping my head around why the variable I'm trying to define doesn't get expanded correctly.
String concatenation is done with ., not with +, which performs coercion into numbers and addition. But :execute takes multiple arguments (which it space-separates), so you don't actually need this here.
You should use :noremap; it makes the mapping immune to remapping and recursion.
Also, I doubt you need visual and operator-pending modes (:help map-modes), so define this just for normal mode.
:exec[ute is an Ex command, so for a normal-mode mapping, you need to first enter command-line mode. So :exec 'edit' instead of exec ':edit'.
Also, this is not a function (though Vim 8 now also has execute()), so the parentheses are superfluous.
The <silent> avoids the printing of the whole command (you'll notice the loading of the vimrc file, anyway); it's optional.
The fnameescape() ensures that pathological path names are also handled; probably not necessary here.
let g:vimrc_path = expand('<sfile>')
nnoremap <silent> <Leader>v :execute 'edit' fnameescape(g:vimrc_path)<CR>
Alternative
As the script path is static, you can move the variable interpolation from runtime (mapping execution) to mapping definition, and get rid of the variable:
execute 'nnoremap <Leader>v :edit' fnameescape(expand('<sfile>')) . '<CR>'
Strings in vimscript are concatenated with ., not with +. For example:
:echo "Hello"." world!"
will echo
Hello world!
If you were to type
:echo "Hello" + " world!"
vim would echo
0
This is because the + operator is only for numbers, so vim attempts to cast these strings to numbers. If you were to run
:echo "3" + "1"
vim would echo "4".
So basically, you just need to change
:map <Leader>v exec(":e " + g:vimrc_path + "<CR>")
to
:map <Leader>v exec(":e ".g:vimrc_path."<CR>")
Another problem you might have not seen is that "<CR>" evaluates to the literal text "<CR>", so it only messes up your function. If you want a literal carriage return, you would need a backslash. However, you definitely do not want to do this! Seriously, try it out and see.
You can see the issue. It looks for a file that has a literal carriage return at the end of the filename! There is a very simple fix though. Remove the "\<cr>" completely. Since :exec runs ex commands by default, the carriage return (and the colon too for that matter) are unnecessary.
Also, as a nitpick,
The parenthesis are not needed for the "exec" function, and
Use nnoremap instead to avoid recursive mappings.
Taking all of this into consideration, I would simplify it to
:nnoremap <Leader>v :exec "e ".g:vimrc_path<cr>

Create VIM Keybinding executing two commands and ranges (count)

I am using Slimux and often use range modifiers to send code to a REPL. The keyboard shortcut ",s" sends a new line to the REPL, executes the commands () and advances one line (j),
map <Leader>s :SlimuxREPLSendLine<CR>j
If I use ranges, e.g. "5,s", this sends 5 lines of code to the REPL, but only advances one line (j) instead of 5j. I tried playing around with :exe and v.count1 to achieve this, but was not very successful.
Can anyone provide a code example (and explanation how this works)?
This should do the trick:
noremap <Leader>s :SlimuxREPLSendLine<Bar>execute 'normal!' v:count1 . 'j'<CR>
It depends on the :SlimuxREPLSendLine command having been defined with -bar to be able to append another command to it.
PS: You should use :noremap; it makes the mapping immune to remapping and recursion.

How to jump to a search in a mapped :normal command?

What do you need to properly jump to a matched search result?
To reproduce, make a macro with a search in it after you've run vim -u NONE to ensure there's no vimrc interfering. You'll need to make a file with at least 2 lines and put the cursor on the line without the text TEST_TEXT.
map x :norm gg/TEST_TEXT^MIthis
My intention is that when I press x, it goes to the top of the file, looks for TEST_TEXT and then puts this at the start of the line that matches the search. The ^M is a literal newline, achieved with the CtrlQ+Enter keypress. What's happening instead is either nothing happens, or the text gets entered on the same line as when I called the macro.
If I just run the :norm gg/TEST_TEXT^MIthis command without mapping it to a key, the command executes successfully.
I had an initially longer command involving a separate file and the tcomment plugin, but I've gotten it narrowed down to this.
What is the correct sequence of keys to pull this off once I've mapped it to a key?
The problem is that the ^M concludes the :normal Ex command, so your search command is aborted instead of executed. The Ithis is then executed outside of :normal.
In fact, you don't need :normal here at all. And, it's easier and more readable to use the special key notation with mappings:
:map x gg/TEST_TEXT<CR>Ithis
If you really wanted to use :normal, you'd have to wrap this in :execute, like this:
:map x :exe "norm gg/TEST_TEXT\<lt>CR>Ithis"<CR>
Bonus tips
You should use :noremap; it makes the mapping immune to remapping and recursion.
Better restrict the mapping to normal mode, as in its current form, it won't behave as expected in visual and operator-pending mode: :nnoremap
This clobbers the last search pattern and its highlighting. Use of lower-level functions like search() is recommended instead.
There are many ways of doing this however this is my preferred method:
nnoremap x :0/TEST_TEXT/norm! Itest<esc>
Explanation:
:{range}norm! {cmd} - execute normal commands, {cmd}, on a range of lines,{range}.
! on :normal means the commands will not be remapped.
The range 0/TEST_TEXT start before the first line and then finds the first matching line.
I have a few issues with your current mapping:
You are not specifying noremap. You usually want to use noremap
It would be best to specifiy a mode like normal mode, e.g. nnoremap
It is usually best to use <cr> notation with mappings
You are using :normal when your command is already in normal mode but not using any of the ex command features, e.g. a range.
For more help see:
:h :map
:h :norm
:h range
try this mapping:
nnoremap x gg/TEST_TEXT<cr>Ithis<esc>
note that, if you map x on this operation, you lost the original x feature.

Change behaviour of mapping in certain cases, and reuse functionality in all others

I'm trying to change the behavior of "K" in certain circumstances, but keep its original functionality in all the others. Here's my failed attempt
function! GetDocumentation()
if &filetype == 'java'
JavaDocSearch
else
normal K
endif
endfunction
nnoremap K :call GetDocumentation()<CR>
However since I use K in a function, when it's called as result of the remapping, the new mapping of K is used, and I get infinite recursion. I guess I could somehow fit the gist of the function onto the nnoremap line, but it seems awkward, and it would be nice to forcefully use the original mapping of a key.
Hope this makes sense,
Thanks.
The :normal command without the ! modifier—as it is used in the
function—executes its arguments as Normal mode commands taking mappings into
consideration. This is why the line
normal K
runs the mapping that has overridden the default handler. So, to ignore
mappings shadowing commands, change the line as follows,
normal! K
#ib. is right: you should be using :normal! here. But function is an overkill: the following expression mapping is sufficient:
nnoremap <expr> K ((&filetype is# "java")?(":JavaDocSearch\n"):("K"))
This mapping won’t trigger itself due to nore and also is eight lines shorter.

Resources