Eddie Parker
Eddie Parker

Reputation: 4888

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.

Upvotes: 1

Views: 378

Answers (2)

DJMcMayhem
DJMcMayhem

Reputation: 7679

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,

  1. The parenthesis are not needed for the "exec" function, and

  2. 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>

Upvotes: 0

Ingo Karkat
Ingo Karkat

Reputation: 172570

  • 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>'

Upvotes: 3

Related Questions