Why using Python in a Vim plugin
Vimscript is a well-designed language that is really tailored to extend Vim. Unfortunately, this language is not very powerful and quite clunky for general-purpose computing.
Fortunately, it is possible to use other languages in your Vim plugin. For example, Python.
Python is very easy to prototype in, and fast to write and its extensive standard library is a great help when writing Vim plugins. Thus, it is a good choice for our plugins.
Making a Vim plugin
Before making a plugin using Python, let’s remind ourselves how to do a simple plugin. Our plugin will be contained in a directory named with the name of our plugin. For example test_plugin
.
This directory will be in the plugin directory of our plugin manager. For example, with Pathogen, test_plugin
will be in <vim dir>/bundle
. For the rest of this post, the plugin manager’s directory will be written <plugin manager dir>
.
In our plugin’s directory, we will create a directory named plugin
where the Vimscript code is stored. Scripts stored there will be run when the plugin manager loads plugins. There can be other directories such as autoload
but their use is outside of the scope of this post.
Let’s write a small plugin that prints the content of the variable g:myVar
when pressing <F1>
. The variable will be initialized to “abcd”.
<plugin manager dir>/test_plugin/plugin/test_plugin.vim
:
1 | " Variable initialization |
Now, when running Vim, pressing <F1>
will write “abcd” as this is the behavior of the plugin.
Calling a python script in the vim plugin
Let’s now make a very simple script that prints “Hello, world!” when starting Vim. But this will have a quirk, the printing will be made in a Python script. The Python script will be in the plugin
directory of our plugin.
<plugin manager dir>/test_plugin/plugin/hello_world.py
:
1 | print("Hello, world!") |
We will write a Vim script that executes the Python script when starting Vim. The first challenge is to find the path of the python script. This can be done with the Vimscript function expand('<sfile>:p:h')
which finds the directory of the script executing this function. Then, we will use py3file
to run the script.
<plugin manager dir>/test_plugin/plugin/hello_world.vim
:
1 | " Finding the path of the current script |
We are now greeted with a slightly annoying “Hello, world!” when starting Vim. But the annoyance is sweetened by the knowledge that this is executed with Python code.
Vim module
In our Python script, to interact with Vim, we must use the module vim
. This module can do a lot of things that we can read with Vim’s included documentation with :help python-vim
.
Communication between Vimscript and Python
To make a more complex plugin, we will need to exchange data between Vimscript code and Python code. To do so, we need to use Vim’s global variable that can be accessed from Python with vim.vars
.
Let’s improve the script test_plugin.vim
so that it writes twice the content of g:myVar
. We will start by making a Python script that duplicates the content of g:myVar
and write it into g:myBigVar
.
<plugin manager dir>/test_plugin/plugin/hello_world.py
:
1 | import vim |
Then, we will rewrite test_plugin.vim
to use the new Python script.
<plugin manager dir>/test_plugin/plugin/test_plugin.vim
:
1 | " Variables initialization |
Using multiple Python files
For an even more complex script, we will want to have multiple Python files that we use with import
. To do so, we need to add the path of our scripts to Python’s library path list. This can be done by putting the path of the scripts in one of Vim’s global variables.
<plugin manager dir>/test_plugin/plugin/test_plugin.vim
:
1 | " Variables initialization |
We will then put the duplicate_string
function in a separate file.
<plugin manager dir>/test_plugin/plugin/my_lib.py
:
1 | def duplicate_string(s): |
Lastly, we will change script.py
so that it uses the lib.
<plugin manager dir>/test_plugin/plugin/script.py
:
1 | import sys |
Conclusion
We are now ready to write complex Vim plugins with a mix of Vimscript and Python.