vim是個偉大的編輯器,不只在於她特立獨行的編輯方式,還在於她強大的擴展能力。然而,vim自身用於寫插件的語言vimL功能有很大的侷限性,實現功能複雜的插件每每力不從心,並且運行效率也不高。幸虧,vim早就想到了這一點,她提供了不少外部語言接口,好比Python,ruby,lua,Perl等,能夠很方便的編寫vim插件。本文主要介紹如何使用Python編寫vim插件。python
在編譯以前,configure
的時候加上--enable-pythoninterp
和--enable-python3interp
選項,使之分別支持Python2和Python3
編譯好以後,能夠經過vim --version | grep +python
來查看是否已經支持Python,結果中應該包含+python
和 +python3
,固然也能夠編譯成只支持Python2或Python3。git
如今好多平臺都有直接編譯好的版本,已經包含Python支持,直接下載就能夠了:github
brew install vim
來安裝。雖然vim已經支持Python,可是可能:echo has("python")
或:echo has("python3")
的結果還是0
,說明Python還不能正常工做。
此時須要檢查:shell
:version
查看)pythondll
和pythonthreedll
來分別指定Python2和Python3所使用的動態庫。set pythondll=/Users/yggdroot/.python2.7.6/lib/libpython2.7.so
經此4步,99%能讓Python工做起來,剩下的1%就看人品了。vim
補充一點:
對於neovim,執行ruby
pip2 install --user --upgrade neovim
pip3 install --user --upgrade neovim複製代碼
就能夠添加Python2和Python3的支持,具體參見:h provider-python
。bash
在命令行窗口執行:pyx print("hello world!")
,輸出「hello world!」,說明Python工做正常,此時咱們已經可使用Python來做爲vim的EX
命令了。多線程
怎麼用Python來訪問vim的信息以及操做vim呢?很簡單,vim的Python接口提供了一個叫vim的模塊(module)。vim模塊是Python和vim溝通的橋樑,經過它,Python能夠訪問vim的一切信息以及操做vim,就像使用vimL同樣。因此寫腳本,首先要import vim
。app
vim模塊提供了兩個很是有用的函數接口:python2.7
vim.command(str)
執行vim中的命令str
(ex-mode),返回值爲None,例如:
:py vim.command("%s/\s\+$//g")
:py vim.command("set shiftwidth=4")
:py vim.command("normal! dd")複製代碼
vim.eval(str)
求vim表達式str
的值,(什麼是vim表達式,參見:h expr
),返回結果類型爲:
string
: 若是vim表達式的值的類型是string
或number
list
:若是vim表達式的值的類型是一個vim list(:h list
)dictionary
:若是vim表達式的值的類型是一個vim dictionary(:h dict
)例如:
:py sw = vim.eval("&shiftwidth")
:py print vim.eval("expand('%:p')")
:py print vim.eval("@a")複製代碼
vim模塊還提供了一些有用的對象:
Tabpage
對象(:h python-tabpage
)Tabpage
對象對應vim的一個Tabpage。Window
對象(:h python-window
)Window
對象對應vim的一個Window。Buffer
對象(:h python-buffer
)
一個Buffer
對象對應vim的一個buffer,Buffer對象提供了一些屬性和方法,能夠很方便操做buffer。
例如 (假定b
是當前的buffer) :
:py print b.name # write the buffer file name
:py b[0] = "hello!!!" # replace the top line
:py b[:] = None # delete the whole buffer
:py del b[:] # delete the whole buffer
:py b[0:0] = [ "a line" ] # add a line at the top
:py del b[2] # delete a line (the third)
:py b.append("bottom") # add a line at the bottom
:py n = len(b) # number of lines
:py (row,col) = b.mark('a') # named mark
:py r = b.range(1,5) # a sub-range of the buffer
:py b.vars["foo"] = "bar" # assign b:foo variable
:py b.options["ff"] = "dos" # set fileformat
:py del b.options["ar"] # same as :set autoread<複製代碼
vim.current
對象(:h python-current
)vim.current
對象提供了一些屬性,能夠方便的訪問「當前」的vim對象
屬性 | 含義 | 類型 |
---|---|---|
vim.current.line | The current line (RW) | String |
vim.current.buffer | The current buffer (RW) | Buffer |
vim.current.window | The current window (RW) | Window |
vim.current.tabpage | The current tab page (RW) | TabPage |
vim.current.range | The current line range (RO) | Range |
訪問vim中的變量,能夠經過前面介紹的vim.eval(str)
來訪問,例如:
:py print vim.eval("v:version")複製代碼
可是, 還有更pythonic的方法:
預約義vim變量(v:var
)
能夠經過vim.vvars
來訪問預約義vim變量,vim.vvars
是個相似Dictionary
的對象。例如,訪問v:version
:
:py print vim.vvars["version"]複製代碼
全局變量(g:var
)
能夠經過vim.vars
來訪問全局變量,vim.vars
也是個相似Dictionary
的對象。例如,改變全局變量g:global_var
的值:
:py vim.vars["global_var"] = 123複製代碼
tabpage變量(t:var
)
例如:
:py vim.current.tabpage.vars["var"] = "Tabpage"複製代碼
window變量(w:var
)
例如:
:py vim.current.window.vars["var"] = "Window"複製代碼
buffer變量(b:var
)
例如:
:py vim.current.buffer.vars["var"] = "Buffer"複製代碼
options
)訪問vim中的選項,能夠經過前面介紹的vim.command(str)
和vim.eval(str)
來訪問,例如:
:py vim.command("set shiftwidth=4")
:py print vim.eval("&shiftwidth")複製代碼
固然, 還有更pythonic的方法:
全局選項設置(:h python-options
)
例如:
:py vim.options["autochdir"] = True複製代碼
注意:若是是window-local
或者buffer-local
選項,此種方法會報KeyError
異常。對於window-local
和buffer-local
選項,請往下看。
window-local
選項設置
例如:
:py vim.current.window.options["number"] = True複製代碼
buffer-local
選項設置
例如:
:py vim.current.buffer.options["shiftwidth"] = 4複製代碼
內嵌式
py[thon] << {endmarker}
{script}
{endmarker}複製代碼
{script}
中的內容爲Python代碼,{endmarker}
是一個標記符號,能夠是任何字符串,不過{endmarker}
前面不能有任何的空白字符,也就是要頂格寫。
例如,寫一個函數,打印出當前buffer全部的行(Demo.vim
):
function! Demo()
py << EOF
import vim
for line in vim.current.buffer:
print line
EOF
endfunction
call Demo()複製代碼
運行:source %
查看結果。
獨立式
把Python代碼寫到*.py
中,vimL只用來定義全局變量、map、command等,LeaderF就是採用這種方式。我的更喜歡這種方式,能夠把所有精力集中在寫Python代碼上。
多線程
能夠經過Python的threading
模塊來實現多線程。可是,線程裏面只能實現與vim無關的邏輯,任何試圖在線程裏面操做vim的行爲均可能(也許用「確定會」更合適)致使vim崩潰,甚至包括只讀一個vim選項。雖然如此,也比vimL好多了,畢竟聊勝於無。
subprocess
能夠經過Python的subprocess
模塊來調用外部命令。
例如:
:py import subprocess
:py print subprocess.Popen("ls -l", shell=True, stdout=subprocess.PIPE).stdout.read()複製代碼
也就是說,從支持Python起,vim就已經支持異步了(雖然直到vim7.4才基本沒有bug),Neovim所增長的異步功能,對用Python寫插件的小夥伴來講,沒有任何吸引力。好多Neovim粉竟以引入異步(job)而引覺得傲,它何時能引入真正的多線程支持我纔會服它。
著名的補全插件YCM和模糊查找神器LeaderF都是使用Python編寫的。
因爲GIL的緣由,Python線程沒法並行處理;而vim又不支持Python的進程(github.com/vim/vim/iss… ),計算密集型任務想利用多核來提升性能已不可能。
把buffer中全部單詞首字母變爲大寫字母
:%pydo return line.title()複製代碼
把buffer中全部的行鏡像顯示
例如,把
vim is very useful
123 456 789
abc def ghi
who am I複製代碼
變爲
lufesu yrev si miv
987 654 321
ihg fed cba
I ma ohw複製代碼
能夠執行此命令::%pydo return line[::-1]
以上只是簡單的介紹,更詳細的資料能夠參考:h python
。