如何使用Python編寫vim插件

前言

vim是個偉大的編輯器,不只在於她特立獨行的編輯方式,還在於她強大的擴展能力。然而,vim自身用於寫插件的語言vimL功能有很大的侷限性,實現功能複雜的插件每每力不從心,並且運行效率也不高。幸虧,vim早就想到了這一點,她提供了不少外部語言接口,好比Python,ruby,lua,Perl等,能夠很方便的編寫vim插件。本文主要介紹如何使用Python編寫vim插件。python

準備工做

1. 編譯vim,使vim支持Python

在編譯以前,configure的時候加上--enable-pythoninterp--enable-python3interp選項,使之分別支持Python2和Python3
編譯好以後,能夠經過vim --version | grep +python來查看是否已經支持Python,結果中應該包含+python+python3,固然也能夠編譯成只支持Python2或Python3。git

如今好多平臺都有直接編譯好的版本,已經包含Python支持,直接下載就能夠了:github

  • Windows:能夠在這裏下載。
  • Mac OS:能夠直接brew install vim來安裝。
  • Linux:也有快捷的安裝方式,就不贅言了。

2. 如何讓Python能正常工做

雖然vim已經支持Python,可是可能:echo has("python"):echo has("python3")的結果還是0,說明Python還不能正常工做。
此時須要檢查:shell

  1. 系統上是否裝了Python?
  2. Python是32位仍是64位跟vim是否匹配?
  3. Python的版本跟編譯時的版本是否一致(編譯時的版本可使用:version查看)
  4. 經過pythondllpythonthreedll來分別指定Python2和Python3所使用的動態庫。
    例如,能夠在vimrc裏添加
    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-pythonbash

從hello world開始

在命令行窗口執行: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 vimapp

vim模塊

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表達式的值的類型是stringnumber
    • 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-localbuffer-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}前面不能有任何的空白字符,也就是要頂格寫。
    例如,寫一個函數,打印出當前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

相關文章
相關標籤/搜索