如何使用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

相关文章
相关标签/搜索