6

In the command-line mode or in a vimscript I can use the following to duplicate a line:

normal! Yp 

Now let's says that I map my leader key to do that: nnoremap <Leader>a Yp. If I use this key combination the following command will interpret litterally the characters or <leader>:

normal! <Leader>a 

How can I use a key combination including <leader> in a normal command?

Note that other keys like <Right> or <F3> doesn't work neither.

Edit Some side notes:

  • Using normal or normal! doesn't seem to change something to the problem
  • The point of the question is to be able to use <Leader> and not replace it by its value for two 2 reasons:
    • It is more portable if I use it in a script and want to distribute it.
    • My leader is <Space> so even if I replace <Leader> by its value I still have the same problem. (Note that I could maybe escape a space character but that is not the point of the question)
4
  • 1
    Since you want your map to be used, you will need normal instead of normal!. My leader is \​, and I can use normal \a to execute the mapping, but I don't know how to keep it generic wrt leader. Commented May 13, 2016 at 8:13
  • @PhilippFrank: Thanks for your comment, please see my update: Replacing <leader> by its value is not really what I'm looking for. Commented May 13, 2016 at 8:20
  • Do you mind using and external command/function for this? Commented May 13, 2016 at 8:20
  • @Nobe4: I'd like a solution as straightforward as possible but if it is the only solution that will be ok :-) Commented May 13, 2016 at 8:27

3 Answers 3

6

I made up a solution to extract the leader, handle the special <space> case and execute the normal command.

The space is a special case, because the normal command does not accept expressions starting with a space:

:h normal {commands} cannot start with a space. Put a count of 1 (one) before it, "1 " is one space. 

Here is the function and command:

function! ExecuteLeader(suffix) let l:leader = get(g:,"mapleader","\\") if l:leader == ' ' let l:leader = '1' . l:leader endif execute "normal ".l:leader.a:suffix endfunction command! -nargs=1 NormLead call ExecuteLeader(<f-args>) 

You can use it this way:

:NormLead a -> will execute `normal <leader>a` 

(Thanks @statox for the update): If you want a one-liner command, without function, you can use the following:

command! -nargs=1 NormLead let leader=get(g:,"mapleader","\\") | exec "normal " . (leader==' '?"1":leader).<f-args> 

This is just for the leader key, but if you want to extend to any kind of key I think you need to use execute anyway.

In the normal help:

An alternative is to use :execute, which uses a expression as argument. This allows the use of printable characters to represent special characters. Example: :exe "normal \<c-w>\<c-w>" {not in Vi, of course} 
6
  • 1
    Thanks for your solution! I made it a one-liner to avoid the use of the function: command! -nargs=1 NormLead execute "normal " . (get(g:,"mapleader","\\")==' ' ? "1" : get(g:,"mapleader","\\")) . <f-args>: it is still not perfect (doesn't work with special keys or with combinations beginning with 2 leaders) but that's something Commented May 13, 2016 at 9:19
  • 1
    Nice, I'll add this to the answer Commented May 13, 2016 at 9:20
  • @statox, in my vimrc I have let mapleader = "\<Space>" and nnoremap <leader>p :pwd<CR>; I've added the command! from your comment (or the one from the answer), and when I open Vim and try :NormLead p, I just get the effect of putting. Any clues? Commented Aug 26, 2023 at 6:17
  • 1
    @Enlico I don't really have a clue no but you should definitely ask that in a new question (don't forget to include all the relevant code from your config in case it's a simple typo) Commented Aug 29, 2023 at 7:19
  • 1
    @statox, I asked in a comment first just to make sure I was not making some mistake which is obvious to you or the answerer. Now that I know that's not the case, I'll ask a question. Thanks ;) Commented Aug 29, 2023 at 8:32
1

The fundamental problem, here, is that "leader mappings" are not special mappings in any way. The <leader> in nnoremap <Leader>a Yp is expanded to whatever :help mapleader is set to at the time of definition:

:let mapleader = "," :nnoremap <Leader>a Yp :filter Yp nmap n ,a * Yp 

If the value of mapleader changes after a "leader mapping" has been defined, then the mapping is not changed:

:let mapleader = "\<Space>" :filter Yp nmap n ,a * Yp 

It is quite easy to end up with a silly mess, even:

:nnoremap <Leader>a Yp :filter Yp nmap n <Space>a * Yp n ,a * Yp 

Said another way, <Leader> is not consumed at runtime but at "source-time". Vim discards that <Leader> string as soon as it defines the mapping, so there is no way to tell if a mapping was defined with <Leader>, and no way to recall it with <Leader> either. This:

:let mapleader = "," :nnoremap <Leader>a Yp 

and this:

:nnoremap ,a Yp 

have exactly the same outcome: a mapping on ,a.

What you think of as a "leader mapping" when you edit your vimrc loses any trace of "leader-osity" the moment the line is executed. So, it is actually quite easy to use the current value of mapleader in a :normal command:

:normal <C-r>=get(g:, 'mapleader', '\')<CR>a 

or, if you feel confident/lazy:

:normal <C-r>=mapleader<CR>a 

But what ends up being executed is <CurrentLeader>a, which may or may not be the same as <LeaderAtDefinitionTime>a.

The value of mapleader at the time the mapping was defined is definitely lost. All you have is a ,a mapping so the snippet above can only really be used if the the current value of mapleader is the same as when the mapping was defined, , in this case.

If it is not, then you are at a dead end.

1
  • I blame the first wave of Vim zealots for the widespread misunderstanding of "leader mappings", with their ready-made vimrcs and hyperbolic blog posts. Commented Nov 18 at 18:09
0

call feedkeys(l:leader . a:suffix, 'm') works in Vim 9.1 (Included patches: 1-818).

Whole code for Vim and NeoVim:

command! -nargs=1 NormLead call Executeleader(<f-args>) function! Executeleader(suffix) " Get mapleader, default to backslash if not set let l:leader = get(g:,"mapleader","\\") " Special handling for space leader in Neovim if l:leader == ' ' && has('nvim') let l:leader = '1 ' endif " For Vim with space leader, l:leader remains ' ' (no conversion needed) " Debugging - uncomment if needed (may fail in Vim, works in Neovim) " echo "l:leader --> " . "'" . l:leader . "'" " echo "a:suffix --> " . "'" . a:suffix . "'" " sleep 1 " Execute the leader key sequence " The 'm' flag ensures the key sequence is executed immediately call feedkeys(l:leader . a:suffix, 'm') " Example: call feedkeys(' CFa', 'm') for space leader + CFa " Why this approach works: " - execute "normal \ CFa" fails in Vim (space interpreted as range separator) " - feedkeys() bypasses the range interpretation issue endfunction 

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.