Nvim-regexplainer: Explain the RegExp Under the Cursor

x-post from r/neovim

I wrote nvim-regexplainer to help me work with regular expressions, mostly in JS/TS.

Inspired by the great atom-regexp-railroad plugin, it pops up a helpful explanation of the regexp under the cursor when invoked. There are a few display options like popup or split display. In the future I’m hoping to implement genuine railroad diagrams using hologram.nvim.

Consider this to be alpha-quality software. If you found a regexp that breaks my plugin, or that it doesn’t work in your language (but should, if the language is well supported in treesitter), please file an issue to let me know. Also beware that while I’ve done some work to prevent infinite loops, I have occasionally experienced them and had to kill nvim . If you manage to track a case like that down and report it, you get 10 points.

If all that sounds nice to you, try it out and let me know what you think

4 Likes

In the video, line 56, the plugin seems to have missed the s at the end of (?:t|j|cs)s.


yeah looks like it misses some of the escape chars as well

I think this has something to do with conceal levels

Here’s the internal “components” tree for that regexp:

local regexp_string = [[@scope\/(.*)\.(?<extension>graphql|(?:t|j|cs)s)";?]]
local components = { {
    text = "@scope\\/",
    type = "pattern_character"
  }, {
    capture_group = 1,
    children = { {
        children = { {
            children = { {
                text = ".",
                type = "any_character",
                zero_or_more = true
              } },
            text = ".*",
            type = "term"
          } },
        text = ".*",
        type = "pattern"
      } },
    depth = 1,
    text = "(.*)",
    type = "anonymous_capturing_group"
  }, {
    text = "\\.",
    type = "identity_escape"
  }, {
    capture_group = 2,
    children = { {
        children = { {
            children = { {
                children = { {
                    text = "graphql",
                    type = "pattern_character"
                  } },
                text = "graphql",
                type = "term"
              }, {
                children = { {
                    children = { {
                        children = { {
                            children = { {
                                children = { {
                                    text = "t",
                                    type = "pattern_character"
                                  } },
                                text = "t",
                                type = "term"
                              }, {
                                children = { {
                                    text = "j",
                                    type = "pattern_character"
                                  } },
                                text = "j",
                                type = "term"
                              }, {
                                children = { {
                                    text = "cs",
                                    type = "pattern_character"
                                  } },
                                text = "cs",
                                type = "term"
                              } },
                            text = "t|j|cs",
                            type = "alternation"
                          } },
                        text = "t|j|cs",
                        type = "pattern"
                      } },
                    depth = 1,
                    text = "(?:t|j|cs)",
                    type = "non_capturing_group"
                  }, {
                    text = "s",
                    type = "pattern_character"
                  } },
                text = "(?:t|j|cs)s",
                type = "term"
              } },
            text = "graphql|(?:t|j|cs)s",
            type = "alternation"
          } },
        text = "graphql|(?:t|j|cs)s",
        type = "pattern"
      } },
    depth = 1,
    group_name = "extension",
    text = "(?<extension>graphql|(?:t|j|cs)s)",
    type = "named_capturing_group"
  }, {
    text = '"',
    type = "pattern_character"
  }, {
    optional = true,
    text = ";",
    type = "pattern_character"
  } }

or folded up for readability:

We’re missing the escaped \/ after the first capture group, and indeed the pattern_character s following the non-capturing group (?:t|j|cs)

@donbex Thanks for the report. Try with latest and let me know

Nice idea. What about other filetypes than js/ts?

if the language is supported in treesitter and injects regex, it should Just Work™.

The only catch in that case would be if somehow the host grammar doesn’t label the regex injection the same as the ecma grammar does