Vimscript 自動(dòng)加載

2018-02-24 16:03 更新

我們已經(jīng)為我們的Potion插件寫了大量的功能,覆蓋了本書所要講的內(nèi)容。 在結(jié)束之前,我們將講到一些非常重要的方法,可以給我們的插件錦上添花。

第一項(xiàng)是使用自動(dò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

實(shí)驗(yàn)一下

為了更好地理解自動(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è)小演示證明了幾件事:

  1. Vim的確是在半途加載了example.vim文件。當(dāng)我們打開Vim的時(shí)候它并不存在,所以不可能是在啟動(dòng)時(shí)加載的。
  2. 當(dāng)Vim找到它需要自動(dòng)加載的文件后,它在調(diào)用對(duì)應(yīng)函數(shù)之前就加載了整個(gè)文件。

先不要關(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è)定義,所以它不再需要重新加載文件,這意味著:

  1. 函數(shù)以外的代碼將不再執(zhí)行。
  2. 它不會(huì)反映函數(shù)本身的變化。

現(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)用吧:

  1. 它首先是否已經(jīng)存在同名的函數(shù)了。如果是,就調(diào)用它。
  2. 否則,查找名字對(duì)應(yīng)的文件,并source它。
  3. 然后試圖調(diào)用那個(gè)函數(shù)。如果成功,太棒了。如果失敗,就輸出一個(gè)錯(cuò)誤。

如果你還是沒(méi)有完成弄懂,回到前面重新過(guò)一遍演示,注意觀察每條規(guī)則生效的地方。

自動(dòng)加載什么

自動(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)加載的。

在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ù)中,僅有nnoremapcommand命令每次都被Vim加載。 每次你寫一個(gè)有用的Vim插件時(shí),不要忘了這一點(diǎn)。

練習(xí)

閱讀: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)不要那么做。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)