我們已經為我們的Potion插件寫了大量的功能,覆蓋了本書所要講的內容。 在結束之前,我們將講到一些非常重要的方法,可以給我們的插件錦上添花。
第一項是使用自動加載讓我們的插件更有效率。
## 如何自動加載
目前,當用戶加載我們的插件時(比如打開了一個Potion文件),所有的功能都會被加載。 我們的插件還很小,所以這大概不是什么大問題,但對于較大的插件,加載全部代碼將會導致可被察覺的卡頓。
Vim使用稱為"自動加載(autoload)"來解決這個問題。自動加載讓你直到需要時才加載某一部分代碼。 會有一些性能上的損失,但如果用戶不總是需要你的插件的每一行代碼,自動加載將帶來速度上的飛躍。
示范一下它是怎么工作的。看看下面的命令:
~~~
:call somefile#Hello()
~~~
當你執行這個命令,Vim的行為與平常的函數調用有些許不同。
如果這個函數已經加載了,Vim簡單地像平常一樣調用它。
否則Vim將在你的`~/.vim`(或`~/.vim/bundles/對應的插件/autoload`)下查找一個叫做`autoload/somefile.vim`的文件。
如果文件存在,Vim將加載/source文件。接著Vim就會像平常一樣調用它。
在這個文件內,函數應該這樣定義:
~~~
function somefile#Hello()
" ...
endfunction
~~~
你可以在函數名中使用多個`#`來表示子目錄。舉個例子:
~~~
:call myplugin#somefile#Hello()
~~~
這將在`autoload/myplugin/somefile.vim`查找自動加載文件。 里面的函數需要使用自動加載的絕對路徑進行定義:
~~~
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!
~~~
這個小演示證明了幾件事:
1. Vim的確是在半途加載了`example.vim`文件。當我們打開Vim的時候它并不存在,所以不可能是在啟動時加載的。
2. 當Vim找到它需要自動加載的文件后,它在調用對應函數之前就加載了整個文件。
**先不要關閉Vim**,修改函數的定義成這樣:
~~~
echom "Loading..."
function! example#Hello()
echom "Hello AGAIN, world!"
endfunction
echom "Done loading."
~~~
保存文件并**不要關閉Vim**,執行`:call example#Hello()`。Vim將簡單地輸出:
~~~
Hello, world!
~~~
Vim已經有了`example#Hello`的一個定義,所以它不再需要重新加載文件,這意味著:
1. 函數以外的代碼將不再執行。
2. 它不會反映函數本身的變化。
現在執行`:call example#BadFunction()`。你將再一次看到加載信息,伴隨著一個函數不存在的錯誤。 但現在嘗試再次執行`:call example#Hello()`。這次你將看到更新后的信息!
目前為止你應該清晰地了解到Vim會怎么處理一個自動加載類型的函數調用吧:
1. 它首先是否已經存在同名的函數了。如果是,就調用它。
2. 否則,查找名字對應的文件,并source它。
3. 然后試圖調用那個函數。如果成功,太棒了。如果失敗,就輸出一個錯誤。
如果你還是沒有完成弄懂,回到前面重新過一遍演示,注意觀察每條規則生效的地方。
## 自動加載什么
自動加載不是沒有缺陷的。 設置了自動加載后,會有一些(小的)運行開銷,更別說你不得不在你的代碼里容忍丑陋的函數名了。
正因為如此,如果你不是寫一個用戶會在_每次_打開Vim對話時都用到的插件,最好盡量把功能代碼都挪到autoload文件中去。 這將減少你的插件在用戶啟動Vim時的影響,尤其是在人們安裝了越來越多的插件的今天。
所以有什么是可以安全地自動加載?那些不由你的用戶直接調用的部分。 映射和自定義命令不能自動加載(因為它們需要由用戶調用),但別的許多東西都可以。
讓我們看看Potion插件里有什么可以自動加載的。
## 在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 <buffer> <localleader>r :call PotionCompileAndRunFile()<cr>
nnoremap <buffer> <localleader>b :call PotionShowBytecode()<cr>
~~~
這個文件僅僅當Potion文件加載時才會調用,所以它通常不會影響Vim的啟動時間。 但可能會有一些用戶就是不想要這些功能,所以如果我們可以自動加載某些部分, 每次打開Potion文件時可以省下他們以毫秒記的時間。
是的,這種情況下我們不會節省多少。 但你可以想象到可能有那么一個插件包括了數千行可以通過自動加載來減少每次的加載時間的代碼。
讓我們開始吧。在你的插件repo中創建一個`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.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>
~~~
保存文件,關閉Vim,然后打開你的`factorial.pn`文件。嘗試這些映射,確保它們依然正常工作。
確保你僅僅在第一次執行其中一個映射的時候才看到診斷信息`Autoloading...`(你可能需要使用`:message`來看到)。 一旦認為自動加載正常工作,你可以移除那些信息。
正如你看到的,我們保留`nnoremap`映射部分不變。 我們不能自動加載它們,不然用戶就沒辦法引發自動加載了!
你將在Vim插件中普遍看到:大多數的功能將位于自動加載函數中,僅有`nnoremap`和`command`命令每次都被Vim加載。 每次你寫一個有用的Vim插件時,不要忘了這一點。
## 練習
閱讀`:help autoload`
稍微測試一下并弄懂自動加載變量是怎么一回事。
假設你想要強制加載一個Vim已經加載的自動加載文件,并不會驚擾到用戶。 你會怎么做?你可能想要閱讀`:help silent!`(譯注:此處應該是`:help :silent`)。不過在現實生活中請不要那么做。
- 前言
- 鳴謝
- 預備知識
- 打印信息
- 設置選項
- 基本映射
- 模式映射
- 精確映射
- Leaders
- 編輯你的Vimrc文件
- Abbreviations
- 更多的Mappings
- 鍛煉你的手指
- 本地緩沖區的選項設置和映射
- 自動命令
- 本地緩沖區縮寫
- 自動命令組
- Operator-Pending映射
- 更多Operator-Pending映射
- 狀態條
- 負責任的編碼
- 變量
- 變量作用域
- 條件語句
- 比較
- 函數
- 函數參數
- 數字
- 字符串
- 字符串函數
- Execute命令
- Normal命令
- 執行normal!
- 基本的正則表達式
- 實例研究:Grep 運算符(Operator),第一部分
- 實例研究:Grep運算符(Operator),第二部分
- 實例研究:Grep運算符(Operator),第三部分
- 列表
- 循環
- 字典
- 切換
- 函數式編程
- 路徑
- 創建一個完整的插件
- 舊社會下的插件配置方式
- 新希望:用Pathogen配置插件
- 檢測文件類型
- 基本語法高亮
- 高級語法高亮
- 更高級的語法高亮
- 基本折疊
- 高級折疊
- 段移動原理
- Potion段移動
- 外部命令
- 自動加載
- 文檔
- 發布
- 還剩下什么?