Você poderia usar a maparg()
função
Para testar se o usuário mapeou algo <C-c>
no modo normal, você deve escrever:
if !empty(maparg('<C-c>', 'n'))
Se o usuário mapeou algo, para armazenar o {rhs}
em uma variável, você deve escrever:
let rhs_save = maparg('<C-c>', 'n')
Se você quiser obter mais informações sobre o mapeamento, como:
- é silencioso (
<silent>
argumento)?
- é local para o buffer atual (
<buffer>
argumento)?
- é a
{rhs}
avaliação de uma expressão ( <expr>
argumento)?
- remapear o
{rhs}
( nnoremap
vs nmap
)?
- se o usuário tiver outro mapeamento que comece com
<C-c>
, o Vim espera que mais caracteres sejam digitados ( <nowait>
argumento)?
- ...
Então, você poderia dar um terceiro e um quarto argumento: 0
e 1
.
0
porque você está procurando um mapeamento e não uma abreviação e 1
porque deseja um dicionário com o máximo de informações e não apenas o {rhs}
valor:
let map_save = maparg('<C-c>', 'n', 0, 1)
Supondo que o usuário não tenha usado nenhum argumento especial em seu mapeamento e que não remapeie o {rhs}
, para restaurá-lo, você pode simplesmente escrever:
let rhs_save = maparg('<C-c>', 'n')
" do some stuff which changes the mapping
exe 'nnoremap <C-c> ' . rhs_save
Ou, para ter certeza e restaurar todos os argumentos possíveis:
let map_save = maparg('<C-c>', 'n', 0, 1)
" do some stuff which changes the mapping
exe (map_save.noremap ? 'nnoremap' : 'nmap') .
\ (map_save.buffer ? ' <buffer> ' : '') .
\ (map_save.expr ? ' <expr> ' : '') .
\ (map_save.nowait ? ' <nowait> ' : '') .
\ (map_save.silent ? ' <silent> ' : '') .
\ ' <C-c> ' .
\ map_save.rhs
Edit: Desculpe, acabei de perceber que não funcionaria conforme o esperado se o usuário chamar uma função local de script no {rhs}
mapeamento.
Suponha que o usuário tenha o seguinte mapeamento dentro dele vimrc
:
nnoremap <C-c> :<C-U>call <SID>FuncA()<CR>
function! s:FuncA()
echo 'hello world!'
endfunction
Quando ele bate <C-c>
, ele exibe a mensagem hello world!
.
E no seu plugin, você salva um dicionário com todas as informações e altera temporariamente o mapeamento dessa maneira:
let map_save = maparg('<C-c>', 'n', 0, 1)
nnoremap <C-c> :<C-U>call <SID>FuncB()<CR>
function! s:FuncB()
echo 'bye all!'
endfunction
Agora, ele será exibido bye all!
. Seu plug-in faz algum trabalho e, quando termina, tenta restaurar o mapeamento com o comando anterior.
Provavelmente falhará com uma mensagem assim:
E117: Unknown function: <SNR>61_FuncA
61
é apenas o identificador do script no qual seu comando de mapeamento seria executado. Pode ser qualquer outro número. Se o seu plug-in for o 42º arquivo originado no sistema do usuário, será 42
.
Dentro de um script, quando um comando de mapeamento é executado, o Vim converte automaticamente a notação <SID>
no código de chave especial <SNR>
, seguido por um número exclusivo para o script e um sublinhado. É necessário fazer isso, porque quando o usuário clicar <C-c>
, o mapeamento será executado fora do script e, portanto, não saberá em qual script FuncA()
está definido.
O problema é que o mapeamento original foi originado em um script diferente do seu plug-in, portanto, aqui a tradução automática está incorreta. Ele usa o identificador do seu script, enquanto deve usar o identificador do usuário vimrc
.
Mas você poderia fazer a tradução manualmente. O dicionário map_save
contém uma chave chamada 'sid'
cujo valor é o identificador correto.
Portanto, para tornar o comando de restauração anterior mais robusto, você pode substituir map_save.rhs
por:
substitute(map_save.rhs, '<SID>', '<SNR>' . map_save.sid . '_', 'g')
Se o {rhs}
mapeamento original contiver <SID>
, ele deverá ser traduzido corretamente. Caso contrário, nada deve ser alterado.
E se você quiser reduzir um pouco o código, poderá substituir as 4 linhas que cuidam dos argumentos especiais por:
join(map(['buffer', 'expr', 'nowait', 'silent'], 'map_save[v:val] ? "<" . v:val . ">": ""'))
A map()
função deve converter cada item da lista ['buffer', 'expr', 'nowait', 'silent']
no argumento de mapeamento correspondente, mas apenas se sua chave dentro map_save
for diferente de zero. E join()
deve juntar todos os itens em uma string.
Portanto, uma maneira mais robusta de salvar e restaurar o mapeamento pode ser:
let map_save = maparg('<C-c>', 'n', 0, 1)
" do some stuff which changes the mapping
exe (map_save.noremap ? 'nnoremap' : 'nmap') .
\ join(map(['buffer', 'expr', 'nowait', 'silent'], 'map_save[v:val] ? "<" . v:val . ">": ""')) .
\ map_save.lhs . ' ' .
\ substitute(map_save.rhs, '<SID>', '<SNR>' . map_save.sid . '_', 'g')
Edit2:
Estou enfrentando o mesmo problema que você, como salvar e restaurar um mapeamento em um plug-in de desenho. E acho que encontrei dois problemas que a resposta inicial não viu no momento em que escrevi, desculpe por isso.
Primeiro problema, suponha que o usuário use <C-c>
em um mapeamento global, mas também em um mapeamento local de buffer. Exemplo:
nnoremap <C-c> :echo 'global mapping'<CR>
nnoremap <buffer> <C-c> :echo 'local mapping'<CR>
Nesse caso, maparg()
dará prioridade ao mapeamento local:
:echo maparg('<C-c>', 'n', 0, 1)
---> {'silent': 0, 'noremap': 1, 'lhs': '<C-C>', 'mode': 'n', 'nowait': 0, 'expr': 0, 'sid': 7, 'rhs': ':echo ''local mapping''<CR>', 'buffer': 1}
O que é confirmado em :h maparg()
:
The mappings local to the current buffer are checked first,
then the global mappings.
Mas talvez você não esteja interessado no mapeamento local do buffer, talvez queira o global.
A única maneira de encontrar, de maneira confiável, as informações sobre o mapeamento global, é tentar remover temporariamente o mapeamento de um mapeamento potencial, sombreado e local do buffer usando a mesma chave.
Isso pode ser feito em 4 etapas:
- salve um mapeamento local de buffer (potencial) usando a chave
<C-c>
- execute
:silent! nunmap <buffer> <C-c>
para excluir um mapeamento local de buffer (potencial)
- salve o mapeamento global (
maparg('<C-c>', 'n', 0, 1)
)
- restaurar o mapeamento local do buffer
A segunda questão é a seguinte. Suponha que o usuário não mapeou nada para <C-c>
, então a saída de maparg()
será um dicionário vazio. E, neste caso, o processo de restauração não consiste na instalação de um mapeamento ( :nnoremap
), mas na destruição de um mapeamento ( :nunmap
).
Para tentar resolver esses 2 novos problemas, você pode tentar esta função para salvar mapeamentos:
fu! Save_mappings(keys, mode, global) abort
let mappings = {}
if a:global
for l:key in a:keys
let buf_local_map = maparg(l:key, a:mode, 0, 1)
sil! exe a:mode.'unmap <buffer> '.l:key
let map_info = maparg(l:key, a:mode, 0, 1)
let mappings[l:key] = !empty(map_info)
\ ? map_info
\ : {
\ 'unmapped' : 1,
\ 'buffer' : 0,
\ 'lhs' : l:key,
\ 'mode' : a:mode,
\ }
call Restore_mappings({l:key : buf_local_map})
endfor
else
for l:key in a:keys
let map_info = maparg(l:key, a:mode, 0, 1)
let mappings[l:key] = !empty(map_info)
\ ? map_info
\ : {
\ 'unmapped' : 1,
\ 'buffer' : 1,
\ 'lhs' : l:key,
\ 'mode' : a:mode,
\ }
endfor
endif
return mappings
endfu
... e este para restaurá-los:
fu! Restore_mappings(mappings) abort
for mapping in values(a:mappings)
if !has_key(mapping, 'unmapped') && !empty(mapping)
exe mapping.mode
\ . (mapping.noremap ? 'noremap ' : 'map ')
\ . (mapping.buffer ? ' <buffer> ' : '')
\ . (mapping.expr ? ' <expr> ' : '')
\ . (mapping.nowait ? ' <nowait> ' : '')
\ . (mapping.silent ? ' <silent> ' : '')
\ . mapping.lhs
\ . ' '
\ . substitute(mapping.rhs, '<SID>', '<SNR>'.mapping.sid.'_', 'g')
elseif has_key(mapping, 'unmapped')
sil! exe mapping.mode.'unmap '
\ .(mapping.buffer ? ' <buffer> ' : '')
\ . mapping.lhs
endif
endfor
endfu
A Save_mappings()
função pode ser usada para salvar mapeamentos.
Espera 3 argumentos:
- uma lista de chaves; exemplo:
['<C-a>', '<C-b>', '<C-c>']
- um modo; exemplo:
n
para o modo normal ou x
visual
- uma bandeira booleana; se for
1
, significa que você está interessado em mapeamentos globais e, se for 0
, em locais
Com ele, você pode salvar os mapeamentos globais usando as teclas C-a
, C-b
e C-c
, no modo normal, dentro de um dicionário:
let your_saved_mappings = Save_mappings(['<C-a>', '<C-b>', '<C-c>'], 'n', 1)
Depois, quando desejar restaurar os mapeamentos, você pode chamar Restore_mappings()
, passando o dicionário que contém todas as informações como argumento:
call Restore_mappings(your_saved_mappings)
Pode haver um terceiro problema ao salvar / restaurar mapeamentos de buffer-local. Porque, entre o momento em que salvamos os mapeamentos e o momento em que tentamos restaurá-los, o buffer atual pode ter sido alterado.
Nesse caso, talvez a Save_mappings()
função possa ser aprimorada salvando o número do buffer atual ( bufnr('%')
).
E então, Restore_mappings()
usaria essas informações para restaurar os mapeamentos locais do buffer no buffer direito. Provavelmente poderíamos usar o:bufdo
comando, prefixar o último com uma contagem (correspondendo ao número do buffer salvo anteriormente) e sufixá-lo com o comando mapping.
Talvez algo como:
:{original buffer number}bufdo {mapping command}
Teríamos que verificar primeiro se o buffer ainda existe, usando a bufexists()
função, porque poderia ter sido excluído nesse meio tempo.