vim是个伟大的编辑器,不仅在于她特立独行的编辑方式,还在于她强大的扩展能力。然而,vim自身用于写插件的语言vimL功能有很大的局限性,实现功能复杂的插件往往力不从心,而且运行效率也不高。幸好,vim早就想到了这一点,她提供了很多外部语言接口,比如Python,ruby,lua,Perl等,可以很方便的编写vim插件。本文主要介绍如何使用Python编写vim插件。
在编译之前,
的时候加上
- configure
和
- --enable-pythoninterp
选项,使之分别支持Python2和Python3编译好之后,可以通过
- --enable-python3interp
来查看是否已经支持Python,结果中应该包含
- vim --version | grep +python
和
- +python
,当然也可以编译成只支持Python2或Python3。
- +python3
现在好多平台都有直接编译好的版本,已经包含Python支持,直接下载就可以了:
来安装。
- brew install vim
虽然vim已经支持Python,但是可能
或
- :echo has("python")
的结果仍是
- :echo has("python3")
,说明Python还不能正常工作。此时需要检查:
- 0
查看)
- :version
和
- pythondll
来分别指定Python2和Python3所使用的动态库。
- pythonthreedll
- set pythondll=/Users/yggdroot/.python2.7.6/lib/libpython2.7.so
经此4步,99%能让Python工作起来,剩下的1%就看人品了。
补充一点:
对于neovim,执行
- pip2 install --user --upgrade neovim
- pip3 install --user --upgrade neovim
就可以添加Python2和Python3的支持,具体参见
。
- :h provider-python
在命令行窗口执行
,输出“hello world!”,说明Python工作正常,此时我们已经可以使用Python来作为vim的
- :pyx print("hello world!")
命令了。
- EX
怎么用Python来访问vim的信息以及操作vim呢?很简单,vim的Python接口提供了一个叫vim的模块(module)。vim模块是Python和vim沟通的桥梁,通过它,Python可以访问vim的一切信息以及操作vim,就像使用vimL一样。所以写脚本,首先要
。
- import vim
vim模块提供了两个非常有用的函数接口:
执行vim中的命令
- vim.command(str)
(ex-mode),返回值为None,例如:
- str
- :py vim.command("%s/\s\+$//g")
- :py vim.command("set shiftwidth=4")
- :py vim.command("normal! dd")
求vim表达式
- vim.eval(str)
的值,(什么是vim表达式,参见
- str
),返回结果类型为:
- :h expr
: 如果vim表达式的值的类型是
- string
或
- string
- number
:如果vim表达式的值的类型是一个vim list(
- list
)
- :h list
:如果vim表达式的值的类型是一个vim dictionary(
- dictionary
)
- :h dict
- :py sw = vim.eval("&shiftwidth")
- :py print vim.eval("expand('%:p')")
- :py print vim.eval("@a")
vim模块还提供了一些有用的对象:
对象(
- Tabpage
)
- :h python-tabpage
对象对应vim的一个Tabpage。
- Tabpage
对象(
- Window
)
- :h python-window
对象对应vim的一个Window。
- Window
对象(
- Buffer
)一个
- :h python-buffer
对象对应vim的一个buffer,Buffer对象提供了一些属性和方法,可以很方便操作buffer。例如 (假定
- Buffer
是当前的buffer) :
- b
- :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对象
- vim.current
属性 | 含义 | 类型 |
---|---|---|
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的方法:
)可以通过
- v:var
来访问预定义vim变量,
- vim.vvars
是个类似
- 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
)例如:
- t:var
- : py vim.current.tabpage.vars["var"] = "Tabpage"
)例如:
- w:var
- : py vim.current.window.vars["var"] = "Window"
)例如:
- 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}
中的内容为Python代码,
- {script}
是一个标记符号,可以是任何字符串,不过
- {endmarker}
前面不能有任何的空白字符,也就是要顶格写。
- {endmarker}
):
- Demo.vim
运行
- function! Demo()
- py << EOF
- import vim
- for line in vim.current.buffer:
- print line
- EOF
- endfunction
- call Demo()
查看结果。
- :source %
中,vimL只用来定义全局变量、map、command等,LeaderF就是采用这种方式。个人更喜欢这种方式,可以把全部精力集中在写Python代码上。
- *.py
模块来实现多线程。但是,线程里面只能实现与vim无关的逻辑,任何试图在线程里面操作vim的行为都可能(也许用“肯定会”更合适)导致vim崩溃,甚至包括只读一个vim选项。虽然如此,也比vimL好多了,毕竟聊胜于无。
- threading
模块来调用外部命令。例如:
- subprocess
也就是说,从支持Python起,vim就已经支持异步了(虽然直到vim7.4才基本没有bug),Neovim所增加的异步功能,对用Python写插件的小伙伴来说,没有任何吸引力。好多Neovim粉竟以引入异步(job)而引以为傲,它什么时候能引入真正的多线程支持我才会服它。
- :py import subprocess
- :py print subprocess.Popen("ls -l", shell=True, stdout=subprocess.PIPE).stdout.read()
著名的补全插件YCM和模糊查找神器LeaderF都是使用Python编写的。
由于GIL的原因,Python线程无法并行处理;而vim又不支持Python的进程(github.com/vim/vim/iss… ),计算密集型任务想利用多核来提高性能已不可能。
- :%pydo return line.title()
变为
- 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
来源: https://juejin.im/post/5a1ceab76fb9a044fb077e76