我們已經(jīng)為我們的Potion插件寫了大量的功能,覆蓋了本書所要講的內(nèi)容。 在結(jié)束之前,我們將講到一些非常重要的方法,可以給我們的插件錦上添花。
第一項(xiàng)是使用自動(dòng)加載讓我們的插件更有效率。
目前,當(dāng)用戶加載我們的插件時(shí)(比如打開了一個(gè)Potion文件),所有的功能都會(huì)被加載。 我們的插件還很小,所以這大概不是什么大問(wèn)題,但對(duì)于較大的插件,加載全部代碼將會(huì)導(dǎo)致可被察覺(jué)的卡頓。
Vim使用稱為"自動(dòng)加載(autoload)"來(lái)解決這個(gè)問(wèn)題。自動(dòng)加載讓你直到需要時(shí)才加載某一部分代碼。 會(huì)有一些性能上的損失,但如果用戶不總是需要你的插件的每一行代碼,自動(dòng)加載將帶來(lái)速度上的飛躍。
示范一下它是怎么工作的。看看下面的命令:
:call somefile#Hello()
當(dāng)你執(zhí)行這個(gè)命令,Vim的行為與平常的函數(shù)調(diào)用有些許不同。
如果這個(gè)函數(shù)已經(jīng)加載了,Vim簡(jiǎn)單地像平常一樣調(diào)用它。
否則Vim將在你的~/.vim
(或~/.vim/bundles/對(duì)應(yīng)的插件/autoload
)下查找一個(gè)叫做autoload/somefile.vim
的文件。
如果文件存在,Vim將加載/source文件。接著Vim就會(huì)像平常一樣調(diào)用它。
在這個(gè)文件內(nèi),函數(shù)應(yīng)該這樣定義:
function somefile#Hello()
" ...
endfunction
你可以在函數(shù)名中使用多個(gè)#
來(lái)表示子目錄。舉個(gè)例子:
:call myplugin#somefile#Hello()
這將在autoload/myplugin/somefile.vim
查找自動(dòng)加載文件。 里面的函數(shù)需要使用自動(dòng)加載的絕對(duì)路徑進(jìn)行定義:
function myplugin#somefile#Hello()
" ...
endfunction
為了更好地理解自動(dòng)加載,讓我們實(shí)驗(yàn)一下。 創(chuàng)建一個(gè)~/.vim/autoload/example.vim
文件并加入下面的內(nèi)容:
echom "Loading..."
function! example#Hello()
echom "Hello, world!"
endfunction
echom "Done loading."
保存文件并執(zhí)行:call example#Hello()
。Vim將輸出下面內(nèi)容:
Loading...
Done loading.
Hello, world!
這個(gè)小演示證明了幾件事:
example.vim
文件。當(dāng)我們打開Vim的時(shí)候它并不存在,所以不可能是在啟動(dòng)時(shí)加載的。先不要關(guān)閉Vim,修改函數(shù)的定義成這樣:
echom "Loading..."
function! example#Hello()
echom "Hello AGAIN, world!"
endfunction
echom "Done loading."
保存文件并不要關(guān)閉Vim,執(zhí)行:call example#Hello()
。Vim將簡(jiǎn)單地輸出:
Hello, world!
Vim已經(jīng)有了example#Hello
的一個(gè)定義,所以它不再需要重新加載文件,這意味著:
現(xiàn)在執(zhí)行:call example#BadFunction()
。你將再一次看到加載信息,伴隨著一個(gè)函數(shù)不存在的錯(cuò)誤。 但現(xiàn)在嘗試再次執(zhí)行:call example#Hello()
。這次你將看到更新后的信息!
目前為止你應(yīng)該清晰地了解到Vim會(huì)怎么處理一個(gè)自動(dòng)加載類型的函數(shù)調(diào)用吧:
如果你還是沒(méi)有完成弄懂,回到前面重新過(guò)一遍演示,注意觀察每條規(guī)則生效的地方。
自動(dòng)加載不是沒(méi)有缺陷的。 設(shè)置了自動(dòng)加載后,會(huì)有一些(小的)運(yùn)行開銷,更別說(shuō)你不得不在你的代碼里容忍丑陋的函數(shù)名了。
正因?yàn)槿绱耍绻悴皇菍懸粋€(gè)用戶會(huì)在_每次_打開Vim對(duì)話時(shí)都用到的插件,最好盡量把功能代碼都挪到autoload文件中去。 這將減少你的插件在用戶啟動(dòng)Vim時(shí)的影響,尤其是在人們安裝了越來(lái)越多的插件的今天。
所以有什么是可以安全地自動(dòng)加載?那些不由你的用戶直接調(diào)用的部分。 映射和自定義命令不能自動(dòng)加載(因?yàn)樗鼈冃枰捎脩粽{(diào)用),但別的許多東西都可以。
讓我們看看Potion插件里有什么可以自動(dòng)加載的。
我們將從編譯和執(zhí)行功能開始下手。 在前一章的最后,我們的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 <buffer> <localleader>r :call PotionCompileAndRunFile()<cr>
nnoremap <buffer> <localleader>b :call PotionShowBytecode()<cr>
這個(gè)文件僅僅當(dāng)Potion文件加載時(shí)才會(huì)調(diào)用,所以它通常不會(huì)影響Vim的啟動(dòng)時(shí)間。 但可能會(huì)有一些用戶就是不想要這些功能,所以如果我們可以自動(dòng)加載某些部分, 每次打開Potion文件時(shí)可以省下他們以毫秒記的時(shí)間。
是的,這種情況下我們不會(huì)節(jié)省多少。 但你可以想象到可能有那么一個(gè)插件包括了數(shù)千行可以通過(guò)自動(dòng)加載來(lái)減少每次的加載時(shí)間的代碼。
讓我們開始吧。在你的插件repo中創(chuàng)建一個(gè)autoload/potion/running.vim
文件。 然后移動(dòng)兩個(gè)函數(shù)進(jìn)去,并修改它們的名字,讓它們看上去像:
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
部分的函數(shù)名怎么匹配它們所在的路徑。 現(xiàn)在修改ftplugin/potion/running.vim
文件成這樣:
if !exists("g:potion_command")
let g:potion_command = "/Users/sjl/src/potion/potion"
endif
nnoremap <buffer> <localleader>r
\ :call potion#running#PotionCompileAndRunFile()<cr>
nnoremap <buffer> <localleader>b
\ :call potion#running#PotionShowBytecode()<cr>
保存文件,關(guān)閉Vim,然后打開你的factorial.pn
文件。嘗試這些映射,確保它們依然正常工作。
確保你僅僅在第一次執(zhí)行其中一個(gè)映射的時(shí)候才看到診斷信息Autoloading...
(你可能需要使用:message
來(lái)看到)。 一旦認(rèn)為自動(dòng)加載正常工作,你可以移除那些信息。
正如你看到的,我們保留nnoremap
映射部分不變。 我們不能自動(dòng)加載它們,不然用戶就沒(méi)辦法引發(fā)自動(dòng)加載了!
你將在Vim插件中普遍看到:大多數(shù)的功能將位于自動(dòng)加載函數(shù)中,僅有nnoremap
和command
命令每次都被Vim加載。 每次你寫一個(gè)有用的Vim插件時(shí),不要忘了這一點(diǎn)。
閱讀:help autoload
稍微測(cè)試一下并弄懂自動(dòng)加載變量是怎么一回事。
假設(shè)你想要強(qiáng)制加載一個(gè)Vim已經(jīng)加載的自動(dòng)加載文件,并不會(huì)驚擾到用戶。 你會(huì)怎么做?你可能想要閱讀:help silent!
(譯注:此處應(yīng)該是:help :silent
)。不過(guò)在現(xiàn)實(shí)生活中請(qǐng)不要那么做。
更多建議: