¿Cómo activar la derecha de un mapa?

Oct 23 2020

Digamos que tengo una función como esta:

function! Foo()
  echom 'foo'
endfunction

Y un mapa como este:

nmap gf :<C-U>call Foo()<CR>0v$

Cuando escribo gf, veo un fooeco y la línea actual está resaltada. Hasta aquí todo bien.

Quiero poder ejecutar los rhs del mapa sin usar lhs. Dado que el rhs se procesa como si estuviera escrito, probé esto:

let rhs = maparg('gf', 'n')
execute 'normal!' rhs

Pero no parece hacer nada. También probé:

call feedkeys(rhs, 'xt')

Pero eso tampoco funcionó.

(Mi motivación es querer anular un mapeo estableciendo un nuevo rhs que llama a una función que puede volver a los rhs originales cuando sea necesario).

¿Cómo puedo invocar los rhs originales?

¡Gracias!

Respuestas

user938271 Oct 23 2020 at 01:53

(Mi motivación es querer anular un mapeo estableciendo un nuevo rhs que llama a una función que puede volver a los rhs originales cuando sea necesario).

Primero, guarde toda la información sobre el mapeo original a través de maparg(). Para hacerlo, debe pasar los argumentos booleanos opcionales {abbr}y {dict}:

let s:gf_mapsave = maparg('gf', 'n', v:false, v:true)

Luego, en su nuevo mapeo, que anula el original, haga lo que quiera. Y cuando necesite invocar el mapeo original, use mapset()para restaurarlo temporalmente:

call mapset('n', v:false, s:gf_mapsave)

Alimente el lhs en el búfer typeahead:

call feedkeys('gf', 'xt')

Y restaure su mapeo principal:

nno gf :call Override()<cr>

Como ejemplo:

fu Foo()
    echom 'foo'
endfu

nno gf :<c-u>call Foo()<cr>
let s:gf_mapsave = maparg('gf', 'n', v:false, v:true)

nno gf :call Override()<cr>
fu Override()
    try
        echom 'working'
        not_working
    " fall back
    catch
        call mapset('n', v:false, s:gf_mapsave)
        call feedkeys('gf', 'xt')
        nno gf :call Override()<cr>
    endtry
endfu

Esto requiere el parche 8.2.0807 .

Para obtener más información, consulte:

  • : h maparg ()
  • : h mapset ()
LucHermitte Oct 23 2020 at 02:35

En un complemento, tengo una lh#bracket#enrich_imap()función que uso para ejecutar alguna acción si se determina que una condición es verdadera y, de lo contrario, desencadenar el mapeo anterior / comportamiento esperado predeterminado.

Por ejemplo, lo uso de la siguiente manera:

call lh#brackets#enrich_imap('<cr>',
        \ {'condition': 'getline(".")[col(".")-2:col(".")-1]=="{}"',
        \   'action': 'lh#brackets#_add_newline_between_brackets()'},
        \ 0
        \ )

Internamente, utilizo maparg()para buscar el mapeo anterior, si lo hubiera. Luego, me ocupo de algunas funciones portátiles antiguas para manejar correctamente las llamadas a funciones locales de scripts y para construir la asignación adecuada.

Las funciones principales son

" from lh-vim-lib
function! lh#mapping#_build_rhs(mapping_definition) abort
  call lh#assert#value(a:mapping_definition)
        \.has_key('rhs')
  " Inject the right SNR instead of "<sid>"
  let rhs = substitute(a:mapping_definition.rhs, '\c<SID>', "\<SNR>".get(a:mapping_definition, 'sid', 'SID_EXPECTED').'_', 'g')
  return rhs
endfunction

function! lh#mapping#_switch(trigger, cases) abort
  return lh#mapping#_switch_int(a:trigger, a:cases)
endfunction

function! lh#mapping#_switch_int(trigger, cases) abort
  for c in a:cases
    if eval(c.condition)
      return eval(c.action)
    endif
  endfor
  return lh#mapping#reinterpret_escaped_char(eval(a:trigger))
endfunction

function! lh#mapping#reinterpret_escaped_char(seq) abort
  let seq = escape(a:seq, '"\')
  exe 'return "' .
        \   substitute( seq, '\\\\<\(.\{-}\)\\\\>', '"."\\<\1>"."', 'g' ) .  '"'
endfunction

-------

" from lh-brackets
function! lh#brackets#enrich_imap(trigger, case, isLocal, ...) abort
  " - Some keys, like '<bs>', cannot be used to code the default.
  " - Double "string(" because those chars are correctly interpreted with
  " lh#mapping#reinterpret_escaped_char(eval()), which requires nested strings...
  if a:0 == 0
    let previous = maparg(a:trigger, 'i', 0, 1)
    if !empty(previous) && previous.expr
      " If not an expression, I do know yet how to forward a non expr mapping
      " from an expr mapping definition
      let default = lh#mapping#_build_rhs(previous)
    else
      let default = string(a:trigger)
    endif
  else
    let default = string(a:1)
  endif
  let sCase='lh#mapping#_switch('.string(default).', '.string([a:case]).')'
  call s:DefineImap(a:trigger, sCase, a:isLocal)
endfunction

function! s:DefineImap(trigger, inserter, isLocal) abort
  if exists('*IMAP') && a:trigger !~? '<bs>\|<cr>\|<up>\|<down>\|<left>\|<right>'
    if a:isLocal
      call IMAP(a:trigger,  "\<c-r>=".a:inserter."\<cr>", &ft)
    else
      call IMAP(a:trigger,  "\<c-r>=".a:inserter."\<cr>", '')
    endif
  else
    call s:DefineMap('inore', a:trigger, (a:inserter), a:isLocal, 1)
  endif
endfunction

function! s:DefineMap(mode, trigger, action, isLocal, isExpr) abort
  " The actual definition does much more as mappings from the plugin can be activated or deactivated
  let crt_mapping = {}
  let crt_mapping.lhs     = escape(a:trigger, '|') " need to escape bar
  let crt_mapping.mode    = a:mode
  let crt_mapping.rhs     = a:action
  let crt_mapping.buffer  = a:isLocal ? '<buffer> ' : ''
  let crt_mapping.expr    = a:isExpr  ? '<expr> '   : ''
  call s:Map(crt_mapping)
endfunction

function! s:Map(m) abort
  " Nowadays there exists a vimscript function that does the same; we no longer need :exe
  let cmd = a:m.mode.'map <silent> ' . a:m.expr . a:m.buffer . a:m.lhs .' '.a:m.rhs
  call s:Verbose(cmd)
  exe cmd
endfunction