Para responder a su pregunta: el prototipo de call()
en el manual es call({func}, {arglist} [, {dict}])
; el {arglist}
argumento debe ser literalmente un objeto List, no una lista de argumentos. Es decir, tienes que escribirlo así:
let @x = call(a:functionToExecute, [GetSelectedText()])
Esto supone que a:functionToExecute
es un Funcref (ver :help Funcref
), o el nombre de una función (es decir, una cadena, como 'Type1ProcessString'
).
Ahora, esa es una característica poderosa que le da a Vim una especie de calidad similar a LISP, pero probablemente rara vez la usará como se indicó anteriormente. Si a:functionToExecute
es una cadena, el nombre de una función, puede hacer esto:
function! Wrapper(functionToExecute)
" ...
let s:processing = function(a:functionToExecute)
let @x = s:processing(GetSelectedText())
" ...
endfunction
y llamarías al contenedor con el nombre de la función:
call Wrapper('Type1ProcessString')
Si por otro lado a:functionToExecute
es un Funcref, puede llamarlo directamente:
function! Wrapper(functionToExecute)
" ...
let @x = a:functionToExecute(GetSelectedText())
" ...
endfunction
pero necesitas llamar al contenedor así:
call Wrapper(function('Type1ProcessString'))
Puede verificar la existencia de funciones con exists('*name')
. Esto hace posible el siguiente pequeño truco:
let s:width = function(exists('*strwidth') ? 'strwidth' : 'strlen')
es decir, una función que usa el incorporado strwidth()
si Vim es lo suficientemente nuevo como para tenerlo, y recurre a lo strlen()
contrario (no estoy argumentando que tal retroceso tenga sentido; solo digo que se puede hacer). :)
Con las funciones de diccionario (ver :help Dictionary-function
) puede definir algo parecido a las clases:
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
Entonces crearías instancias de objetos como este:
let little_object = g:MyClass.New({'foo': 'bar'})
Y llame a sus métodos:
call little_object.setFoo('baz')
echomsg little_object.getFoo()
call little_object.process()
También puede tener atributos y métodos de clase:
let g:MyClass.__meaning_of_life = 42
function g:MyClass.GetMeaningOfLife()
return get(g:MyClass, '__meaning_of_life')
endfunction
(Aviso no es necesario dict
aquí).
Editar: Subclasificar es algo como esto:
let g:MySubclass = copy(g:MyClass)
call extend(g:MySubclass, subclass_attributes)
El punto sutil aquí es el uso de en copy()
lugar de deepcopy()
. La razón de esto es poder acceder a los atributos de la clase padre por referencia. Esto se puede lograr, pero es muy frágil y hacerlo bien está lejos de ser trivial. Otro problema potencial es que este tipo de subclase se combina is-a
con has-a
. Por esta razón, los atributos de clase generalmente no valen la pena.
Ok, esto debería ser suficiente para pensar un poco.
Volviendo a su fragmento de código inicial, hay dos detalles que podrían mejorarse:
- no necesita
normal gvd
eliminar la selección anterior, la normal "xp
reemplazará incluso si no la mata primero
- usar en
call setreg('x', [lines], type)
lugar de let @x = [lines]
. Esto establece explícitamente el tipo de registro x
. De lo contrario, dependerá de que x
ya tenga el tipo correcto (es decir, en forma de caracteres, en línea o en bloque).
dict
palabra clave. Esto se aplica a sus "métodos de clase". Ver:h numbered-function
.