SoFunction
Updated on 2025-03-01

Detailed explanation of how to write vim plug-in using Python

Preface

Vim is a great editor, not only in her unique editing method, but also in her powerful expansion ability. However, vim's own language used to write plug-ins has great limitations. Plug-ins with complex functions are often unable to implement and their operational efficiency is not high. Fortunately, vim has long thought of this. She provides many external language interfaces, such as Python, ruby, lua, Perl, etc., which can easily write vim plug-ins. This article mainly introduces how to write vim plug-ins using Python.

Preparation

1. Compile vim to make vim support Python

Before compiling, add --enable-pythoninterp and --enable-python3interp options when configure to support Python2 and Python3 respectively
After compiling, you can use vim --version | grep +python to check whether Python is already supported. The result should include +python and +python3. Of course, it can also be compiled to only support Python2 or Python3.

Now many platforms have directly compiled versions, which already include Python support, just download it directly:

  1. Windows: You canheredownload.
  2. Mac OS: You can directly brew install vim to install.
  3. Linux: There are also quick installation methods, so I won’t go into details.

2. How to make Python work properly

Although vim already supports Python, the result of :echo has("python") or :echo has("python3") may still be 0, indicating that Python is not working properly.
At this point, you need to check:

  1. Is Python installed on the system?
  2. Does Python match vim?
  3. Is the Python version consistent with the compiled version (the compiled version can be viewed using: version)
  4. pythondll and pythonthreeedll to specify the dynamic libraries used in Python2 and Python3 respectively.

For example, you can add it in vimrc

set pythondll=/Users/yggdroot/.python2.7.6/lib/libpython2.

After these 4 steps, 99% of Python can work, while the remaining 1% depends on character.

One more thing:

For neovim, execute

pip2 install --user --upgrade neovim
pip3 install --user --upgrade neovim

You can add support for Python2 and Python3, see: h provider-python for details.

Start with hello world

Execute: pyx print("hello world!") in the command line window and output "hello world!", indicating that Python is working normally. At this time, we can already use Python as the EX command of vim.

Operating vim is as easy as vimL

How to use Python to access vim's information and operate vim? It's very simple. vim's Python interface provides a module called vim. The vim module is a bridge between Python and vim. Through it, Python can access all information about vim and operate vim, just like using vimL. So, first of all, you need to import vim.

vim module

The vim module provides two very useful function interfaces:

(str)

Execute the command str(ex-mode) in vim, and the return value is None, for example:

:py ("%s/\s\+$//g")
:py ("set shiftwidth=4")
:py ("normal! dd")

(str)

Find the value of vim expression str (what is a vim expression, see: h expr), and the return result type is:

  1. string: If the value of the vim expression is of string or number
  2. list: If the type of the value of the vim expression is a vim list (:h list)
  3. dictionary: If the value of the vim expression is of a vim dictionary (:h dict)

For example:

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

The vim module also provides some useful objects:

  1. Tabpage object (:h python-tabpage) A Tabpage object corresponds to a Tabpage of vim.
  2. Window object (:h python-window) A Window object corresponds to a Window in vim.
  3. Buffer object (:h python-buffer) A Buffer object corresponds to a buffer of vim. The Buffer object provides some properties and methods, which can be very convenient to operate the buffer.

For example (assuming b is the current buffer):

:py print    # 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 ("bottom")  # add a line at the bottom
:py n = len(b)    # number of lines
:py (row,col) = ('a') # named mark
:py r = (1,5)  # a sub-range of the buffer
:py ["foo"] = "bar" # assign b:foo variable
:py ["ff"] = "dos" # set fileformat
:py del ["ar"]  # same as :set autoread<

Object (:h python-current)

The object provides some properties that can easily access the "current" vim object

property meaning type
The current line (RW) String
The current buffer (RW) Buffer
The current window (RW) Window
The current tab page (RW) TabPage
The current line range (RO) Range

python access variables in vim

To access variables in vim, you can access them through the previously introduced (str), for example:

:py print ("v:version")

However, there are more pythonic methods:

Predefined vim variable (v:var)

You can access predefined vim variables, which are an object similar to Dictionary. For example, visit v:version:

:py print ["version"]

Global variable (g:var)

You can access global variables through it, which is also an object similar to Dictionary. For example, change the value of the global variable g:global_var:

:py ["global_var"] = 123

tabpage variable (t:var)

For example:

:py ["var"] = "Tabpage"

window variable (w:var)

For example:

:py ["var"] = "Window"

buffer variable (b:var)

For example:

:py ["var"] = "Buffer"

python access options in vim (options)

To access the options in vim, you can access them through the (str) and (str) mentioned earlier, for example:

:py ("set shiftwidth=4")
:py print ("&shiftwidth")

Of course, there are more pythonic methods:

Global Options Settings (:h python-options)

For example:

:py ["autochdir"] = True

Note: If it is the window-local or buffer-local option, this method will report a KeyError exception. For window-local and buffer-local options, please read down.

window-local option settings

For example:

:py ["number"] = True

Buffer-local option settings

For example:

:py ["shiftwidth"] = 4

Two ways to write vim plug-in

Embedded

py[thon] << {endmarker}
{script}
{endmarker}

The content in {script} is Python code. {endmarker} is a marker symbol, which can be any string. However, there cannot be any blank characters in front of {endmarker}, which means it must be written in the top grid.

For example, write a function to print out all lines of the current buffer ():

function! Demo()
py << EOF
import vim
for line in :
 print line
EOF
endfunction
call Demo()

Run:source % to view the results.

Standalone

Write Python code into *.py, vimL is only used to define global variables, maps, commands, etc., and LeaderF adopts this method. I personally prefer this method and can focus all my energy on writing Python code.

asynchronous

Multi-threaded

Multithreading can be implemented through Python's threading module. However, the thread can only implement logic that is not related to vim. Any attempt to operate vim in the thread may (maybe "certainly" is more appropriate) cause vim to crash, and even include only reading one vim option. Even so, it is much better than vimL, after all, it is better than nothing.

subprocess

External commands can be called through Python's subprocess module.

For example:

:py import subprocess
:py print ("ls -l", shell=True, stdout=).()

In other words, since the support of Python, vim has already supported asynchronous (although there were basically no bugs until vim7.4). Neovim added asynchronous functions are not attractive to those who write plug-ins in Python. Many Neovim fans are proud of introducing asynchronous (jobs). I will only accept it when it can introduce real multithreading support.

Case

Famous completion plugin YCM and fuzzy search toolLeaderFAll are written in Python.

defect

Due to GIL, Python threads cannot be processed in parallel; vim does not support Python processes (/vim/vim/issues/906), it is no longer possible for compute-intensive tasks to use multiple cores to improve performance.

Strange tricks and tricks

Change the first letter of all words in the buffer to capital letters

:%pydo return ()

Mirror all rows in the buffer

For example,

vim is very useful
123 456 789
abc def ghi
who am I

Become

lufesu yrev si miv
987 654 321
ihg fed cba
I ma ohw

This command can be executed: :%pydo return line[::-1]

Summarize

The above is just a brief introduction, and for more detailed information, please refer to: h python. I hope it will be helpful to everyone's learning and I hope everyone will support me more.