现在我们已经给我们的Potion插件添加了足够多的功能,这也是我们在本书中要做得事情。在完成本书之前,我们会讨论一些能够让它更加闪亮的一些重要的方式。
第一件事是通过自动加载来让我们的插件更加有效率。
现在,如果一个用户加载我们的插件(通过打开一个Potion文件)它所有的功能都一次性加载完成。我们的插件还比较小,所以没有什么问题,但是对于一个大型插件而言,加载所有的代码会消耗一定的时间的。
Vim的解决方案是通过“自动加载”的方式。自动加载让你可以延迟代码到实际用的时候才加载。这样会导致性能有一点点损耗,但是如果用户不是每次都是使用你插件的全部代码,自动加载会带来很好的提升。
下面我们来看看它是怎么运作的。先看看下面的命令:
:call somefile#Hello()
当你运行这个命令时,Vim的表现会和运行一个正常的函数有一些差别。
如果这个函数已经被加载了,Vim会直接调用它。
否则的花,Vim会在~/.vim
目录(以及所有的Pathogen插件目录)查找一个autoload/somefile.vim
文件。
如果这个文件存在,Vim会加载这个文件,然后再按照正常的方式来调用他们。
在这个文件里,函数的定义应该和下面的相似:
function somefile#Hello()
" ...
endfunction
你可以在函数名里添加多个#
来表示多个字目录。例如:
:call myplugin#somefile#Hello()
这样就会查找自动加载的文件autoload/myplugin/somefile.vm
。并且文件内部的函数的定义需要带上全路径:
function myplugin#somefile#Hello()
" ...
endfunction
为了能够体验它是怎么工作的,我们先试试。创建一个~/.vim/autoload/example.vim
文件然后添加如下内容:
echom "Loading..."
function! example#Hello()
echom "Hello, world!"
endfunction
echom "Done loading."
保存文件,然后运行:call example#Hello()
命令。Vim会输出如下内容:
Loading...
Done loading.
Hello, world!
这个小示例证实了以下几件事情:
不用关闭Vim,把函数的定义改成如下所示:
echom "Loading..."
function! example#Hello()
echom "Hello AGAIN, world!"
endfunction
echom "Done loading."
保存文件,不用关闭Vim,运行:call example#Hello()
函数。Vim会简单地输出:
Hello, world!
Vim已经有了对于exmpale#Hello
函数的定义,所以它并不需要重新加载这个文件,也就是说:
现在运行:call example#BadFunction()
。你会再次看到加载的信息,同时也会看到函数不存在的错误提示。现在再次运行:call example#Hello()
。这次你会看到更新后的信息~
现在,你应该对于Vim如何加载自动加载形式的函数名的函数了:
如果你还没有彻底理解上面的内容,回头再看看上面的示例内容,然后看看每条规则都在哪儿生效。
自动加载并不是完全免费得。除了代码里丑陋的命名规则外,对于加载而言还是有一定(小)的影响的。
我们已经说过,如果你创建一个不是每次用户打开Vim会话的时候都进行加载的差距,那么你就需要尽量把功能都移到延迟加载里面。这样会减少你的插件对于Vim启动时间的影响,尤其是在用户安装了很多插件的时候。
那么,哪些是可以被安全地自动加载的呢?答案是所有的那些不是被用户直接调用的代码。映射和自定义命令不能被自动加载(因为那样的话,用户就没法调用它们了),但是其他的很多都可以。
现在我们来看看我们的Potion插件,看看哪些是可以自动加载的。
我们会从编译和运行的功能着手。回忆一下上一章节的末尾,我们的ftplugin/potion/running.vim
文件的内容:
if !exists("g:potion_command")
let g:potion_command = "/Users/sjl/src/potion/potion"
endif
function! PotionCompileAndRunFile()
silent !clear
execute "!" . g:potion_command . " " . bufname("%")
endfunction
function! PotionShowBytecode()
" Get the bytecode.
let bytecode = system(g:potion_command . " -c -V " . bufname("%"))
" Open a new split and set it up.
vsplit __Potion_Bytecode__
normal! ggdG
setlocal filetype=potionbytecode
setlocal buftype=nofile
" Insert the bytecode.
call append(0, split(bytecode, '\v\n'))
endfunction
nnoremap r :call PotionCompileAndRunFile()
nnoremap b :call PotionShowBytecode()
这个文件只会在Potion文件加载的时候调用,所以它不会对Vim的启动有影响。但是也有些用户不需要使用这些功能,所以如果可以使用自动加载,那么每次打开Potion文件的时候可以帮他们省几百毫秒。
当然,在这个场景下它不能节省多少时间。但是我可以肯定的时,假如一个插件有成千上万行函数的话,那么加载它们的时间也是很客观的。
我们开始吧。在你的插件代码库里创建一个autoload/potion/running.vim
文件。然后把下面的两个函数移到这个文件里面,并且调整它们的名称,最终看起来如下所示:
echom "Autoloading..."
function! potion#running#PotionCompileAndRunFile()
silent !clear
execute "!" . g:potion_command . " " . bufname("%")
endfunction
function! potion#running#PotionShowBytecode()
" Get the bytecode.
let bytecode = system(g:potion_command . " -c -V " . bufname("%"))
" Open a new split and set it up.
vsplit __Potion_Bytecode__
normal! ggdG
setlocal filetype=potionbytecode
setlocal buftype=nofile
" Insert the bytecode.
call append(0, split(bytecode, '\v\n'))
endfunction
注意potion#running
是如何匹配它所在的目录和文件名的。现在修改ftplugin/potion/running
,内容如下所示:
f !exists("g:potion_command")
let g:potion_command = "/Users/sjl/src/potion/potion"
endif
nnoremap r
\ :call potion#running#PotionCompileAndRunFile()
nnoremap b
\ :call potion#running#PotionShowBytecode()p
保存文件,关闭Vim然后打开你的factorial.pn
文件。试试我们创建的映射,确保它们能够正常使用。
确保你只会在第一次使用映射的时候才会看到调试信息Autoloading...
(也许你需要使用:messages
命令来查看它)。当你确定自动加载已经能够正常运行的时候,你就可以删除这些调试信息了。
正如你所看到的,我们现在只留下了nnoremap
的映射。我们不能自动加载这些,因为如果这样做了用户就没有办法能够去自动加载这些映射了。
这个在Vim插件里是很常用的一种模式:大部分的功能都是自动加载的,只会在Vim每次都加载的文件里放上nnoremap
和command
命令。注意,当你写一个不小的插件的时候都要注意这样去做。
:help autoload
的内容加载你想编程实现如何加载一个Vim已经加载过的文件,并且不会打扰用户。也许你要试试这样做?你或许可以阅读一下:help silent!
。
但是在现实中不要这么做。