Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

66 Commits

Repository files navigation

🧩 patterns.nvim

NeovimRepo sizeGitHub Release

A simple pattern explainer & editor for Neovim.

📖 Table of contents

✨ Features

  • Tree-sitter based pattern explainer.

  • LSP-like hover window for strings.

  • A real-time pattern editor & matcher.

  • Support for multiple pattern languages,

    • regex
    • lua_patterns(requires custom parser)
  • Highly configurable! Almost everything can be configured(without needing to leave your editor).

📚 Requirements

  • Tree-sitter parser.

    • regex(install through nvim-treesitter via :TSInstall regex)
    • lua_patterns(optional, See parser installation).
  • A tree-sitter supported colorscheme(optional).

  • A nerd font.

  • Node.js(optional, needed if you use javascript's regexp).

📐 Installation

🧩 Vim-plug

Add this to your plugin list.

Plug "OXY2DEV/patterns.nvim"

💤 lazy.nvim

Note

Lazy loading is NOT needed for this plugin!

For plugins.lua users,

{
"OXY2DEV/patterns.nvim",
},

For plugins/patterns.lua,

return {
"OXY2DEV/patterns.nvim",
};

🦠 mini.deps

localMiniDeps=require("mini.deps");
MiniDeps.add({
source="OXY2DEV/patterns.nvim"
});

🌒 Rocks.nvim

Warning

luarocks package may sometimes be a bit behind main.

:Rocks install patterns.nvim

📥 GitHub release

Tagged releases can be found in the release page.

Note

Github releases may sometimes be slightly behind main.

🧭 Configuration

See the default configuration here.

Show type definitions
--- Configuration for `patterns.nvim`.---@classpatterns.config------ WARNING, This just changes the priority--- of the matchers.--- Both matchers will be tried!---@fieldpreferred_regex_matcher---| "node" Allows matching Javascript's regexp.---| "vim" Allows matching Vim's regexp.------ Delay for updating explainer UI.---@fieldupdate_delayinteger------@fieldkeymaps? patterns.keymaps---@fieldwindowspatterns.windows---@fieldlua_patternspatterns.lua_patterns---@fieldregexpatterns.regex---@classpatterns.keymaps------@fieldhovertable<string, patterns.keymap_opts>---@fieldexplain_inputtable<string, patterns.keymap_opts>---@fieldexplain_previewtable<string, patterns.keymap_opts>--- Action names for the explainer.---@aliasexplain_actions---| "toggle" Toggle focus of window.---| "mode_change" Switches between the explainer & the matcher.------ Changes pattern language backwards.---| "lang_prev"---| "lang_next" Changes pattern language forwards.------ Closes explainer.---| "close"---| "apply" Applies changes.--- Action names for the hover.---@aliashover_actions---| "close" Closes hover window.---| "edit" Edit pattern.---@classpatterns.keymap_opts------@fielddesc? string---@fieldcallbackexplain_actions | hover_actions | function--- Window configurations for various--- windows.---@classpatterns.windows------@fieldhover? table | fun(q1: "left" | "right" | "center", q2: "top" | "bottom" | "center"): table------@fieldinput? table | fun(): table---@fieldpreview? table | fun(): table--- Options for Lua patterns.--- Option name matches the tree-sitter node name.---@classpatterns.lua_patterns------@fieldindent_sizeinteger Indentation size.---@fieldindent_markerstring Marker used for indentation.---@fieldindent_hl? string Highlight group for the indentation markers.------@fieldpatternpattern_item.opts------@fieldanchor_startpattern_item.opts---@fieldanchor_endpattern_item.opts------@fieldquantifier_optionalpattern_item.opts---@fieldquantifier_minuspattern_item.opts---@fieldquantifier_pluspattern_item.opts---@fieldquantifier_starpattern_item.opts------@fieldliteral_characterpattern_item.opts---@fieldany_characterpattern_item.opts---@fieldescape_sequencepattern_item.opts---@fieldescaped_characterpattern_item.opts------@fieldcapture_grouppattern_item.opts---@fieldcharacter_setpattern_item.opts---@fieldcharacter_set_contentpattern_item.opts---@fieldcharacter_rangepattern_item.opts---@fieldcharacter_classpattern_item.opts--- Options for Regex.--- Option name matches the tree-sitter node name.---@classpatterns.regex------@fieldindent_sizeinteger Indentation size.---@fieldindent_markerstring Marker used for indentation.---@fieldindent_hl? string Highlight group for the indentation markers.------@fieldpatternpattern_item.opts---@fieldalternationpattern_item.opts---@fieldtermpattern_item.opts------@fieldstart_assertionpattern_item.opts---@fieldend_assertionpattern_item.opts---@fieldboundary_assertionpattern_item.opts---@fieldnon_boundary_assertionpattern_item.opts---@fieldlookaround_assertionpattern_item.opts------@fieldquantifier_countpattern_item.opts---@fieldquantifier_optionalpattern_item.opts---@fieldquantifier_pluspattern_item.opts---@fieldquantifier_starpattern_item.opts------@fieldpattern_characterpattern_item.opts---@fieldclass_characterpattern_item.opts---@fieldany_characterpattern_item.opts---@fielddecimal_escapepattern_item.opts---@fieldcharacter_class_escapepattern_item.opts---@fieldunicode_character_escapepattern_item.opts---@fieldunicode_property_valuepattern_item.opts---@fieldcontrol_escapepattern_item.opts---@fieldcontrol_letter_escapepattern_item.opts---@fieldidentity_escapepattern_item.opts---@fieldbackreference_escapepattern_item.opts---@fieldunicode_property_value_expressionpattern_item.opts------@fieldcharacter_classpattern_item.opts---@fieldposix_character_classpattern_item.opts---@fieldnamed_group_backreferencepattern_item.opts---@fieldcapturing_grouppattern_item.opts---@fieldnon_capturing_grouppattern_item.opts------@fieldflags_grouppattern_item.opts---@fieldflagspattern_item.opts--- Options for each node type.---@classpattern_item.opts------ Can be set to `false` to disable rendering of --- a specific node type.---@fieldenable? boolean | fun(buffer: integer, item: __patterns.item): boolean------ Can be set to `true` to show the range of a--- node.---@fieldshow_range? boolean | fun(buffer: integer, item: __patterns.item): boolean------ Highlight group for the text.---@fieldtext_hl? string | fun(buffer: integer, item: __patterns.item): string?------ Text to show for a node.---@fieldtext? string | fun(buffer: integer, item: __patterns.item): string------ When set to `true`, shows tooltip for nodes.--- By default this only shows tips for the current--- node.---@fieldshow_tip? boolean | fun(buffer: integer, item: __patterns.item): boolean------ Highlight group for the tooltip text.---@fieldtip_hl? string | fun(buffer: integer, item: __patterns.item): string------ Number of spaces to add before tooltip text.--- This is added AFTER the indentation.---@fieldtip_offset? integer | fun(buffer: integer, item: __patterns.item): integer------ Highlight group for the node range.---@fieldrange_hl? string | fun(buffer: integer, item: __patterns.item): string?------ Bade highlight group. Used by other *_hl--- options when they don't have a value.---@fieldhl? string | fun(buffer: integer, item: __patterns.item): string?
Show default configuration
spec.default= {
preferred_regex_matcher="vim",
update_delay=150,
keymaps= {
explain_input= {
["<CR>"] = {
callback="apply"
},
["q"] = {
callback="close"
},
["<tab>"] = {
callback="toggle"
},
["H"] = {
callback="lang_prev"
},
["L"] = {
callback="lang_next"
},
},
explain_preview= {
["q"] = {
callback="close"
},
["<tab>"] = {
callback="toggle"
},
["T"] = {
callback="mode_change"
}
},
hover= {
["q"] = {
callback="close"
},
["i"] = {
callback="edit"
}
}
},
windows= {
hover=function (q1, q2)
localborder= { "", "", "", "", "", "", "", "" };
ifq2=="top" thenifq1=="left" thenborder[5] ="";
elseifq1=="right" thenborder[7] ="";
endelseifq2=="bottom" thenifq1=="left" thenborder[3] ="";
elseifq1=="right" thenborder[1] ="";
endendlocalft;
ifpackage.loaded["patterns.hover"] andpackage.loaded["patterns.hover"].bufthenft=vim.bo[package.loaded["patterns.hover"].buf].ft;
endreturn {
width=math.floor(vim.o.columns*0.6),
height=math.floor(vim.o.lines*0.5),
border=border,
footer_pos="right",
footer= {
{ "", "FloatBorder" },
{ " 󰛪 " .. (ftor"Patterns") .."", "FloatBorder" },
{ "", "FloatBorder" },
}
}
end
},
lua_patterns= {
indent_size=2,
indent_marker="",
indent_hl="PatternsPalette0Fg",
pattern= {
text="󰐱 Pattern",
show_tip=on_current,
tip_hl="PatternsPalette0Bg",
hl="PatternsPalette0";
},
----------------------------------------anchor_start= {
text="󰾺 From start",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
anchor_end= {
text="󰾸 To end",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
----------------------------------------quantifier_minus= {
text="󰑖 Zero or more times(non-greedily)",
show_tip=on_current,
tip_hl="PatternsPalette7Bg",
hl="PatternsPalette7"
},
quantifier_optional= {
text="󰑘 Zero or one time",
show_tip=on_current,
tip_hl="PatternsPalette7Bg",
hl="PatternsPalette7"
},
quantifier_plus= {
text="󰑘 One or more times",
show_tip=on_current,
tip_hl="PatternsPalette7Bg",
hl="PatternsPalette7"
},
quantifier_star= {
text="󰑖 Zero or more times(greedily)",
show_tip=on_current,
tip_hl="PatternsPalette7Bg",
hl="PatternsPalette7"
},
----------------------------------------literal_character= {
text=function (_, item)
ifitem.text=="\\" thenreturn'󱄽 Character: "\\"';
elsereturnstring.format("󱄽 Character: %s", vim.inspect(item.text));
endend,
show_tip=on_current,
tip_hl="PatternsPalette4Bg",
hl="PatternsPalette4"
},
any_character= {
text=" Any character",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
escape_sequence= {
text=function (_, item)
returnstring.format('󰩈 Escape sequence: "%s"', item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette1Bg",
hl="PatternsPalette1"
},
escaped_character= {
text=function (_, item)
returnstring.format('󰩈 Escaped character: "%s"', item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette6Bg",
hl="PatternsPalette6"
},
----------------------------------------capture_group= {
text=function (_, item)
returnstring.format(" Capture group, 󱤬 %d", item.idor-1);
end,
show_tip=on_current,
tip_hl="PatternsPalette6Bg",
hl="PatternsPalette6"
},
character_set= {
text="󱉓 Character set",
show_tip=on_current,
tip_hl="PatternsPalette3Bg",
hl="PatternsPalette3"
},
character_set_content= {
text="󰆦 Character set content,",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
character_range= {
text=function (_, item)
returnstring.format("󰊱 Character range: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette6Bg",
hl="PatternsPalette6"
},
character_class= {
text=function (_, item)
return"󰏗 Character class: " ..vim.inspect(item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette4Bg",
hl="PatternsPalette4"
},
},
regex= {
indent_size=2,
indent_marker="",
indent_hl="PatternsPalette0Fg",
pattern= {
text="󰛪 Pattern",
show_tip=on_current,
tip_hl="PatternsPalette0Bg",
hl="PatternsPalette0"
},
alternation= {
text="󰋰 Alternative pattern(s)",
show_tip=on_current,
tip_hl="PatternsPalette6Bg",
hl="PatternsPalette6"
},
term= {
text=function (_, item)
returnstring.format("󰊲 Regex term(#%d)", item.idor-1);
end,
show_tip=on_current,
tip_hl="PatternsPalette6Bg",
hl="PatternsPalette6"
},
----------------------------------------start_assertion= {
text="󰾺 From start",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
end_assertion= {
text="󰾸 To end",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
boundary_assertion= {
text="󰕤 Match as a word",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
non_boundary_assertion= {
text="󰕛 Match as part of a word",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
lookaround_assertion= {
text=function (_, item)
ifstring.match(item.text, "^%(%?%<") thenreturn"󰡭 Look behind";
elsereturn"󰡮 Look ahead";
endend,
show_tip=on_current,
tip_hl="PatternsPalette3Bg",
hl="PatternsPalette3"
},
----------------------------------------quantifier_count= {
text=function (_, item)
ifstring.match(item.text, "^%d+$") thenreturnstring.format(" Repeats exactly %s times", item.text);
elseifstring.match(item.text, "^%d+,$") thenreturnstring.format(
" Repeats at least %s times",
string.match(item.text, "^(%d+)")
);
elseifstring.match(item.text, "^,%d+$") thenreturnstring.format(
" Repeats at most %s times",
string.match(item.text, "^,(%d+)$")
);
elsereturnstring.format(
" Repeats between %s & %s times",
string.match(item.text, "^(%d+),"),
string.match(item.text, "^%d+,(%d+)$")
);
endend,
show_tip=on_current,
tip_hl="PatternsPalette7Bg",
hl="PatternsPalette7"
},
quantifier_optional= {
text=" Repeats zero or one time",
show_tip=on_current,
tip_hl="PatternsPalette7Bg",
hl="PatternsPalette7"
},
quantifier_plus= {
text=" Repeats one or more times",
show_tip=on_current,
tip_hl="PatternsPalette7Bg",
hl="PatternsPalette7"
},
quantifier_star= {
text=" Repeats zero or more times",
show_tip=on_current,
tip_hl="PatternsPalette7Bg",
hl="PatternsPalette7"
},
----------------------------------------pattern_character= {
text=function (_, item)
returnstring.format("󱄽 Character: %s", vim.inspect(item.text));
end,
show_tip=on_current,
tip_hl="PatternsPalette2Bg",
hl="PatternsPalette2"
},
class_character= {
text=function (_, item)
returnstring.format("󱄽 Character: %s", vim.inspect(item.text));
end,
show_tip=on_current,
tip_hl="PatternsPalette2Bg",
hl="PatternsPalette2"
},
any_character= {
text=" Any character",
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
decimal_escape= {
text=function (_, item)
returnstring.format("󰩈 Decimal escape: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette1Bg",
hl="PatternsPalette1"
},
character_class_escape= {
text=function (_, item)
returnstring.format("󰩈 Character class escape: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette1Bg",
hl="PatternsPalette1"
},
unicode_character_escape= {
text=function (_, item)
returnstring.format("󰩈 Unicode character escape: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette1Bg",
hl="PatternsPalette1"
},
unicode_property_value= {
text=function (_, item)
returnstring.format("󰗊 Unicode property value: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette6Bg",
hl="PatternsPalette6"
},
control_escape= {
text=function (_, item)
returnstring.format("󰁨 Control character escape: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette1Bg",
hl="PatternsPalette1"
},
control_letter_escape= {
text=function (_, item)
returnstring.format("󰁨 Control letter escape: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette1Bg",
hl="PatternsPalette1"
},
identity_escape= {
text=function (_, item)
returnstring.format("󰩈 Identity escape: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette1Bg",
hl="PatternsPalette1"
},
backreference_escape= {
text=function (_, item)
returnstring.format("󰒻 Backreference escape: %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette1Bg",
hl="PatternsPalette1"
},
----------------------------------------unicode_property_value_expression= {
text="󰁀 Unicode property value expression",
show_tip=on_current,
-- show_content = true,tip_hl="PatternsPalette0Bg",
hl="PatternsPalette0"
},
----------------------------------------character_class= {
text="󰏗 Character class",
show_tip=on_current,
tip_hl="PatternsPalette4Bg",
hl="PatternsPalette4"
},
posix_character_class= {
text=function (_, item)
returnstring.format("󰏗 POSIX Character class: ", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
named_group_backreference= {
text=function (_, item)
returnstring.format("󰒻 Named backreference: ", string.match(item.text, "^%(%?P%=(.-)%)$"));
end,
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
capturing_group= {
text=function (_, item)
iftype(item.id) =="string" thenreturnstring.format("󱉶 Capture group(#%s)", item.idor"???");
elsereturnstring.format("󱉶 Capture group(#%d)", item.idor-1);
endend,
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
non_capturing_group= {
text=function (_, item)
returnstring.format("󰒉 Non-capture group(#%d)", item.idor-1);
end,
show_tip=on_current,
tip_hl="PatternsPalette5Bg",
hl="PatternsPalette5"
},
flags_group= {
text="󰂖 Flags group",
show_tip=on_current,
tip_hl="PatternsPalette2Bg",
hl="PatternsPalette2"
},
flags= {
text=function (_, item)
returnstring.format("󰈻 Flag(s): %s", item.text);
end,
show_tip=on_current,
tip_hl="PatternsPalette2Bg",
hl="PatternsPalette2"
},
}
};

💡 Commands

This plugin creates the :Patterns command. It has 2 sub-commands,

  • explain Explains the pattern under the cursor.

  • hover LSP-like hover for the pattern under cursor. It's behavior is similar to K(or vim.lsp.buf.hover()).

When :Patterns is run without any arguments, it opens the explain window.

🎹 keymaps

The hover & explain buffers have some pre-defined keymaps.

Tip

You can disable these keymaps individually via the config.

{
keymaps= {
hover= {
["i"] = { enable=false }
}
}
}

⭐ hover

The hover buffer has the following keymaps,

  • q Closes hover window.

  • i Opens the pattern inside the explainer.

⭐ explain

⭐ explain_input

The text input buffer has the following keymaps,

  • <CR> Replaces the pattern under cursor with the text in the input buffer.

  • q Quits the explainer.

  • <tab> Switches to the explanation/preview buffer.

  • H Cycles backward through the list of supported languages(lua_patterns, regex).

  • L Cycles forward through the list of supported languages(lua_patterns, regex).

⭐ explain_preview

The pattern preview/explanation buffer has the following keymaps,

  • q Quits the explainer.

  • <tab> Switches to the input buffer.

  • T Toggles between the explanation and the matcher.

About

A simple pattern viewer for Neovim with support for Lua patterns & regex.

Resources

Stars

97 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages