क्या एक प्रतिनिधि का उपयोग करना संभव है या किसी फ़ंक्शन को विम्सस्क्रिप्ट में तर्क के रूप में पारित करना संभव है?


11

मैं vimscript सीखने के लिए एक छोटा सा प्लगइन बनाने की कोशिश कर रहा हूँ, मेरा लक्ष्य चयनित पाठ को संसाधित करने और परिणाम के साथ बदलने के लिए कुछ फ़ंक्शन बनाना है। स्क्रिप्ट में निम्न आइटम हैं:

  • पाठ को संसाधित करने वाले दो कार्य: वे एक स्ट्रिंग को पैरामीटर के रूप में लेते हैं एक वापसी स्ट्रिंग जिसे मूल पाठ को बदलने के लिए उपयोग किया जाना चाहिए। अभी के लिए मेरे पास केवल दो हैं लेकिन कुछ समय में बहुत अधिक हो सकते हैं।

  • एक फ़ंक्शन जो चयनित पाठ प्राप्त कर रहा है: जो केवल अंतिम चयन को छोड़ देता है और इसे वापस कर देता है।

  • एक आवरण फ़ंक्शन: जो एक प्रोसेसिंग फ़ंक्शन को कॉल करता है, अपना परिणाम प्राप्त करता है और इस परिणाम के साथ पुराने चयन को प्रतिस्थापित करता है।

अभी के लिए मेरा रैपर फंक्शन ऐसा दिखता है:

function! Wrapper()
    " Get the string to insert
    let @x = Type1ProcessString(GetSelectedText())

    " remove the old selection
    normal gvd

    " insert the new string
    normal "xp
endfunction

और मुझे लाइन 3 की जगह दूसरा रैपर बनाना है

let @x = Type2ProcessString(GetSelectedText())

मैं अपने रैपर फंक्शन को एक पैरामीटर देना चाहता हूँ, जिसमें प्रोसेस फंक्शन को शामिल करने के लिए एक पैरामीटर है, जो लाइन 3 में एक जेनेरिक कॉल का उपयोग करता है। अभी के लिए मैंने callविभिन्न तरीकों का उपयोग करने की कोशिश की है, उदाहरण के लिए, यह:

let @x = call('a:functionToExecute', GetSelectedText()) 

लेकिन मैं वास्तव में आत्मघाती नहीं रहा और :h callप्रतिनिधि विषय पर वास्तव में मददगार नहीं रहा।

इसे यहाँ प्रस्तुत करने के लिए मेरे प्रश्न हैं:

  • मैं सभी प्रसंस्करण वालों के लिए केवल एक आवरण समारोह कैसे बना सकता हूं?
  • क्या कोई ऐसी चीज़ है जो विमिस्क्रिप्ट में एक प्रतिनिधि के रूप में काम करती है?
  • यदि प्रतिनिधियों का अस्तित्व नहीं है, तो मैं जो करना चाहता हूं, उसके लिए "अच्छा" तरीका क्या होगा?

जवाबों:


16

Youre के सवाल का जवाब देने के लिए: call()मैनुअल का प्रोटोटाइप है call({func}, {arglist} [, {dict}]); {arglist}तर्क सचमुच एक सूची वस्तु, नहीं तर्कों की सूची की जरूरत है। अर्थात्, आपको इसे इस तरह लिखना होगा:

let @x = call(a:functionToExecute, [GetSelectedText()])

यह a:functionToExecuteमाना जाता है कि या तो एक Funcref है (देखें :help Funcref), या किसी फ़ंक्शन का नाम (जैसे कि एक स्ट्रिंग, जैसे 'Type1ProcessString')।

अब, यह एक शक्तिशाली विशेषता है जो विम को एक प्रकार की एलआईएसपी जैसी गुणवत्ता प्रदान करती है, लेकिन आप शायद ही कभी इसे ऊपर के रूप में उपयोग करेंगे। यदि a:functionToExecuteएक स्ट्रिंग, एक फ़ंक्शन का नाम है, तो आप यह कर सकते हैं:

function! Wrapper(functionToExecute)
    " ...
    let s:processing = function(a:functionToExecute)
    let @x = s:processing(GetSelectedText())
    " ...
endfunction

और आप फ़ंक्शन के नाम के साथ रैपर को कॉल करेंगे:

call Wrapper('Type1ProcessString')

यदि दूसरी ओर a:functionToExecuteएक Funcref है, तो आप इसे सीधे कॉल कर सकते हैं:

function! Wrapper(functionToExecute)
    " ...
    let @x = a:functionToExecute(GetSelectedText())
    " ...
endfunction

लेकिन आपको इस तरह के रैपर को कॉल करने की आवश्यकता है:

call Wrapper(function('Type1ProcessString'))

आप के साथ कार्यों के अस्तित्व के लिए जाँच कर सकते हैं exists('*name')। यह निम्नलिखित छोटी चाल को संभव बनाता है:

let s:width = function(exists('*strwidth') ? 'strwidth' : 'strlen')

यानी एक फ़ंक्शन जो बिल्ट-इन का उपयोग करता है strwidth()यदि विम के पास नया है, और strlen()अन्यथा वापस आ जाता है (मैं बहस नहीं कर रहा हूं कि इस तरह की गिरावट का मतलब है; मैं सिर्फ यह कह रहा हूं कि यह किया जा सकता है)। :)

शब्दकोश कार्यों के साथ (देखें :help Dictionary-function) आप कुछ समान वर्गों को परिभाषित कर सकते हैं:

let g:MyClass = {}

function! g:MyClass.New(...)
    let newObj = copy(self)

    if a:0 && type(a:1) == type({})
        let newObj._attributes = deepcopy(a:1)
    endif
    if exists('*MyClassProcess')
        let newObj._process = function('MyClassProcess')
    else
        let newObj._process = function('s:_process_default')
    endif

    return newObj
endfunction

function! g:MyClass.getFoo() dict
    return get(get(self, '_attributes', {}), 'foo')
endfunction

function! g:MyClass.setFoo(val) dict
    if !has_key(self, '_attributes')
        let self._attributes = {}
    endif
    let self._attributes['foo'] = a:val
endfunction

function! g:MyClass.process() dict
    call self._process()
endfunction

function! s:_process_default()
    echomsg 'nothing to see here, define MyClassProcess() to make me interesting'
endfunction

फिर आप इस तरह से वस्तुओं को तुरंत भेज देंगे:

let little_object = g:MyClass.New({'foo': 'bar'})

और इसके तरीकों को बुलाओ:

call little_object.setFoo('baz')
echomsg little_object.getFoo()
call little_object.process()

आपके पास श्रेणी विशेषताएँ और विधियाँ भी हो सकती हैं:

let g:MyClass.__meaning_of_life = 42

function g:MyClass.GetMeaningOfLife()
    return get(g:MyClass, '__meaning_of_life')
endfunction

( dictयहां कोई आवश्यकता नहीं नोटिस )।

संपादित करें: उपवर्ग कुछ इस प्रकार है:

let g:MySubclass = copy(g:MyClass)
call extend(g:MySubclass, subclass_attributes)

यहाँ सूक्ष्म बिंदु copy()इसके बजाय का उपयोग है deepcopy()। इसका कारण माता-पिता वर्ग की विशेषताओं को संदर्भ द्वारा एक्सेस करने में सक्षम होना है। यह हासिल किया जा सकता है, लेकिन यह बहुत नाजुक है और इसे सही होना तुच्छ से दूर है। एक और संभावित समस्या यह है कि इस तरह के उपवर्ग का सामना is-aकरना पड़ता है has-a। इस कारण से कक्षा की विशेषताएं आमतौर पर दर्द के लायक नहीं होती हैं।

ठीक है, यह आपको विचार के लिए कुछ भोजन देने के लिए पर्याप्त होना चाहिए।

अपने प्रारंभिक कोड स्निपेट पर वापस जाएं, इसके साथ दो विवरण हैं जिन्हें बेहतर बनाया जा सकता है:

  • आपको normal gvdपुराने चयन को हटाने की आवश्यकता नहीं है , normal "xpयदि आप इसे पहले नहीं मारते हैं तो भी इसे बदल देगा
  • के call setreg('x', [lines], type)बजाय का उपयोग करें let @x = [lines]। यह स्पष्ट रूप से रजिस्टर के प्रकार को निर्धारित करता है x। अन्यथा आप xपहले से ही सही प्रकार (यानी वर्ण, अलंकृत, या अवरोधक) पर भरोसा कर रहे हैं ।

जब आप सीधे शब्दकोश में फ़ंक्शन बनाते हैं (यानी "क्रमांकित फ़ंक्शन"), तो आपको dictकीवर्ड की आवश्यकता नहीं है । यह आपके "क्लास मेथड्स" पर लागू होता है। देखते हैं :h numbered-function
कार्ल यंगवे लार्वाग

@ KarlYngveLervåg तकनीकी रूप से यह वर्ग और वस्तु दोनों तरीकों पर लागू होता है (अर्थात dictकिसी भी MyClassकार्य के लिए इसकी कोई आवश्यकता नहीं है )। लेकिन मुझे वह भ्रामक लगता है, इसलिए मैं dictस्पष्ट रूप से जोड़ना चाहता हूं ।
lcd047

समझा। तो आप dictअपने इरादे को स्पष्ट करने में मदद करने के लिए, क्लास के तरीकों के लिए नहीं, बल्कि ऑब्जेक्ट के तरीकों को जोड़ते हैं ?
कार्ल यंगवे लार्वाग

@ lcd047 इस अद्भुत उत्तर के लिए बहुत बहुत धन्यवाद! मुझे इस पर काम करना होगा, लेकिन यह वही है जो मैं देख रहा था!
statox

1
@ KarlYngveLervåg यहाँ एक घटाव है, selfवर्ग विधियों के लिए और वस्तु विधियों के लिए अलग-अलग अर्थ है - यह पूर्व के मामले में स्वयं कक्षा है, और उत्तरार्द्ध में वर्तमान वस्तु का उदाहरण है। इस कारण से मैं हमेशा क्लास को ही संदर्भित करता हूं g:MyClass, कभी उपयोग नहीं करता self, और मैं ज्यादातर dictएक अनुस्मारक के रूप में देखता हूं कि इसका उपयोग करना ठीक है self(अर्थात, एक फ़ंक्शन जो dictहमेशा ऑब्जेक्ट उदाहरण पर कार्य करता है)। फिर से, मैं कक्षा के तरीकों का ज्यादा इस्तेमाल नहीं करता हूं, और जब मैं ऐसा करता हूं तो मुझे भी dictहर जगह छोड़ना पड़ता है। हाँ, आत्म-संगति मेरा मध्य नाम है। ;)
lcd047

1

एक स्ट्रिंग में कमांड बनाएँ और :exeइसे चलाने के लिए उपयोग करें। :help executeअधिक जानकारी के लिए देखें।

इस स्थिति में, executeफ़ंक्शन को कॉल करने और परिणाम को रजिस्टर में डालने के लिए उपयोग किया जाता है, कमांड के विभिन्न तत्वों को .ऑपरेटर के साथ नियमित स्ट्रिंग के रूप में संक्षिप्त किया जाना चाहिए । पंक्ति 3 तब बननी चाहिए:

execute "let @x = " . a:functionToExecute . "(GetSelectedText())"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.