Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

OXY2DEV/patterns.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

66 Commits

Repository files navigation

🧩 patterns.nvim

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

local MiniDeps = 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`.
---@class patterns.config
---
--- WARNING, This just changes the priority
--- of the matchers.
--- Both matchers will be tried!
---@field preferred_regex_matcher
---| "node" Allows matching Javascript's regexp.
---| "vim" Allows matching Vim's regexp.
---
--- Delay for updating explainer UI.
---@field update_delay integer
---
---@field keymaps? patterns.keymaps
---@field windows patterns.windows
---@field lua_patterns patterns.lua_patterns
---@field regex patterns.regex
---@class patterns.keymaps
---
---@field hover table<string, patterns.keymap_opts>
---@field explain_input table<string, patterns.keymap_opts>
---@field explain_preview table<string, patterns.keymap_opts>
--- Action names for the explainer.
---@alias explain_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.
---@alias hover_actions
---| "close" Closes hover window.
---| "edit" Edit pattern.
---@class patterns.keymap_opts
---
---@field desc? string
---@field callback explain_actions | hover_actions | function
--- Window configurations for various
--- windows.
---@class patterns.windows
---
---@field hover? table | fun(q1: "left" | "right" | "center", q2: "top" | "bottom" | "center"): table
---
---@field input? table | fun(): table
---@field preview? table | fun(): table
--- Options for Lua patterns.
--- Option name matches the tree-sitter node name.
---@class patterns.lua_patterns
---
---@field indent_size integer Indentation size.
---@field indent_marker string Marker used for indentation.
---@field indent_hl? string Highlight group for the indentation markers.
---
---@field pattern pattern_item.opts
---
---@field anchor_start pattern_item.opts
---@field anchor_end pattern_item.opts
---
---@field quantifier_optional pattern_item.opts
---@field quantifier_minus pattern_item.opts
---@field quantifier_plus pattern_item.opts
---@field quantifier_star pattern_item.opts
---
---@field literal_character pattern_item.opts
---@field any_character pattern_item.opts
---@field escape_sequence pattern_item.opts
---@field escaped_character pattern_item.opts
---
---@field capture_group pattern_item.opts
---@field character_set pattern_item.opts
---@field character_set_content pattern_item.opts
---@field character_range pattern_item.opts
---@field character_class pattern_item.opts
--- Options for Regex.
--- Option name matches the tree-sitter node name.
---@class patterns.regex
---
---@field indent_size integer Indentation size.
---@field indent_marker string Marker used for indentation.
---@field indent_hl? string Highlight group for the indentation markers.
---
---@field pattern pattern_item.opts
---@field alternation pattern_item.opts
---@field term pattern_item.opts
---
---@field start_assertion pattern_item.opts
---@field end_assertion pattern_item.opts
---@field boundary_assertion pattern_item.opts
---@field non_boundary_assertion pattern_item.opts
---@field lookaround_assertion pattern_item.opts
---
---@field quantifier_count pattern_item.opts
---@field quantifier_optional pattern_item.opts
---@field quantifier_plus pattern_item.opts
---@field quantifier_star pattern_item.opts
---
---@field pattern_character pattern_item.opts
---@field class_character pattern_item.opts
---@field any_character pattern_item.opts
---@field decimal_escape pattern_item.opts
---@field character_class_escape pattern_item.opts
---@field unicode_character_escape pattern_item.opts
---@field unicode_property_value pattern_item.opts
---@field control_escape pattern_item.opts
---@field control_letter_escape pattern_item.opts
---@field identity_escape pattern_item.opts
---@field backreference_escape pattern_item.opts
---@field unicode_property_value_expression pattern_item.opts
---
---@field character_class pattern_item.opts
---@field posix_character_class pattern_item.opts
---@field named_group_backreference pattern_item.opts
---@field capturing_group pattern_item.opts
---@field non_capturing_group pattern_item.opts
---
---@field flags_group pattern_item.opts
---@field flags pattern_item.opts
--- Options for each node type.
---@class pattern_item.opts
---
--- Can be set to `false` to disable rendering of 
--- a specific node type.
---@field enable? boolean | fun(buffer: integer, item: __patterns.item): boolean
---
--- Can be set to `true` to show the range of a
--- node.
---@field show_range? boolean | fun(buffer: integer, item: __patterns.item): boolean
---
--- Highlight group for the text.
---@field text_hl? string | fun(buffer: integer, item: __patterns.item): string?
---
--- Text to show for a node.
---@field text? 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.
---@field show_tip? boolean | fun(buffer: integer, item: __patterns.item): boolean
---
--- Highlight group for the tooltip text.
---@field tip_hl? string | fun(buffer: integer, item: __patterns.item): string
---
--- Number of spaces to add before tooltip text.
--- This is added AFTER the indentation.
---@field tip_offset? integer | fun(buffer: integer, item: __patterns.item): integer
---
--- Highlight group for the node range.
---@field range_hl? string | fun(buffer: integer, item: __patterns.item): string?
---
--- Bade highlight group. Used by other *_hl
--- options when they don't have a value.
---@field hl? 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)
 local border = { "", "", "", "", "", "", "", "" };
 if q2 == "top" then
 if q1 == "left" then
 border[5] = "";
 elseif q1 == "right" then
 border[7] = "";
 end
 elseif q2 == "bottom" then
 if q1 == "left" then
 border[3] = "";
 elseif q1 == "right" then
 border[1] = "";
 end
 end
 local ft;
 if package.loaded["patterns.hover"] and package.loaded["patterns.hover"].buf then
 ft = vim.bo[package.loaded["patterns.hover"].buf].ft;
 end
 return {
 width = math.floor(vim.o.columns * 0.6),
 height = math.floor(vim.o.lines * 0.5),
 border = border,
 footer_pos = "right",
 footer = {
 { "", "FloatBorder" },
 { " 󰛪 " .. (ft or "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)
 if item.text == "\\" then
 return '󱄽 Character: "\\"';
 else
 return string.format("󱄽 Character: %s", vim.inspect(item.text));
 end
 end,
 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)
 return string.format('󰩈 Escape sequence: "%s"', item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette1Bg",
 hl = "PatternsPalette1"
 },
 escaped_character = {
 text = function (_, item)
 return string.format('󰩈 Escaped character: "%s"', item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette6Bg",
 hl = "PatternsPalette6"
 },
 ----------------------------------------
 capture_group = {
 text = function (_, item)
 return string.format(" Capture group, 󱤬 %d", item.id or -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)
 return string.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)
 return string.format("󰊲 Regex term(#%d)", item.id or -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)
 if string.match(item.text, "^%(%?%<") then
 return "󰡭 Look behind";
 else
 return "󰡮 Look ahead";
 end
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette3Bg",
 hl = "PatternsPalette3"
 },
 ----------------------------------------
 quantifier_count = {
 text = function (_, item)
 if string.match(item.text, "^%d+$") then
 return string.format(" Repeats exactly %s times", item.text);
 elseif string.match(item.text, "^%d+,$") then
 return string.format(
 " Repeats at least %s times",
 string.match(item.text, "^(%d+)")
 );
 elseif string.match(item.text, "^,%d+$") then
 return string.format(
 " Repeats at most %s times",
 string.match(item.text, "^,(%d+)$")
 );
 else
 return string.format(
 " Repeats between %s & %s times",
 string.match(item.text, "^(%d+),"),
 string.match(item.text, "^%d+,(%d+)$")
 );
 end
 end,
 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)
 return string.format("󱄽 Character: %s", vim.inspect(item.text));
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette2Bg",
 hl = "PatternsPalette2"
 },
 class_character = {
 text = function (_, item)
 return string.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)
 return string.format("󰩈 Decimal escape: %s", item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette1Bg",
 hl = "PatternsPalette1"
 },
 character_class_escape = {
 text = function (_, item)
 return string.format("󰩈 Character class escape: %s", item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette1Bg",
 hl = "PatternsPalette1"
 },
 unicode_character_escape = {
 text = function (_, item)
 return string.format("󰩈 Unicode character escape: %s", item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette1Bg",
 hl = "PatternsPalette1"
 },
 unicode_property_value = {
 text = function (_, item)
 return string.format("󰗊 Unicode property value: %s", item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette6Bg",
 hl = "PatternsPalette6"
 },
 control_escape = {
 text = function (_, item)
 return string.format("󰁨 Control character escape: %s", item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette1Bg",
 hl = "PatternsPalette1"
 },
 control_letter_escape = {
 text = function (_, item)
 return string.format("󰁨 Control letter escape: %s", item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette1Bg",
 hl = "PatternsPalette1"
 },
 identity_escape = {
 text = function (_, item)
 return string.format("󰩈 Identity escape: %s", item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette1Bg",
 hl = "PatternsPalette1"
 },
 backreference_escape = {
 text = function (_, item)
 return string.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)
 return string.format("󰏗 POSIX Character class: ", item.text);
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette5Bg",
 hl = "PatternsPalette5"
 },
 named_group_backreference = {
 text = function (_, item)
 return string.format("󰒻 Named backreference: ", string.match(item.text, "^%(%?P%=(.-)%)$"));
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette5Bg",
 hl = "PatternsPalette5"
 },
 capturing_group = {
 text = function (_, item)
 if type(item.id) == "string" then
 return string.format("󱉶 Capture group(#%s)", item.id or "???");
 else
 return string.format("󱉶 Capture group(#%d)", item.id or -1);
 end
 end,
 show_tip = on_current,
 tip_hl = "PatternsPalette5Bg",
 hl = "PatternsPalette5"
 },
 non_capturing_group = {
 text = function (_, item)
 return string.format("󰒉 Non-capture group(#%d)", item.id or -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)
 return string.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

License

Stars

Watchers

Forks

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /