My First Neovim Plugin: Neorg Worklog
Published Mon Jul 29 2024Today I finished and released my first Neovim plugin! It is called Neorg Worklog, a external module for Neorg that extends the daily journal module.
I wanted my daily note to include a list of the notes I edited that day. This was surprisingly more difficult than I originally expected, but I am very happy with the result. When set up, the plugin will add notes to your daily journal like this:
* Worklog
** workspace-name
- [metadata title]{:/Absolute/path/to/file.norg:}
** journal
- [2024-07-29]{:/journals/2024-07-29:}
Creating a Neorg Module
While the Neorg docs could use some updating, I found them easier to navigate than the Neovim docs. Thankfully a helpful guide for creating external modules was published on GitHub by Ben Lubas here. My local development workflow could use improvement, I wrote the initial version out of my ~/.local/share/nvim
directory.
Using Treesitter
Treesitter was a big help with getting this functionality to work. I used some simple queries to check for the presence of headings and avoid linking to a file multiple times. nvim-treesitter includes some awesome commands to experiment with treesitter live. :InspectTree
and :EditQuery
are incredibly hepful here. These queries are written as S-expressions, feeling very similar to lisp syntax.
((heading1 title: (paragraph_segment) @title)
(#eq? @title "%s"))
This query selects heading1
nodes with a paragraph_segment
title attribute, providing this title to the @title
capture group. The #eq
predicate is used for comparison, %s
is for the Lua code to interpolate the title we are searching for using string.format
. I use the same query to search for workspace headings, here is the complete Lua code using Neorg’s treesitter module:
local workspace_title_tmpl = [[
((heading2 title: (paragraph_segment) @workspace)
(#eq? @workspace "%s"))
]]
treesitter.execute_query(
string.format(workspace_title_tmpl, workspace),
function(query, id, node, metadata)
local text = treesitter.get_node_text(node)
workspace_title_line = treesitter.get_node_range(node).row_start
end,
bufnr)
The base functionality is complete, but I have a couple changes I want to add eventually:
- Separate modified and created files
- Sort the logged note lists under each workspace
- Log
.norg
files written outside of your Neorg workspaces
My plugin can be installed through the GitHub path: bottd/neorg-worklog
, or if you are super cool you can install it from Luarocks neorg-worklog.
As is I am proud of this v1.0.0, it feels great to contribute a small gift to a community I’ve received so much for free from :)