我正在尝试创建一个小的插件来学习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())


我想给包装器函数一个参数,该参数包含要执行的Process函数并使用第3行中的一般调用。到目前为止,我尝试使用call的不同方式,例如,例如:
在委托主题上并没有真正帮助。

总结一下,这是我的问题:


我怎样才能仅为所有对象创建一个包装函数?正在处理吗?
有什么东西可以用作delega在vimscript中?
如果不存在代表,那么做我想要的事情的“好”方法是什么?


#1 楼

回答您的问题:手册中call()的原型为call({func}, {arglist} [, {dict}]){arglist}参数实际上是一个List对象,而不是参数列表。也就是说,您必须这样编写:

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


假设a:functionToExecute是Funcref(请参阅:help Funcref)或函数名称(例如字符串) ,例如'Type1ProcessString')。

现在,这是一项功能强大的功能,可为Vim提供类似LISP的质量,但是您可能很少像上面那样使用它。如果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')


即如果Vim足够新,则使用内置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-ahas-a合并。出于这个原因,类属性通常并不值得付出。

好吧,这应该足以让您思考一下。

回到您的初始代码段,有两个可以改进的细节:


您不需要normal gvd即可删除旧选择,即使您不先杀死它,normal "xp也会取代它
使用call setreg('x', [lines], type)代替let @x = [lines]。这将显式设置寄存器x的类型。否则,您将依靠已经具有正确类型(即字符,行或块)的x


评论


直接在字典中创建函数(即“编号函数”)时,不需要dict关键字。这适用于您的“类方法”。参见:h numbered-function。

– Karl YngveLervåg
15年7月11日在19:33

@KarlYngveLervåg从技术上讲,它适用于类和对象方法(即,对于任何MyClass函数都不需要dict)。但是我发现这很令人困惑,因此我倾向于显式地添加字典。

–lcd047
15年7月11日在20:46

我懂了。因此,您为对象方法而不是类方法添加了dict,以帮助阐明您的意图?

– Karl YngveLervåg
15年7月12日在11:08

@ lcd047非常感谢这个惊人的答案!我将不得不对其进行处理,但这正是我所寻找的!

–statox♦
15年7月12日在12:02

@KarlYngveLervåg这里有一个微妙之处,self的含义对于类方法和对象方法是不同的-在前一种情况下,它是类本身,而在后一种情况下,是当前对象的实例。因此,我总是将类本身称为g:MyClass,从不使用self,而我大多将dict看作是可以使用self的提示(即,具有dict的函数始终作用于对象实例) 。再说一次,我不太使用类方法,当我这样做时,我也倾向于在各处忽略字典。是的,自洽是我的中间名。 ;)

–lcd047
15年7月12日在16:25

#2 楼

将命令构建为字符串,然后使用:exe运行该命令。有关更多详细信息,请参见:help execute

在这种情况下,使用execute进行函数调用并将结果存储在寄存器中,该命令的不同元素必须与.运算符串联在一起常规字符串。第3行应变为:

execute "let @x = " . a:functionToExecute . "(GetSelectedText())"