詳解如何使用Python編寫vim插件
前言
vim是個偉大的編輯器,不僅在于她特立獨行的編輯方式,還在于她強大的擴展能力。然而,vim自身用于寫插件的語言vimL功能有很大的局限性,實現(xiàn)功能復雜的插件往往力不從心,而且運行效率也不高。幸好,vim早就想到了這一點,她提供了很多外部語言接口,比如Python,ruby,lua,Perl等,可以很方便的編寫vim插件。本文主要介紹如何使用Python編寫vim插件。
準備工作
1. 編譯vim,使vim支持Python
在編譯之前,configure的時候加上--enable-pythoninterp和--enable-python3interp選項,使之分別支持Python2和Python3
編譯好之后,可以通過vim --version | grep +python來查看是否已經支持Python,結果中應該包含+python和 +python3,當然也可以編譯成只支持Python2或Python3。
現(xiàn)在好多平臺都有直接編譯好的版本,已經包含Python支持,直接下載就可以了:
- Windows:可以在這里下載。
- Mac OS:可以直接brew install vim來安裝。
- Linux:也有快捷的安裝方式,就不贅言了。
2. 如何讓Python能正常工作
雖然vim已經支持Python,但是可能:echo has("python")或:echo has("python3")的結果仍是0,說明Python還不能正常工作。
此時需要檢查:
- 系統(tǒng)上是否裝了Python?
- Python是32位還是64位跟vim是否匹配?
- Python的版本跟編譯時的版本是否一致(編譯時的版本可以使用:version查看)
- 通過pythondll和pythonthreedll來分別指定Python2和Python3所使用的動態(tài)庫。
例如,可以在vimrc里添加
set pythondll=/Users/yggdroot/.python2.7.6/lib/libpython2.7.so
經此4步,99%能讓Python工作起來,剩下的1%就看人品了。
補充一點:
對于neovim,執(zhí)行
pip2 install --user --upgrade neovim pip3 install --user --upgrade neovim
就可以添加Python2和Python3的支持,具體參見:h provider-python。
從hello world開始
在命令行窗口執(zhí)行:pyx print("hello world!"),輸出“hello world!”,說明Python工作正常,此時我們已經可以使用Python來作為vim的EX命令了。
操作vim像vimL一樣容易
怎么用Python來訪問vim的信息以及操作vim呢?很簡單,vim的Python接口提供了一個叫vim的模塊(module)。vim模塊是Python和vim溝通的橋梁,通過它,Python可以訪問vim的一切信息以及操作vim,就像使用vimL一樣。所以寫腳本,首先要import vim。
vim模塊
vim模塊提供了兩個非常有用的函數(shù)接口:
vim.command(str)
執(zhí)行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 |
python訪問vim中的變量
訪問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"
python訪問vim中的選項(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
兩種方式寫vim插件
內嵌式
py[thon] << {endmarker} {script} {endmarker}
{script}中的內容為Python代碼,{endmarker}是一個標記符號,可以是任何字符串,不過{endmarker}前面不能有任何的空白字符,也就是要頂格寫。
例如,寫一個函數(shù),打印出當前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模塊來實現(xiàn)多線程。但是,線程里面只能實現(xiàn)與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的進程(https://github.com/vim/vim/issues/906),計算密集型任務想利用多核來提高性能已不可能。
奇技淫巧
把buffer中所有單詞首字母變?yōu)榇髮懽帜?/p>
:%pydo return line.title()
把buffer中所有的行鏡像顯示
例如,把
vim is very useful 123 456 789 abc def ghi who am I
變?yōu)?/p>
lufesu yrev si miv 987 654 321 ihg fed cba I ma ohw
可以執(zhí)行此命令::%pydo return line[::-1]
總結
以上只是簡單的介紹,更詳細的資料可以參考:h python。希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
python+selenium自動化實戰(zhàn)攜帶cookies模擬登陸微博
這篇文章主要介紹了python+selenium自動化實戰(zhàn)攜帶cookies模擬登陸微博,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01簡單介紹一下pyinstaller打包以及安全性的實現(xiàn)
這篇文章主要介紹了簡單介紹一下pyinstaller打包以及安全性的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-06-06Python中np.percentile和df.quantile分位數(shù)詳解
分位數(shù)(Quantile)亦稱分位點是指將一個隨機變量的概率分布范圍分為幾個等份的數(shù)值點,下面這篇文章主要給大家介紹了關于Python中np.percentile和df.quantile分位數(shù)的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-05-05基于Pydantic封裝的通用模型在API請求驗證中的應用詳解
這篇文章主要介紹了基于Pydantic封裝的通用模型在API請求驗證中的應用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2023-05-05