*I remember watching a scene from Death Becomes Her. Meryl Streep
finishes her drink of Elixir and Isabella Rosellini tells her,
"Now a warning. Take care of your body. You and your body will be
together a very long time."*
As a software engineer, I always pay attention to what I consider is one of the
essential "tools of the trade", my fingers. I know one day my code writing
endurance will not be as long as they are today, but I'll be sad if I can't
write code because I did not take care of my body. The decision to write in VIM
and now NVIM was fueled by that same desire for lengevity. So when I was
writing my second post, I thought to myself, if I'll be blogging consistently,
I should make it as convenient and condusive as possible.
I built my blog using fastapi as a backend and react as a
frontend. I was already using OpenID Connect to authenticate
readers and myself when they want to comment and when I want to
write or update posts. If I were to write an NVIM plugin, I'll
use the same authentication system to secure access and refresh
tokens, and use those tokens to create or update posts, but this
time it'll be from an NVIM session rather than React.
## Plugin folder structure
You can pick any folder where you want to use to do your development. I chose
to use `~/nvim-python.nvim` folder for mine. From there you'll need
to have a `python3` or `python` folder within a `rplugin` folder and the python
file with your python nvim plugin inside. Like this:
```
~/nvim-python.nvim/
└── rplugin
└── python3
└── nvim_python.py
```
## NVIM Python Provider
Before we can continue, there are a few things you'll need to do
before NVIM can recognize your python plugin. You'll need to make sure you have
a `python provider in NVIM`. To find out if you are already set up, run
this in nvim:
```vim
:checkhealth vim.provider
```
You're supposed to see something like this;
```
...
Python 3 provider (optional)
- pyenv: Path: /opt/homebrew/Cellar/pyenv/2.4.10/libexec/pyenv
- pyenv: $PYENV_ROOT is not set. Infer from pyenv root.
- pyenv: Root: ~/.pyenv
- Using: g:python3_host_prog = "~/nvimvenv/venv/bin/python"
- Executable: ~/nvimvenv/venv/bin/python
- Python version: 3.11.9
- pynvim version: 0.5.2
- ✅ OK Latest pynvim is installed.
Python virtualenv
- ✅ OK no $VIRTUAL_ENV
...
```
If NVIM says python provider is not available, you'll need to set it up. The steps
are:
1. Create a `virtual env` that nvim will use whenever it runs any python plugin
2. Point NVIM to this `virtual env`
3. Install important modules in this `virtual env`
For step 1, I created the `virtual env` in `~/nvimvenv/`. You can choose a
different location, but you'll need to remember where the location is. If you
are copying me, you can just run:
```bash
python3 -m venv ~/nvimvenv/
```
And then you'll need to point NVIM to this location. To do that add this to
your nvim setup.
```lua
-- set python plugin virtualenv
vim.g.python3_host_prog = "~/nvimvenv/venv/bin/python"
```
And then, you'll need to add one important package to the `virtual env`. To do
so you'll need to activate the `virtual env` and pip install `pynvim`. Like
this:
```bash
. ~/nvimvenv/venv/bin/activate
pip install pynvim
```
Every time there is a module you need to use with your python nvim plugin,
you'll need to install it in this `virtual env`. Installing it outside might
work, but it'll dirty your system's setup.
## How NVIM interacts with python3 plugins
Now that that setup is done, you can tell NVIM to start loading your python
plugins. The python NVIM plugins are python modules in
`rplugin/python` or `rplugin/python3` folders with classes, and methods that
tagged with either `function` or `command` decorators. These decorators will
later tell `NVIM` that those functions or methods need to be syncronized. For
example, you could put this in your python nvim plugin module.
```python
# file nvim_python.py
import pynvim
@pynvim.plugin
class Main(object):
def __init__(self, nvim):
self.nvim = nvim
@pynvim.function('FooPython')
def foo_python(self, args):
self.nvim.command('echo "hello from foo python"')
```
Notice from the example above how I have the `@pynvim.function` decorator. Then
run `:UpdateRemotePlugins`.
When you run `:UpdateRemotePlugins`, that command tells `NVIM` to go through all
available remote plugins, and register all `commands` and `functions`. Running
that command might give you a message similar to this:
```nvim
:UpdateRemotePlugins
remote/host: python3 host registered plugins ['nvim_python.py']
remote/host: generated rplugin manifest: ~/.local/share/nvim/rplugin.vim
```
The `rplugin.vim` file contains a registry of methods that are synced after the
`:UpdateRemotePlugins` run.
After running `:UpdateRemotePlugins`, you can test if it worked by running:
`:call FooPython()`. If it worked you should see a message that says "hello
from foo python".
I do think it is clunky to have to run a command just to tell NVIM to look at
the python rplugins. For this reason alone, I have thought of re-writing this
plugin into pure lua, but I'm not that frustrated yet.
## To be continued
In part 2, I'll discuss how how I authenticate myself and how I create posts.