详解如何使用Python编写vim插件


Posted in Python onNovember 28, 2017

前言

vim是个伟大的编辑器,不仅在于她特立独行的编辑方式,还在于她强大的扩展能力。然而,vim自身用于写插件的语言vimL功能有很大的局限性,实现功能复杂的插件往往力不从心,而且运行效率也不高。幸好,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。

现在好多平台都有直接编译好的版本,已经包含Python支持,直接下载就可以了:

  1. Windows:可以在这里下载。
  2. Mac OS:可以直接brew install vim来安装。
  3. Linux:也有快捷的安装方式,就不赘言了。

2. 如何让Python能正常工作

虽然vim已经支持Python,但是可能:echo has("python")或:echo has("python3")的结果仍是0,说明Python还不能正常工作。
此时需要检查:

  1. 系统上是否装了Python?
  2. Python是32位还是64位跟vim是否匹配?
  3. Python的版本跟编译时的版本是否一致(编译时的版本可以使用:version查看)
  4. 通过pythondll和pythonthreedll来分别指定Python2和Python3所使用的动态库。

例如,可以在vimrc里添加

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开始

在命令行窗口执行: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模块提供了两个非常有用的函数接口:

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),返回结果类型为:

  1. string: 如果vim表达式的值的类型是string或number
  2. list:如果vim表达式的值的类型是一个vim list(:h list)
  3. dictionary:如果vim表达式的值的类型是一个vim dictionary(:h dict)

例如:

:py sw = vim.eval("&shiftwidth")
:py print vim.eval("expand('%:p')")
:py print vim.eval("@a")

vim模块还提供了一些有用的对象:

  1. Tabpage对象(:h python-tabpage) 一个Tabpage对象对应vim的一个Tabpage。
  2. Window对象(:h python-window) 一个Window对象对应vim的一个Window。
  3. 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}前面不能有任何的空白字符,也就是要顶格写。

例如,写一个函数,打印出当前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的进程(https://github.com/vim/vim/issues/906),计算密集型任务想利用多核来提高性能已不可能。

奇技淫巧

把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。希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
pycharm 使用心得(二)设置字体大小
Jun 05 Python
python中pygame模块用法实例
Oct 09 Python
使用Python脚本将Bing的每日图片作为桌面的教程
May 04 Python
Python中的rfind()方法使用详解
May 19 Python
浅谈Python对内存的使用(深浅拷贝)
Jan 17 Python
使用apidoc管理RESTful风格Flask项目接口文档方法
Feb 07 Python
Python实现的当前时间多加一天、一小时、一分钟操作示例
May 21 Python
详解重置Django migration的常见方式
Feb 15 Python
Numpy 中的矩阵求逆实例
Aug 26 Python
python读取Excel表格文件的方法
Sep 02 Python
python中的函数递归和迭代原理解析
Nov 14 Python
Window版下在Jupyter中编写TensorFlow的环境搭建
Apr 10 Python
从头学Python之编写可执行的.py文件
Nov 28 #Python
浅谈用Python实现一个大数据搜索引擎
Nov 28 #Python
Python中用psycopg2模块操作PostgreSQL方法
Nov 28 #Python
Python搜索引擎实现原理和方法
Nov 27 #Python
python输入错误密码用户锁定实现方法
Nov 27 #Python
动态规划之矩阵连乘问题Python实现方法
Nov 27 #Python
Python基于贪心算法解决背包问题示例
Nov 27 #Python
You might like
通过php快速统计某个数据库中每张表的数据量
2012/09/04 PHP
php setcookie函数的参数说明及其用法
2014/04/20 PHP
关于laravel框架中的常用目录路径函数
2019/10/23 PHP
javascript下function声明一些小结
2007/12/28 Javascript
很多人都是用下面的js刷新站IP和PV
2008/09/05 Javascript
Wordpress ThickBox 点击图片显示下一张图的修改方法
2010/12/11 Javascript
javascript中使用replaceAll()函数实现字符替换的方法
2010/12/25 Javascript
对jQuery的事件绑定的一些思考(补充)
2013/04/20 Javascript
jQuery获取Radio,CheckBox选择的Value值(示例代码)
2013/12/12 Javascript
js中一维数组和二位数组中的几个问题示例说明
2014/07/17 Javascript
jQuery实现的网页左侧在线客服效果代码
2015/10/23 Javascript
JavaScript数组实现数据结构中的队列与堆栈
2016/05/26 Javascript
通过js修改input、select默认字体颜色
2017/04/19 Javascript
发布一款npm包帮助理解npm的使用
2019/01/03 Javascript
Nodejs实现用户注册功能
2019/04/14 NodeJs
Vue项目路由刷新的实现代码
2019/04/17 Javascript
BootstrapValidator实现表单验证功能
2019/11/08 Javascript
在Python中使用__slots__方法的详细教程
2015/04/28 Python
django实现登录时候输入密码错误5次锁定用户十分钟
2017/11/05 Python
python下解压缩zip文件并删除文件的实例
2018/04/24 Python
将pandas.dataframe的数据写入到文件中的方法
2018/12/07 Python
python求平均数、方差、中位数的例子
2019/08/22 Python
Python 面向对象部分知识点小结
2020/03/09 Python
openCV提取图像中的矩形区域
2020/07/21 Python
Python reques接口测试框架实现代码
2020/07/28 Python
Python实现播放和录制声音的功能
2020/08/12 Python
python matplotlib工具栏源码探析二之添加、删除内置工具项的案例
2021/02/25 Python
幼儿园教师个人反思
2014/01/30 职场文书
收银员岗位职责
2014/02/07 职场文书
《乌塔》教学反思
2014/02/17 职场文书
邀请函样本
2015/02/02 职场文书
物业项目经理岗位职责
2015/04/01 职场文书
2016新年年会主持词
2015/07/06 职场文书
2019广播稿怎么写
2019/04/17 职场文书
Mysql数据库中datetime、bigint、timestamp来表示时间选择,谁来存储时间效率最高
2021/08/23 MySQL
宫崎骏十大动画电影,宫崎骏好看的动画电影排名
2022/03/22 日漫