How I Developed My First Neovim Plugin: A Step-by-Step Guide
Gonçalo Alves
Posted on September 19, 2024
Are you a Neovim enthusiast looking to extend its functionality? Ever wondered how to create your own plugin and publish it up on Github? Look no further! In this post, I'll walk you through my journey of developing a simple "Hello World" plugin for Neovim.
For those who want to see the finished product or follow along with the complete source code, you can check out the full repository here:
neovim-plugin-hello-world GitHub Repository
Feel free to star the repository if you find it helpful!
Btw, I'm building a real useful Neovim plugin and I will share how I built this plugin here, so make sure you follow me if you don't wanna miss that.
Introduction
Neovim, a hyper-extensible Vim-based text editor, allows users to create custom plugins to enhance their editing experience. In this tutorial, we'll create a simple plugin that adds a :HelloWorld
command and a keymapping to Neovim, which prints "Hello from Neovim!" when executed.
Setting Up the Development Environment
Before we dive into coding, let's set up our development environment:
- Ensure you have Neovim installed (version 0.5 or later recommended).
- Choose a directory for your plugin development. I used
~/development/neovim/neovim-plugins/
.
Creating the Plugin Structure
Before we dive into coding our specific plugin, it's important to understand the typical structure of a Neovim plugin. This structure follows conventions that determine how and when different parts of the plugin are loaded.
The Anatomy of a Neovim Plugin
A generic Neovim plugin usually has the following structure:
.
├── LICENSE
├── README.md
├── plugin/
│ └── plugin-file.lua
└── lua/
└── plugin-name.lua
Let's break down each component:
-
Root Directory:
-
LICENSE
: The license file for your plugin. -
README.md
: Documentation and usage instructions for your plugin.
-
-
plugin/
Directory:- Files in this directory are automatically executed when Neovim starts.
- This is useful for setting up global commands, autocommands, or keymaps that should be available immediately.
- Example:
plugin/plugin-file.lua
-
lua/
Directory:- This is where the main plugin code resides.
- Code here is only executed when explicitly required by the user or other parts of the plugin.
- There are two common ways to structure the main file:
a. Single file:
lua/plugin-name.lua
b. Directory with init file:lua/plugin-name/init.lua
Key Points About Plugin Structure
The
plugin/
directory is for code that needs to run immediately when Neovim starts, regardless of whether the user explicitly requires the plugin.The
lua/
directory contains the main plugin code, which is only executed when required. This is where most of your plugin's functionality should be implemented.Naming is important. The main file in the
lua/
directory should typically match your plugin's name (e.g.,hello-world.lua
for a plugin named "hello-world").For simple plugins, a single file in the
lua/
directory is often sufficient. For more complex plugins, you might use a directory with aninit.lua
file.The main entry point of your plugin (in the
lua/
directory) is what users will require to use your plugin. For example, users would userequire('hello-world')
to load a plugin with the filelua/hello-world.lua
.
This structure allows for efficient loading of plugin code, with immediate execution of necessary setup code (in plugin/
) and on-demand loading of the main functionality (in lua/
).
Structure for Our Hello World Plugin
For our simple "Hello World" plugin, we'll use the following structure:
neovim-plugin-hello-world/
├── LICENSE
├── README.md
└── lua/
└── neovim-plugin-hello-world.lua
Since our plugin doesn't need any immediate setup, we'll omit the plugin/
directory and focus on the main functionality in the lua/
directory.
Let's create this structure in your chosen development directory:
- Create a new directory for your plugin:
mkdir -p neovim-plugin-hello-world/lua
- Navigate to the plugin directory:
cd neovim-plugin-hello-world
- Create the main Lua file:
touch lua/neovim-plugin-hello-world.lua
- Create a README.md file:
touch README.md
- Add a LICENSE file (choose an appropriate license for your project).
Now that we have our plugin structure set up, we're ready to start writing the actual plugin code.
Writing the Plugin Code
Let's create the main plugin file. Open lua/neovim-plugin-hello-world.lua
in your favorite text editor and add the following code:
-- Main module for the Hello World plugin
local M = {}
-- Function to print the hello message
function M.say_hello()
print("Hello from Neovim!")
end
-- Function to set up the plugin (Most package managers expect the plugin to have a setup function)
function M.setup(opts)
-- Merge user options with defaults
opts = opts or {}
-- Create the user command
vim.api.nvim_create_user_command("HelloWorld", M.say_hello, {})
-- Set up a key mapping
-- Use opts.keymap if provided, otherwise default to '<leader>hw'
local keymap = opts.keymap or '<leader>hw'
-- Create the keymap
vim.keymap.set('n', keymap, M.say_hello, {
desc = "Say hello from our plugin",
silent = true -- Prevents the command from being echoed in the command line
})
end
-- Return the module
return M
Let's break down this code:
- We define a module
M
that will contain all our plugin's functionality. - The
say_hello()
function is the core of our plugin. It simply prints a greeting message. - The
setup()
function is used to initialize our plugin with user-provided options:- It creates a user command
:HelloWorld
that calls oursay_hello()
function. - It sets up a keymapping (default
<leader>hw
) that also callssay_hello()
.
- It creates a user command
- We return the module at the end, making its functions available to users of our plugin.
Debugging Your Plugin
To debug your plugin locally using lazy.nvim
with the dir
option:
Ensure you have
lazy.nvim
installed in your Neovim configuration.Modify your Neovim configuration to load your plugin locally. Add the following to your
init.lua
or wherever you configure your plugins:
require("lazy").setup({
{
"neovim-plugin-hello-world",
dir = "~/development/neovim/neovim-plugins/neovim-plugin-hello-world",
config = function()
require("neovim-plugin-hello-world").setup({
keymap = "<leader>hello" -- optional: override the default keymap
})
end,
},
-- ... your other plugins ...
})
Restart Neovim or run
:Lazy sync
to load your plugin.-
Test your plugin:
- Try running the command
:HelloWorld
- Use the keymapping (default
<leader>hw
or your custom mapping) in normal mode
- Try running the command
You should see "Hello from Neovim!" printed in the command line for both methods.
Iterative Development
With this setup, you can easily iterate on your plugin:
- Make changes to your plugin code.
- Save the files.
- In Neovim, run
:Lazy reload neovim-plugin-hello-world
to reload your plugin. - Test the changes by running
:HelloWorld
or using the keymapping.
This workflow allows for rapid development and testing without constantly restarting Neovim or manually sourcing files.
Installing the Plugin Remotely
When you're ready to share your plugin with the world, you'll need to push it to a GitHub repository. Here's how to make it installable via lazy.nvim
:
- Create a GitHub repository for your plugin.
- Push your plugin code to the repository.
- Update your
README.md
file with installation and usage instructions.
Here's a sample README.md
:
# neovim-plugin-hello-world
A simple Neovim plugin that adds a `:HelloWorld` command and keymapping.
## Installation
Using [lazy.nvim](https://github.com/folke/lazy.nvim):
{
"yourusername/neovim-plugin-hello-world",
opts = {
keymap = "<leader>hello" -- optional: override the default keymap
}
}
## Usage
After installation:
1. Run `:HelloWorld` command in Neovim
2. Or use the keymapping (default `<leader>hw` or your custom mapping) in normal mode
Both will print "Hello from Neovim!" in the command line.
(Don't forget to replace yourusername
with your actual GitHub username.)
Conclusion
Congratulations! You've just created your first Neovim plugin. This simple example demonstrates the basics of plugin development, including structuring your code, creating user commands, setting up keymappings, and making your plugin installable.
As you become more comfortable with plugin development, you can explore more advanced features like autocommands, integrating with Neovim's API, and creating more complex functionality. The possibilities are endless!
Remember, the key to successful plugin development is iterative improvement and testing. With the setup we've created, you can quickly make changes, reload your plugin, and see the results in real-time.
Happy coding, and may your Neovim experience be ever more customized and efficient!
Connect with me
If you reached this far and liked this article be sure to leave a comment. That will make my day!
If you want to connect with me you can send me a message on Twitter/X.
If you want to read other stuff by me you can check out my personal blog.
You can also check other stuff that I have going on here
Posted on September 19, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.