Bir eşleme nasıl kaydedilir ve geri yüklenir?


13

Vim için bir eklenti geliştiriyorum ve sadece "eklentinin yürütülmesi" sırasında kullanılabilecek bir eşleme tanımlamak istiyorum.

Şimdiye kadar eklentinin (basitleştirilmiş) iş akışı şöyledir:

  1. Kullanıcı eklentinin bir komutunu çağırır
  2. Komut, tedavi öncesi işlevi çağırır:

    function! s:PreTreatmentFunction(function, ...)
        " Do some pretreatment stuff
    
        " Create a mapping to call the TearDown
        nnoremap <C-c> :call TeardDown()<CR>
    
        " Call a function depending on the parameter passed to this one
        if function == "foo"
            call Foo()
        else
            call Bar()
        endif
    endfunction
    
  3. Tamponun durumunu değiştiren ( Foo()veya Bar()önceki işlevin son satırlarında) başka bir işlev çağrılır

  4. Kullanıcı yırtma işlevini çağırmak için eşlemeyi kullanır
  5. Yırtma işlevi oluşturulan eşlemeyi kaldırır:

    function! s:TearDown()
        " Do some tear down stuff
    
        " Remove the mapping
        unmap <C-c>
    endfunction
    

Eşlememi işleme biçimimden memnun değilim: Kullanıcı zaten başka bir şeye eşlediyse orijinal eşlemesini kaybedecektir.

Benim sorum şudur: Eşlenenleri (eşlenmişse) nasıl kaydedebilir <C-c>ve yırtma fonksiyonumda nasıl geri yükleyebilirim? Bunu yapmak için yerleşik bir özellik var mı? grepSonuç ing ing hakkında :nmap <C-c>ama bu gerçekten "temiz" hissetmiyorum.

Birkaç yan not:

  • LearnVimScriptTheHardWay bu konuda bir bölüm var biliyorum , ama burada mümkün olmayan bir ftplugin kullanmak söylüyorlar: eklenti bir dosya türüne bağlı değildir
  • Kullanıcının hangi tuşları kullanacağını seçmesine izin vermek için bir değişken oluşturabilirim: Muhtemelen ne yapacağım ama esas olarak kaydetme ve geri yükleme ile ilgileniyorum.
  • Yerel bir lider kullanabilirdim ama bence biraz abartılı ve hala esas olarak kaydetme ve geri yükleme şeyini merak ediyorum.

Yanıtlar:


25

maparg()İşlevi kullanabilirsiniz .

Kullanıcının bir şeyi <C-c>normal modda eşleyip eşlemediğini test etmek için şunu yazarsınız:

if !empty(maparg('<C-c>', 'n'))

Kullanıcı bir şeyi eşlediyse, {rhs}değişkeninde saklamak için şunu yazarsınız:

let rhs_save = maparg('<C-c>', 'n')

Eşleme hakkında daha fazla bilgi istiyorsanız, örneğin:

  • sessiz <silent>mi ( tartışma)?
  • geçerli arabellek ( <buffer>bağımsız değişken) için yerel mi?
  • olan {rhs}bir ifade (değerlendirilmesi <expr>bağımsız değişkeni)?
  • {rhs}( nnoremapvs nmap) 'yi yeniden hatırlıyor mu?
  • kullanıcı ile başlayan başka bir eşleme varsa, <C-c>Vim daha fazla karakterin yazılmasını bekler <nowait>mi ( bağımsız değişken)?
  • ...

Sonra, üçüncü ve dördüncü bir argüman verebilirsin: 0ve 1.
0çünkü bir kısaltma değil bir eşleme arıyorsunuz 1ve yalnızca {rhs}değeri değil, maksimum bilgi içeren bir sözlük istediğiniz için :

let map_save = maparg('<C-c>', 'n', 0, 1)

Kullanıcının eşlemesinde özel bir argüman kullanmadığını ve {rhs}geri yüklemek için yeniden eşleme yapmadığını varsayarsak , şunları yazabilirsiniz:

let rhs_save = maparg('<C-c>', 'n')

" do some stuff which changes the mapping

exe 'nnoremap <C-c> ' . rhs_save

Veya olası tüm argümanlardan emin olmak ve geri yüklemek için:

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

Düzenleme: Üzgünüm, kullanıcı {rhs}eşleme bir komut dosyası yerel işlevi çağırırsa beklendiği gibi işe yaramayacağını fark ettim .

Kullanıcının kendi içinde aşağıdaki eşleştirmeye sahip olduğunu varsayalım vimrc:

nnoremap <C-c> :<C-U>call <SID>FuncA()<CR>

function! s:FuncA()
    echo 'hello world!'
endfunction

Vurulduğunda <C-c>mesajı görüntüler hello world!.

Eklentinizde, tüm bilgileri içeren bir sözlüğü kaydedersiniz, ardından eşlemesini geçici olarak şu şekilde değiştirirsiniz:

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

Şimdi görüntülenecektir bye all!. Eklentiniz biraz işe yarıyor ve bittiğinde eşlemeyi önceki komutla geri yüklemeye çalışıyor.

Muhtemelen şöyle bir mesajla başarısız olacaktır:

E117: Unknown function: <SNR>61_FuncA

61yalnızca eşleme komutunuzun yürütüleceği komut dosyasının tanımlayıcısıdır. Başka bir sayı olabilir. Eklentiniz kullanıcının sistemine ait 42. dosya ise, olacaktır 42.

Bir komut dosyasının içinde, bir eşleme komutu yürütüldüğünde, Vim gösterimi otomatik olarak <SID>özel anahtar koduna <SNR>, ardından komut dosyası için benzersiz bir sayıya ve bir alt çizgiye dönüştürür. Bunu yapmak zorundadır, çünkü kullanıcı vurduğunda <C-c>, eşleme komut dosyasının dışında yürütülür ve böylece hangi komut dosyasının FuncA()tanımlandığını bilemez .

Sorun, orijinal eşlemenin eklentinizden farklı bir komut dosyasında kaynaklanmış olması, bu yüzden burada otomatik çeviri yanlış. Komut dosyanızın tanımlayıcısını kullanırken, kullanıcının tanımlayıcısını kullanmalıdır vimrc.

Ancak çeviriyi elle yapabilirsiniz. Sözlük , değeri doğru tanımlayıcı olan map_savebir anahtar içeriyor 'sid'.
Dolayısıyla, önceki geri yükleme komutunu daha sağlam hale getirmek için aşağıdakilerle değiştirebilirsiniz map_save.rhs:

substitute(map_save.rhs, '<SID>', '<SNR>' . map_save.sid . '_', 'g')

{rhs}Orijinal eşlemeden biri varsa, <SID>düzgün bir şekilde çevrilmelidir. Aksi takdirde hiçbir şey değiştirilmemelidir.

Kodu biraz kısaltmak isterseniz, özel argümanlarla ilgilenen 4 satırı şu şekilde değiştirebilirsiniz:

join(map(['buffer', 'expr', 'nowait', 'silent'], 'map_save[v:val] ? "<" . v:val . ">": ""'))

map()Fonksiyon listeden her bir öğeyi dönüştürmek gerekir ['buffer', 'expr', 'nowait', 'silent']tekabül haritalama tartışma içine ama onun anahtar içeride sadece map_savesıfırdan farklıdır. Ve join()tüm öğeleri bir dizeye birleştirmelisiniz.

Bu nedenle, eşlemeyi kaydetmek ve geri yüklemek için daha sağlam bir yol olabilir:

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:

Sizinle aynı sorunla karşılaşıyorum, bir çizim eklentisindeki bir eşlemeyi nasıl kaydedebilir ve geri yükleyebilirim. Sanırım ilk cevabın yazdığım sırada görmediği 2 konu buldum, bunun için üzgünüm.

İlk sorun, kullanıcının <C-c>global bir haritalamada ve aynı zamanda bir tampon-yerel haritalamada kullandığını varsayalım . Misal:

nnoremap          <C-c>    :echo 'global mapping'<CR>
nnoremap <buffer> <C-c>    :echo 'local  mapping'<CR>

Bu durumda, maparg()yerel eşlemeye öncelik verir:

: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}

Hangi teyit edilir :h maparg():

    The mappings local to the current buffer are checked first,
    then the global mappings.

Ama belki arabellek-yerel haritalama ile ilgilenmiyorsunuzdur, belki de global olanı istiyorsunuz.
Global haritalama hakkında güvenilir bir şekilde bilgi edinmenin tek yolu, aynı anahtarı kullanarak potansiyel, gölgelenme, arabellek-yerel haritalamanın geçici olarak eşlemesini kaldırmaya çalışmaktır.

4 adımda yapılabilir:

  1. tuşunu kullanarak (potansiyel) bir arabellek yerel eşlemesini kaydetme <C-c>
  2. :silent! nunmap <buffer> <C-c>(potansiyel) arabellek-yerel eşlemeyi silmek için yürütme
  3. genel eşlemeyi kaydet ( maparg('<C-c>', 'n', 0, 1))
  4. arabellek yerel eşlemesini geri yükle

İkinci konu şudur. Kullanıcının hiçbir şeyi eşlemediğini varsayalım <C-c>, o zaman çıktısı maparg()boş bir sözlük olacaktır. Ve bu durumda, geri yükleme işlemi bir eşlemenin ( :nnoremap) kurulumundan değil, bir eşlemenin ( ) imha edilmesinden ibarettir :nunmap.

Bu 2 yeni sorunu çözmeye çalışmak için, eşlemeleri kaydetmek üzere bu işlevi deneyebilirsiniz:

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

... ve bunları geri yüklemek için:

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

Save_mappings()Fonksiyon eşleştirmeleri kaydetmek için kullanılabilir.
3 argüman bekliyor:

  1. bir anahtar listesi; misal:['<C-a>', '<C-b>', '<C-c>']
  2. bir mod; örnek: nnormal mod veya xgörsel mod için
  3. boole bayrağı; eğer öyleyse 1, bu, küresel eşlemelerle ilgilendiğiniz anlamına gelir ve 0yereldir.

Bununla beraber, tuşlarını kullanarak küresel eşleşmelerini kurtarabilecek C-a, C-bve C-csözlükten içinde normal modda,:

let your_saved_mappings = Save_mappings(['<C-a>', '<C-b>', '<C-c>'], 'n', 1)

Daha sonra, eşlemeleri geri yüklemek istediğinizde Restore_mappings(), tüm bilgileri içeren sözlüğü bağımsız değişken olarak ileterek arayabilirsiniz :

call Restore_mappings(your_saved_mappings)

Arabellek-yerel eşlemeleri kaydederken / geri yüklerken 3. bir sorun olabilir. Çünkü, eşlemeleri kaydettiğimiz an ile bunları geri yüklemeye çalıştığımız an arasında, geçerli arabellek değişmiş olabilir.

Bu durumda, Save_mappings()işlev geçerli arabellek ( bufnr('%')) sayısı kaydedilerek geliştirilebilir .

Ve sonra, Restore_mappings()sağ tampondaki arabellek-yerel eşlemelerini geri yüklemek için bu bilgileri kullanır. Muhtemelen :bufdokomutu kullanabilir, ikincisine bir sayı ekleyebilir (daha önce kaydedilen arabellek numarasıyla eşleşebilir) ve eşleme komutuyla ekleyebiliriz.

Belki şöyle bir şey:

:{original buffer number}bufdo {mapping command}

İlk önce arabellek hala var olup olmadığını kontrol etmeliyiz bufexists(), çünkü bu arada, silinebilirdi.


İnanılmaz tam da ihtiyacım olan şey bu. Teşekkürler!
statox

2

Eklentilerimde, geçici eşlemelerim olduğunda, bunlar her zaman yereldir - küresel eşlemeleri kaydetmek veya bunları içeren karmaşık bir şey hakkında gerçekten umurumda değil. Bu yüzdenlh#on#exit().restore_buffer_mapping() yardımcı fonksiyonum - lh-vim-lib'den .

Sonunda ne olur:

" excerpt from autoload/lh/on.vim
function! s:restore_buffer_mapping(key, mode) dict abort " {{{4
  let keybinding = maparg(a:key, a:mode, 0, 1)
  if get(keybinding, 'buffer', 0)
    let self.actions += [ 'silent! call lh#mapping#define('.string(keybinding).')']
  else
    let self.actions += [ 'silent! '.a:mode.'unmap <buffer> '.a:key ]
  endif
  return self
endfunction

" The action will be executed later on with:
" # finalizer methods {{{2
function! s:finalize() dict " {{{4
  " This function shall not fail!
  for l:Action in self.actions
    try
      if type(l:Action) == type(function('has'))
        call l:Action()
      elseif !empty(l:Action)
        exe l:Action
      endif
    catch /.*/
      call lh#log#this('Error occured when running action (%1)', l:Action)
      call lh#log#exception()
    finally
      unlet l:Action
    endtry
  endfor
endfunction


" excerpt from autoload/lh/mapping.vim
" Function: lh#mapping#_build_command(mapping_definition) {{{2
" @param mapping_definition is a dictionary witch the same keys than the ones
" filled by maparg()
function! lh#mapping#_build_command(mapping_definition)
  let cmd = a:mapping_definition.mode
  if has_key(a:mapping_definition, 'noremap') && a:mapping_definition.noremap
    let cmd .= 'nore'
  endif
  let cmd .= 'map'
  let specifiers = ['silent', 'expr', 'buffer']
  for specifier in specifiers
    if has_key(a:mapping_definition, specifier) && a:mapping_definition[specifier]
      let cmd .= ' <'.specifier.'>'
    endif
  endfor
  let cmd .= ' '.(a:mapping_definition.lhs)
  let rhs = substitute(a:mapping_definition.rhs, '<SID>', "\<SNR>".(a:mapping_definition.sid).'_', 'g')
  let cmd .= ' '.rhs
  return cmd
endfunction
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.