Para responder à sua pergunta: o protótipo de call()
no manual é call({func}, {arglist} [, {dict}])
; o {arglist}
argumento precisa ser literalmente um objeto de lista, não uma lista de argumentos. Ou seja, você deve escrevê-lo assim:
let @x = call(a:functionToExecute, [GetSelectedText()])
Isso pressupõe que a:functionToExecute
é um Funcref (consulte :help Funcref
) ou o nome de uma função (ou seja, uma string, como 'Type1ProcessString'
).
Agora, esse é um recurso poderoso que oferece ao Vim uma qualidade semelhante ao LISP, mas você provavelmente nunca o usaria como acima. Se a:functionToExecute
for uma string, o nome de uma função, você poderá fazer isso:
function! Wrapper(functionToExecute)
" ...
let s:processing = function(a:functionToExecute)
let @x = s:processing(GetSelectedText())
" ...
endfunction
e você chamaria o wrapper com o nome da função:
call Wrapper('Type1ProcessString')
Se, por outro lado, a:functionToExecute
é um Funcref, você pode chamá-lo diretamente:
function! Wrapper(functionToExecute)
" ...
let @x = a:functionToExecute(GetSelectedText())
" ...
endfunction
mas você precisa chamar o wrapper assim:
call Wrapper(function('Type1ProcessString'))
Você pode verificar a existência de funções com exists('*name')
. Isso possibilita o seguinte pequeno truque:
let s:width = function(exists('*strwidth') ? 'strwidth' : 'strlen')
ou seja, uma função que usa o built-in strwidth()
se o Vim é novo o suficiente para tê-lo e volta ao strlen()
contrário (não estou argumentando que esse fallback faz sentido; estou apenas dizendo que pode ser feito). :)
Com as funções de dicionário (consulte :help Dictionary-function
), você pode definir algo parecido com classes:
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
Então você instanciaria objetos como este:
let little_object = g:MyClass.New({'foo': 'bar'})
E chame seus métodos:
call little_object.setFoo('baz')
echomsg little_object.getFoo()
call little_object.process()
Você também pode ter atributos e métodos de classe:
let g:MyClass.__meaning_of_life = 42
function g:MyClass.GetMeaningOfLife()
return get(g:MyClass, '__meaning_of_life')
endfunction
(observe que não é necessário dict
aqui).
Edit: Subclassing é algo como isto:
let g:MySubclass = copy(g:MyClass)
call extend(g:MySubclass, subclass_attributes)
O ponto sutil aqui é o uso de em copy()
vez de deepcopy()
. O motivo para isso é poder acessar os atributos da classe pai por referência. Isso pode ser alcançado, mas é altamente frágil e acertar está longe de ser trivial. Outro problema potencial é que este tipo de conflates subclasse is-a
com has-a
. Por esse motivo, os atributos de classe geralmente não valem a pena.
Ok, isso deve ser suficiente para lhe dar um pouco de reflexão.
De volta ao seu snippet de código inicial, existem dois detalhes que podem ser aprimorados:
- você não precisa
normal gvd
remover a seleção antiga, normal "xp
substitui-a mesmo que não a mate primeiro
- use em
call setreg('x', [lines], type)
vez de let @x = [lines]
. Isso define explicitamente o tipo do registro x
. Caso contrário, você x
já conta com o tipo correto (ou seja, caractere, linha ou bloco).
dict
palavra - chave. Isso se aplica aos seus "métodos de classe". Veja:h numbered-function
.