मैपिंग को कैसे बचाएं और पुनर्स्थापित करें?


12

मैं विम के लिए एक प्लगइन विकसित कर रहा हूं और मैं एक मैपिंग को परिभाषित करना चाहूंगा जो "प्लगइन के निष्पादन" के दौरान ही उपलब्ध होगा।

अब तक प्लगइन का (सरलीकृत) वर्कफ़्लो निम्नलिखित है:

  1. उपयोगकर्ता प्लगइन का एक आदेश कहता है
  2. पूर्व-उपचार फ़ंक्शन को कमांड कॉल करती है:

    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. एक अन्य फ़ंक्शन को कहा जाता है जो बफर की स्थिति को बदलता है ( Foo()या Bar()पिछले फ़ंक्शन की अंतिम पंक्तियों में)

  4. उपयोगकर्ता टैपिंग फ़ंक्शन को कॉल करने के लिए मैपिंग का उपयोग करते हैं
  5. आंसू डाउन फ़ंक्शन निर्मित मैपिंग को हटा दें:

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

मैं अपनी मैपिंग को संभालने के तरीके से संतुष्ट नहीं हूं: यदि उपयोगकर्ता पहले से ही इसे किसी और चीज़ से मैप करता है तो वह अपनी मूल मैपिंग को ढीला कर देगा।

तो मेरा सवाल यह है कि मैं कैसे <C-c>मैप किया जा सकता है (यदि यह मैप किया गया है) को कैसे सहेजा जाए और इसे मेरे आंसू डाउन फ़ंक्शन में पुनर्स्थापित किया जाए? क्या ऐसा करने के लिए एक अंतर्निहित सुविधा है? मैं हालांकि grepपरिणाम के बारे में आईएनजी, :nmap <C-c>लेकिन यह वास्तव में "साफ" महसूस नहीं करता है।

कुछ साइड नोट्स:

  • मुझे पता है कि LearnVimScriptTheHardWay के बारे में एक खंड है , लेकिन वे एक ftplugin का उपयोग करने के लिए कहते हैं जो यहां संभव नहीं है: प्लगइन एक फ़ाइल प्रकार पर निर्भर नहीं है
  • मैं उपयोगकर्ता को यह बताने के लिए एक चर बना सकता हूं कि किस कुंजी का उपयोग करने के लिए चुनें: यह शायद वही है जो मैं करूंगा लेकिन मैं मुख्य रूप से इस बात में दिलचस्पी रखता हूं कि बचत और पुनर्स्थापना कैसे करें।
  • मैं एक स्थानीय नेता का उपयोग कर सकता हूं, लेकिन मुझे लगता है कि यह थोड़ा अधिक है और मैं अभी भी मुख्य रूप से बचत और पुनर्स्थापना के बारे में उत्सुक हूं।

जवाबों:


24

आप maparg()फ़ंक्शन का उपयोग कर सकते हैं ।

यह जांचने के लिए कि उपयोगकर्ता <C-c>ने सामान्य मोड में कुछ मैप किया है , आप लिखेंगे:

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

यदि उपयोगकर्ता {rhs}किसी चर को संग्रहित करने के लिए कुछ मैप करता है, तो आप लिखेंगे:

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

यदि आप मानचित्रण के बारे में अधिक जानकारी चाहते हैं, जैसे:

  • क्या यह चुप है ( <silent>तर्क)?
  • क्या यह वर्तमान बफर ( <buffer>तर्क) के लिए स्थानीय है ?
  • है {rhs}एक अभिव्यक्ति (के मूल्यांकन <expr>तर्क)?
  • क्या यह रीमैप {rhs}( nnoremapबनाम nmap) करता है?
  • यदि उपयोगकर्ता के पास एक और मैपिंग है, जो शुरू होता है <C-c>, तो क्या विम अधिक अक्षर टाइप करने के लिए प्रतीक्षा करता है ( <nowait>तर्क)?
  • ...

फिर, आप एक तीसरा और चौथा तर्क दे सकते हैं: 0और 1
0क्योंकि आप एक मानचित्रण की तलाश कर रहे हैं और एक संक्षिप्त नाम नहीं, और 1क्योंकि आप अधिकतम जानकारी के साथ एक शब्दकोश चाहते हैं और न केवल {rhs}मूल्य:

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

यह मानते हुए कि उपयोगकर्ता ने अपनी मैपिंग में किसी विशेष तर्क का उपयोग नहीं किया है, और इसे हटाने के लिए {rhs}, इसे पुनर्स्थापित करने के लिए, आप इसे केवल नहीं पढ़ सकते हैं:

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

" do some stuff which changes the mapping

exe 'nnoremap <C-c> ' . rhs_save

या सुनिश्चित करें और सभी संभावित तर्कों को पुनर्स्थापित करें:

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

संपादित करें: क्षमा करें, मैंने महसूस किया है कि यह अपेक्षा के अनुरूप काम नहीं करेगा यदि उपयोगकर्ता {rhs}मैपिंग में स्क्रिप्ट-स्थानीय फ़ंक्शन को कॉल करता है ।

मान लीजिए कि उपयोगकर्ता के अंदर निम्नलिखित मानचित्रण है vimrc:

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

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

जब वह हिट <C-c>करता है, तो यह संदेश प्रदर्शित करता है hello world!

और अपने प्लगइन में, आप सभी जानकारी के साथ एक शब्दकोश को बचाते हैं, फिर अस्थायी रूप से उसकी मैपिंग को इस तरह बदलते हैं:

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

अब, यह प्रदर्शित करेगा bye all!। आपका प्लगइन कुछ काम करता है, और जब यह खत्म हो जाता है, तो यह पिछले कमांड के साथ मैपिंग को पुनर्स्थापित करने का प्रयास करता है।

यह शायद इस तरह दिखने वाले संदेश के साथ विफल हो जाएगा:

E117: Unknown function: <SNR>61_FuncA

61उस स्क्रिप्ट की पहचानकर्ता है जिसमें आपकी मैपिंग कमांड निष्पादित की जाएगी। यह कोई अन्य संख्या हो सकती है। यदि आपका प्लगइन उपयोगकर्ता के सिस्टम पर 42 वाँ फ़ाइल है, तो यह होगा 42

एक स्क्रिप्ट के अंदर, जब एक मैपिंग कमांड निष्पादित की जाती है, तो विम स्वचालित रूप <SID>से विशेष कुंजी कोड में नोटेशन का अनुवाद करता है <SNR>, इसके बाद एक नंबर होता है जो स्क्रिप्ट के लिए अद्वितीय होता है, और एक अंडरस्कोर होता है। इसे ऐसा करना होगा, क्योंकि जब उपयोगकर्ता हिट करेगा <C-c>, तो मैपिंग स्क्रिप्ट के बाहर निष्पादित की जाएगी, और इस प्रकार यह पता नहीं चलेगा कि किस स्क्रिप्ट FuncA()को परिभाषित किया गया है।

समस्या यह है कि मूल मानचित्रण आपके प्लगइन की तुलना में एक अलग स्क्रिप्ट में दिया गया था, इसलिए यहां स्वचालित अनुवाद गलत है। यह आपकी स्क्रिप्ट के पहचानकर्ता का उपयोग करता है, जबकि इसे उपयोगकर्ता के पहचानकर्ता का उपयोग करना चाहिए vimrc

लेकिन आप अनुवाद स्वयं कर सकते हैं। शब्दकोश map_saveमें एक कुंजी होती है, 'sid'जिसका मूल्य सही पहचानकर्ता होता है।
इसलिए, पिछली पुनर्स्थापना आदेश को और अधिक मजबूत बनाने के लिए, आप इसके map_save.rhsसाथ प्रतिस्थापित कर सकते हैं :

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

यदि {rhs}मूल मानचित्रण निहित है <SID>, तो इसे ठीक से अनुवादित किया जाना चाहिए। अन्यथा, कुछ भी नहीं बदला जाना चाहिए।

और यदि आप कोड को छोटा करना चाहते हैं, तो आप उन 4 लाइनों की जगह ले सकते हैं, जो विशेष तर्कों का ध्यान रखते हैं:

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

map()समारोह सूची से प्रत्येक आइटम परिवर्तित करना चाहिए ['buffer', 'expr', 'nowait', 'silent']इसी मानचित्रण बहस में लेकिन उसके प्रमुख के अंदर ही अगर map_saveगैर-शून्य है। और join()एक स्ट्रिंग में सभी आइटम शामिल होना चाहिए।

तो, मैपिंग को बचाने और बहाल करने का एक अधिक मजबूत तरीका हो सकता है:

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:

मैं आप के रूप में एक ही मुद्दे का सामना कर रहा हूं, एक ड्राइंग प्लगइन में मैपिंग को कैसे सहेजना और पुनर्स्थापित करना है। और मुझे लगता है कि मैंने 2 मुद्दों को पाया है जो प्रारंभिक उत्तर उस समय नहीं देखा था जब मैंने इसे लिखा था, इसके बारे में क्षमा करें।

पहला मुद्दा, मान लीजिए कि उपयोगकर्ता <C-c>एक ग्लोबल मैपिंग में उपयोग करता है , लेकिन बफर-लोकल मैपिंग में भी। उदाहरण:

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

इस मामले में, maparg()स्थानीय मानचित्रण को प्राथमिकता देंगे:

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

जिसकी पुष्टि की गई है :h maparg():

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

लेकिन शायद आपको बफर-लोकल मैपिंग में कोई दिलचस्पी नहीं है, हो सकता है कि आप ग्लोबल चाहते हों।
एकमात्र तरीका जो मैंने पाया, मज़बूती से, वैश्विक मैपिंग के बारे में जानकारी प्राप्त करने के लिए, एक ही कुंजी का उपयोग करके एक संभावित, छायांकन, बफर-लोकल मैपिंग को अस्थायी रूप से अनमैप करने का प्रयास करना है।

यह 4 चरणों में किया जा सकता है:

  1. कुंजी का उपयोग करके एक संभावित (संभावित) बफर-लोकल मैपिंग को बचाएं <C-c>
  2. :silent! nunmap <buffer> <C-c>(संभावित) बफर-लोकल मैपिंग को हटाने के लिए निष्पादित करें
  3. वैश्विक मानचित्रण को बचाएं ( maparg('<C-c>', 'n', 0, 1))
  4. बफर-लोकल मैपिंग को पुनर्स्थापित करें

दूसरा मुद्दा निम्नलिखित है। मान लीजिए कि उपयोगकर्ता ने कुछ भी मैप नहीं किया है <C-c>, तो आउटपुट maparg()एक खाली शब्दकोश होगा। और इस मामले में, पुनर्स्थापना प्रक्रिया एक मैपिंग ( :nnoremap) की स्थापना में शामिल नहीं है , लेकिन एक मैपिंग ( ) के विनाश में :nunmap

इन 2 नए मुद्दों को हल करने के लिए, आप मैपिंग को बचाने के लिए इस फ़ंक्शन को आज़मा सकते हैं:

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

... और यह उन्हें बहाल करने के लिए:

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()समारोह मैपिंग को बचाने के लिए इस्तेमाल किया जा सकता।
यह 3 तर्क की उम्मीद है:

  1. चाबियों की एक सूची; उदाहरण:['<C-a>', '<C-b>', '<C-c>']
  2. एक मोड; उदाहरण: nसामान्य मोड के लिए या xदृश्य मोड के लिए
  3. एक बूलियन ध्वज; यदि यह है 1, तो इसका मतलब है कि आप वैश्विक मैपिंग में रुचि रखते हैं, और यदि यह 0स्थानीय लोगों में है

इसके साथ, आप कुंजियों का उपयोग करके वैश्विक मैपिंग को सहेज सकते हैं C-a, C-bऔर C-c, सामान्य मोड में, शब्दकोश के अंदर:

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

फिर, बाद में, जब आप मैपिंग को पुनर्स्थापित करना चाहते हैं, तो आप कॉल कर सकते हैं Restore_mappings(), डिक्शनरी में सभी जानकारी को एक तर्क के रूप में पास कर सकते हैं:

call Restore_mappings(your_saved_mappings)

बफर-स्थानीय मैपिंग को सहेजते / बहाल करते समय एक 3 समस्या हो सकती है। क्योंकि, उन क्षणों के बीच जब हमने मैपिंग को सहेजा है, और जिस क्षण हम उन्हें पुनर्स्थापित करने का प्रयास करते हैं, वर्तमान बफर बदल सकता है।

इस स्थिति में, हो Save_mappings()सकता है कि वर्तमान बफर ( bufnr('%')) की संख्या को बचाकर फ़ंक्शन को बेहतर बनाया जा सके ।

और फिर, Restore_mappings()इस जानकारी का उपयोग सही बफर में बफर-स्थानीय मैपिंग को पुनर्स्थापित करने के लिए करेंगे। हम संभवतः :bufdoकमांड का उपयोग कर सकते हैं , उत्तरार्द्ध को एक गिनती के साथ उपसर्ग कर सकते हैं (पहले सहेजे गए बफर नंबर से मेल खाते हुए), और इसे मैपिंग कमांड के साथ प्रत्यय दें।

शायद कुछ इस तरह:

:{original buffer number}bufdo {mapping command}

हमें पहले चेक करना होगा कि क्या बफर अभी भी मौजूद है, bufexists()फ़ंक्शन का उपयोग करते हुए , क्योंकि इस बीच इसे हटाया जा सकता था।


कमाल है कि वास्तव में मैं क्या जरूरत है। धन्यवाद!
statox

2

मेरे प्लगइन्स में, जब मेरे पास अस्थायी मैपिंग होती है, तो वे हमेशा स्थानीय बफर होते हैं - मैं वास्तव में ग्लोबल मैपिंग को बचाने के बारे में परवाह नहीं करता हूं और न ही कुछ जटिल के बारे में जो उन्हें शामिल करता है। इसलिए मेरा lh#on#exit().restore_buffer_mapping()सहायक कार्य - lh-vim-lib से

अंत में, निम्नलिखित में से क्या होता है:

" 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.