Open-source project
bennypowers/nvim-regexplainer avatar
bennypowers/nvim-regexplainer

nvim-regexplainer: describe the regular expression under the cursor

Describe the regexp under the cursor

757 stars7 forksLuaGPL-3.0

At a glance

What is it?
nvim-regexplainer is a Neovim plugin that takes the regular expression your cursor is sitting in and explains it, either as an indented narrative of the pattern's structure or as a railroad diagram rendered in a terminal image. It parses the pattern with tree-sitter, supports nine host languages, and needs Neovim 0.11 or newer.
Who is it for?
nvim-regexplainer fits a Neovim user who writes regular expressions by hand and wants to understand one without leaving the editor, particularly one that inherited a pattern from a colleague or from a config file. It does not fit a user on a Neovim older than 0.11, and the graphical mode does not fit a terminal that cannot display images.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Lua, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Explain the pattern where the cursor is

nvim-regexplainer describes the regular expression under the cursor, and the whole plugin exists to answer one question in one keystroke: what does this pattern actually match.

The idea is small and the reason it is useful is that regular expressions are exactly the kind of code you did not write and cannot easily hold in your head. A pattern in a configuration file, a pattern in a search across a large codebase, a pattern somebody pasted into a ticket. Reading it as a wall of metacharacters is slow, and reading it in a diagram or an indented outline is fast.

The plugin is written in Lua, licensed under GPL-3.0, and not archived, with the last push on 2026-04-14. The releases are v2.0.0 on the same day, v1.1.0 in September 2025 whose release note says Regexp Railroad Diagrams, and v1.0.0 in May 2024, so the graphical mode arrived in the 1.1 line and the 2.0 release is the current major.

The plugin is inspired by the venerable atom-regexp-railroad, which is a good sign, because the atom package was the tool most people reached for when they wanted to see a pattern as a picture, and it was a package rather than an editor feature.

There is a hard requirement, stated in a note of its own: it requires Neovim 0.11 or newer. That is a recent floor, and it is worth knowing before you install, because the plugin uses the newer package management interface rather than a plugin manager you already have set up.

The installation is the modern one, with the plugin and its optional companion added together.

lua
vim.pack.add {
  'https://github.com/bennypowers/nvim-regexplainer',
  'https://github.com/edluffy/hologram.nvim', -- for graphical mode
}

The second entry is the image display plugin that the graphical mode needs, and it is only required if you want that mode.

Tree-sitter is the parser, and it sets the language list

nvim-regexplainer requires the regex tree-sitter parser, as well as the parser for the host language, and the list of supported languages follows from that.

The supported languages are JavaScript and TypeScript, HTML, Ruby, Python, Go, Rust, PHP, Java, and C#. The reason it is a list of nine and not every language is that tree-sitter has a grammar for the regex constructs in each language, and the differences between them are real. A regular expression in a JavaScript string literal, a Python raw string, and a Go backtick literal are not the same text, and the plugin has to find the pattern in the host syntax before it can explain it.

That dependency has a practical consequence. If the parser for your language is missing, the plugin cannot find the pattern at all, and the symptom is a plugin that does nothing rather than an error. The README gives the check for exactly that case, the health check command for the plugin, which verifies the tree-sitter setup.

The activation list in the configuration mirrors the same list of filetypes, and it is the other half of the setup. The default covers html, javascript, javascriptreact, typescript, typescriptreact, ruby, python, go, rust, php, java, and cs, which is the same set of languages with the framework variants of JavaScript and TypeScript spelled out separately.

The auto option is the other setting that decides when the plugin acts. When it is off, the explainer appears when you ask for it. When it is on, the popup displays automatically whenever the cursor moves inside a regular expression, which is convenient and, in a large file, noisy.

Narrative mode, and an option name that is spelled two ways

The default mode is narrative, and the output is an indented outline of the pattern with the capture groups shown as nesting.

The rendering is controlled by the narrative indentation string, and the README is explicit that it can be a function taking the current component and returning an indentation indicator string, with the example given showing the capture group on each line by returning the capture depth followed by a marker.

lua
narrative = {
  indentation_string = function(component)
    return component.capture_depth .. '>  '
  end
},

The example input is a nested pattern with three capture groups, and the example output is the outline it produces, with each group labelled and indented one level deeper than the last.

markdown
`zero`
capture group 1:
1>  `one`
1>  capture group 2:
1>  2>  `two`
1>  2>  capture group 3:
1>  2>  3>  `three`

There is an inconsistency in the documentation that will cost you an afternoon if you hit it, and it is worth naming because the two spellings are one character apart. The prose describing the option calls it the indendation string, and the defaults block in the configuration uses that spelling with the value set to a marker and a comment noting the default. The worked example later in the document uses the correct spelling, indentation string, with the function form.

So one of the two is a typo in the prose, and which one your installed version expects is the thing to check. The safest approach is to set the option the way the function example does, since that is the one that comes with a body, and then confirm the behaviour matches the example output.

Graphical mode, and the Python dependency it brings

Graphical mode is the feature that makes the plugin feel like the old atom package: the pattern is rendered as a railroad diagram, in a terminal image, with the same structure you would see in a textbook.

The requirements are three. The hologram plugin for image display, a supported terminal, with Kitty and iTerm2 named as the examples, and Python 3.7 or newer, with the dependencies managed automatically.

The mode is switched in the configuration, and both the popup and the split display work with it.

lua
require'regexplainer'.setup {
  mode = 'graphical',
  -- Both popup and split modes work with graphical display
  display = 'popup', -- or 'split'
  
  graphical = {
    -- Optional: customize image generation
    generation_width = 1200,   -- Initial generation width (default: 1200)
    generation_height = 800,   -- Initial generation height (default: 800)
  },
  
  deps = {
    auto_install = true, -- automatically install Python dependencies
  },
}

The feature list behind that configuration is more interesting than it looks. The diagrams come from the railroad diagrams library. Images are cached, so the second look at the same pattern is fast. The Python dependencies are managed in an isolated environment, with an auto install option, a python command that is auto detected, a generated virtual environment path, and a dependency check interval of 3600 seconds, which is the mechanism that keeps a long lived environment from drifting.

Two behaviours are worth planning for. The images scale to fit the window while preserving the aspect ratio, which means a very wide pattern produces a very wide image. And there is a graceful fallback: if the graphics are unavailable, the plugin falls back to narrative mode rather than showing an error, so a user without a compatible terminal still gets an explanation.

The dependency management section continues past what is available here, and it is the part to read before enabling the mode on a machine you care about, because installing Python packages automatically is a decision your user did not make.

Popup, split, and the commands

There are two display modes, and the choice between them is a matter of what you are doing.

The split mode shows the explainer in a window below the editor. The window is reused, so it does not open a new one for every pattern, and it has the filetype Regexplainer, which means you can hook filetype specific autocommands onto it.

The popup mode is the default, and it shows the explainer in a popup below the cursor. When the cursor moves, the popup closes, which is the behaviour that stops the popup from covering the code you are trying to read. If the auto option is set, the popup instead displays automatically whenever the cursor moves inside a regular expression.

You can override the configured display for one call by passing a display type to the show function.

lua
require'regexplainer'.show { display = 'split' }

There are equivalent commands for the same thing, the show split and show popup commands, and the hide and toggle commands as well. The toggle is bound by default to the mapping in the configuration, and the configuration file shows a set of alternative bindings as examples rather than defaults, which is a sensible way to document a mapping table.

The popup window itself is customisable through the popup options, and the border is a nested table, so a different border can be set depending on the state of the editor.

lua
require'regexplainer'.show {
  display = 'popup',
  popup = {
    border = {
      padding = { 1, 2 },
      style = 'solid',
    },
  },
}

The yank feature is the one to know about if you are reviewing a pattern with someone else. You can yank the explanation into any register with the yank function, the default register being the unnamed one, and the argument is either a string naming the register or a table with the register and options such as the mode and the narrative settings. To put the explanation on the system clipboard you use the plus register, either as a string or as a table, and there is a command for it as well.

lua
require'regexplainer'.yank'+'
lua
require'regexplainer'.yank { register = '+' }

That is a small thing with a large use. An explanation of a pattern pasted into a chat message answers the question without either person leaving the editor.

How the plugin is built and tested

The repository is a Neovim plugin laid out the conventional way, with the Lua modules in a lua directory, the plugin entry points in a plugin directory, the tree-sitter queries in a queries directory, and the test suite in a tests directory. There is a stylua configuration, which is the formatter for Lua that the project uses.

The Makefile is where the testing story is, and it is short.

makefile
test:
	@REGEXPLAINER_DEBOUNCE=false \
		nvim \
			--headless \
			--noplugin \
			-u tests/bootstrap.lua \
			-c "PlenaryBustedDirectory tests/regexplainer/ {minimal_init='tests/mininit.lua',sequential=true,keep_going=false}"\
			-c "qa!"

The tests are busted tests run inside a headless Neovim with no plugins loaded, using a bootstrap file and a minimal init, and they run sequentially with the run stopping on the first failure. The debug setting is forced off for the run through an environment variable, which is a small piece of care, because a plugin that logs debug messages while under test produces noise in the output.

There is a watch target that uses entr to re-run the tests when a Lua or JavaScript file changes, and a clean target that removes the vendor and test directories.

The test suite lives under a subdirectory matching the plugin, which suggests the tests are organised by feature, and the fact that the watch pattern includes JavaScript files as well as Lua tells you that part of the plugin, the tree-sitter queries, is written in that language.

The whole project is small, in the sense that a plugin with one job should be, and the interesting decisions are the ones about how the pattern is found and how the explanation is rendered, both of which are handled by delegating to tree-sitter and to Python respectively.

Editorial conclusion

nvim-regexplainer fits a Neovim user who writes regular expressions by hand and wants to understand one without leaving the editor, particularly one that inherited a pattern from a colleague or from a config file. It does not fit a user on a Neovim older than 0.11, and the graphical mode does not fit a terminal that cannot display images. Verify first that the tree-sitter parser for your language and the regex parser are installed, that the health check command reports them, that the filetype you work in is in the activation list, and that the narrative indentation option name is spelled the way the version you installed expects, because the documentation uses two different spellings of it.

Frequently asked questions

What does nvim-regexplainer do?

It describes the regular expression under the cursor in Neovim, in either a narrative mode that indents the pattern structure by capture group, or a graphical mode that renders it as a railroad diagram. The explanation can be displayed in a popup below the cursor or in a split window.

What does nvim-regexplainer require?

Neovim 0.11 or newer, the regex tree-sitter parser, and the tree-sitter parser for the host language. Nine languages are supported: JavaScript and TypeScript, HTML, Ruby, Python, Go, Rust, PHP, Java, and C#. You can verify the setup with the checkhealth command for the plugin.

How do I install nvim-regexplainer with Neovim's package manager?

With the pack add function, passing the repository for the plugin and, if you want the graphical mode, the hologram plugin for image display. The graphical mode also needs a terminal that can display images, such as Kitty or iTerm2, and Python 3.7 or newer.

How do I copy an nvim-regexplainer explanation to the clipboard?

Use the yank function with the plus register, either as a string or as a table with the register and the display options. The default register is the unnamed one, and there is also a yank command that takes the register as an argument.

How are the nvim-regexplainer tests run?

With make test, which runs a headless Neovim with no plugins loaded, boots the test environment, and runs the busted test directory sequentially. The debug setting is forced off for the run through an environment variable, and a watch target re-runs the tests on changes using entr.

Official sources

  1. bennypowers/nvim-regexplainer on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bennypowers-nvim-regexplainer.svg)](https://hysenlabs.com/projects/bennypowers-nvim-regexplainer)

Was this useful?