Per rispondere alla tua domanda: il prototipo di call()
nel manuale è call({func}, {arglist} [, {dict}])
; l' {arglist}
argomento deve essere letteralmente un oggetto Elenco, non un elenco di argomenti. Cioè, devi scriverlo in questo modo:
let @x = call(a:functionToExecute, [GetSelectedText()])
Ciò presuppone che a:functionToExecute
sia Funcref (vedi :help Funcref
) o il nome di una funzione (cioè una stringa, come 'Type1ProcessString'
).
Ora, questa è una potente funzionalità che dà a Vim una sorta di qualità simile a LISP, ma probabilmente la useresti raramente come sopra. Se a:functionToExecute
è una stringa, il nome di una funzione, puoi farlo:
function! Wrapper(functionToExecute)
" ...
let s:processing = function(a:functionToExecute)
let @x = s:processing(GetSelectedText())
" ...
endfunction
e chiamereste il wrapper con il nome della funzione:
call Wrapper('Type1ProcessString')
Se d'altra parte a:functionToExecute
è un Funcref, puoi chiamarlo direttamente:
function! Wrapper(functionToExecute)
" ...
let @x = a:functionToExecute(GetSelectedText())
" ...
endfunction
ma devi chiamare il wrapper in questo modo:
call Wrapper(function('Type1ProcessString'))
È possibile verificare l'esistenza di funzioni con exists('*name')
. Questo rende possibile il seguente piccolo trucco:
let s:width = function(exists('*strwidth') ? 'strwidth' : 'strlen')
vale a dire una funzione che utilizza il built-in strwidth()
se Vim è abbastanza nuovo per averlo, e ricade in strlen()
altro modo (non sto sostenendo che un tale fallback abbia senso; sto solo dicendo che può essere fatto). :)
Con le funzioni del dizionario (vedi :help Dictionary-function
) puoi definire qualcosa di simile alle classi:
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
Quindi istanziate oggetti come questo:
let little_object = g:MyClass.New({'foo': 'bar'})
E chiama i suoi metodi:
call little_object.setFoo('baz')
echomsg little_object.getFoo()
call little_object.process()
Puoi anche avere attributi e metodi di classe:
let g:MyClass.__meaning_of_life = 42
function g:MyClass.GetMeaningOfLife()
return get(g:MyClass, '__meaning_of_life')
endfunction
(notare che non è necessario dict
qui).
Modifica: la sottoclasse è qualcosa del genere:
let g:MySubclass = copy(g:MyClass)
call extend(g:MySubclass, subclass_attributes)
Il punto sottile qui è l'uso copy()
invece di deepcopy()
. Il motivo di ciò è di poter accedere agli attributi della classe genitore per riferimento. Questo può essere ottenuto, ma è molto fragile e farlo nel modo giusto è tutt'altro che banale. Un altro potenziale problema è che questo tipo di sottoclasse si confonde is-a
con has-a
. Per questo motivo gli attributi di classe di solito non valgono davvero la pena.
Ok, questo dovrebbe bastare a darti un po 'di spunti di riflessione.
Torna allo snippet di codice iniziale, ci sono due dettagli che potrebbero essere migliorati:
- non è necessario
normal gvd
rimuovere la vecchia selezione, normal "xp
la sostituirà anche se non la uccidi per prima
- utilizzare
call setreg('x', [lines], type)
invece di let @x = [lines]
. Questo imposta esplicitamente il tipo di registro x
. Altrimenti stai facendo affidamento sul fatto di x
avere già il tipo corretto (ovvero, a livello di carattere, di linea o di blocco).
dict
parola chiave. Questo vale per i tuoi "metodi di classe". Vedere:h numbered-function
.