Photo by Tangerine Chan on Unsplash

Write a (Neo)vim plugin using Python

Fabio Salvatori
TUI Tech Blog
Published in
4 min readJun 10, 2022

--

The aim of this article is to create a Vim plugin that will open a test class file (if it exists) related to the current open file.

What is Vim?

Vim is a highly configurable text editor built to make creating and changing any kind of text very efficient. It is included as “vi” with most UNIX systems and with Apple OS X.

What is Neovim?

Neovim is a continuation and extension of Vim.

Get Neovim and the Python provider

To get started you need to install Neovim: https://github.com/neovim/neovim/wiki/Installing-Neovim

You also have to install Python: https://www.python.org/downloads/

In order to have support for Python’s plugins in Vim, you will also need to install pynvim: https://github.com/neovim/pynvim

Once everything is installed, open Vim and run the following command:

:checkhealth

The command will confirm if you have the correct runtimes for the provider:

checkhealth result for python3 provider

If our provider installation is OK, we are good to go.

What is a plugin in Neovim?

A plugin is a way to extend the functionality of Neovim.

It helps you organize your Neovim functions. Even if you only ever keep the plugin for yourself, adapting your more complex Neovim code into a plugin can help you keep your configurations more organized and maintainable.

You can also easily share your Neovim scripts and tools with a wider community. Plugins are the preferred way to distribute your Vim and Neovim code for others to use. Hosting your plugin on GitHub makes your plugin accessible to others.

Crafting the plugin

Let’s explore Neovim plugins with an example.
I want to create a functionality that lets me find a test class file (if it exists) related to the current file I have open.
For PHP files the convention is to name the test file by adding “Test” to the end of the file name, for example:

╔═════════════════╦═════════════════════╗
║ class name ║ file name ║
╠═════════════════╬═════════════════════╣
║ BestService ║ BestService.php ║
║ BestServiceTest ║ BestServiceTest.php ║
╚═════════════════╩═════════════════════╝

Let’s start by creating our project with the following directory structure:

├── doc
│ └── vim-find-test.txt
├── plugin
│ └── vim_find_test.vim
├── python3
│ └── vim_find_test
│ ├── __init__.py
│ └── finder.py
├── LICENSE
└── README.md
  • plugin: Contains the main script files. Files with the .vim extension in this directory will be sourced.
  • python3: Contains Python files for our plugins.
  • LICENSE: Contains the license for the plugin :)
  • doc: Contains plugin documentation files.

Let’s look at the main plugin file, vim_find_test.vim:

Lines 1–4: Check if Vim has support for Python 3. If it doesn’t, print a message informing the user that the plugin can’t be loaded.

Line 6: Import the finder module from the vim_find_test package.

Line 7: Assign the Finder object to the variable finder (we’ll do more with this in a moment).

Line 9: Define a command named FindTest that will run the find method of the finder object.

The finder.py file contains the following code:

Line 1: Import the Vim package in order to allow the Python script to interact with Vim.

Line 2: Import the os package, useful for working with operating system dependent functionality.

Line 7: Define the method find which is called by the FindTest Vim command in vim_find_test.vim.

Lines 8–10: Check if the current file’s type is supported and print a message if it is not. We are going to support just PHP files for the moment.

Line 12: Get the current file name.

Line 13: Get the current project directory, the one that contains the .git directory.

Lines 17–22: Cycle through the files inside the project directory, searching for the current filename with “Test” at the end. If such a file exists build the path to the file.

Line 21: If the test file was found, split the current Vim window vertically into two and open the test file.

Lines 24–25: If no test file was found, print a message to the user.

Now we need to load the plugin when we start Neovim.
I personally use vim-plug to manage plugins.
After you’ve installed vim-plug, add your plugin to init.vim:

call plug#begin()

Plug 'fabiosal/vim-find-test'
call plug#end()

You can find the init.vim file, depending on your operating system, in one of the following directories:

  Unix  ~/.config/nvim/init.vim
Windows ~/AppData/Local/nvim/init.vim

We’re almost done! Save the init.vim file, source it and execute the following command to install it:

:PlugInstall

Now you’re ready to use your plugin!
Simply execute the FindTest command:

vim-find-test demonstration

Conclusion

We went through the process of creating a Neovim plugin using Python, looking at:

  • How to set up our editor installing Neovim, Python and pynvim
  • The directory structure of the plugin
  • The main plugin .vim file and how to import the Python script
  • The Python code and how it comunicates with Neovim
  • How to import the new plugin with a plugin manager and use it

Thank you for reading this and I’ll meet you in the next article.

If you are looking for new job opportunities, check out what we’re offering at TUI Musement:
https://medium.com/tuitech/join-our-team-in-tui-musement-ad2e51cfd722

Github repository

https://github.com/fabiosal/vim-find-test

More information

https://vim.fandom.com/wiki/How_to_write_a_plugin
https://vimhelp.org/if_pyth.txt.html
https://www.linode.com/docs/guides/writing-a-vim-plugin

--

--