diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/lua/plug_2.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/lua/plug_2.lua new file mode 100644 index 00000000..c886720a --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/lua/plug_2.lua @@ -0,0 +1 @@ +return { 'plugin_2/lua/plug_2.lua' } diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/plugin/plug_2.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/plugin/plug_2.lua new file mode 100644 index 00000000..4deb1555 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/plugin/plug_2.lua @@ -0,0 +1,2 @@ +_G.plugin_log = _G.plugin_log or {} +table.insert(_G.plugin_log, 'plugin/plug_2.lua') diff --git a/config/neovim/store/lazy-plugins/promise-async/.github/colorscheme/catppuccin.vim b/config/neovim/store/lazy-plugins/promise-async/.github/colorscheme/catppuccin.vim new file mode 100644 index 00000000..aeb778eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.github/colorscheme/catppuccin.vim @@ -0,0 +1,22 @@ +let s:c = has("nvim") == 1 ? luaeval('require("catppuccin.palettes").get_palette()') : luaeval('vim.dict(require("catppuccin.palettes").get_palette())') + +let s:p = {'normal': {}, 'inactive': {}, 'insert': {}, 'replace': {}, 'visual': {}, 'tabline': {}} +let s:p.normal.left = [ [ s:c.mantle, s:c.blue ], [ s:c.blue, s:c.base ] ] +let s:p.normal.middle = [ [ s:c.blue, s:c.mantle ] ] +let s:p.normal.right = [ [ s:c.overlay0, s:c.base ], [ s:c.blue, s:c.surface0 ] ] +let s:p.insert.left = [ [ s:c.mantle, s:c.teal ], [ s:c.blue, s:c.base ] ] +let s:p.visual.left = [ [ s:c.mantle, s:c.mauve ], [ s:c.blue, s:c.base ] ] +let s:p.replace.left = [ [ s:c.mantle, s:c.red ], [ s:c.blue, s:c.base ] ] + +let s:p.inactive.left = [ [ s:c.blue, s:c.base ], [ s:c.overlay0, s:c.base ] ] +let s:p.inactive.middle = [ [ s:c.surface1, s:c.base ] ] +let s:p.inactive.right = [ [ s:c.surface1, s:c.base ], [ s:c.overlay0, s:c.base ] ] + +let s:p.tabline.left = [ [ s:c.overlay0, s:c.base ], [ s:c.overlay0, s:c.base ] ] +let s:p.tabline.tabsel = [ [ s:c.blue, s:c.surface1 ], [ s:c.overlay0, s:c.base] ] +let s:p.tabline.middle = [ [ s:c.surface1, s:c.base ] ] +let s:p.tabline.right = copy(s:p.inactive.right) +let s:p.normal.error = [ [ s:c.mantle, s:c.red ] ] +let s:p.normal.warning = [ [ s:c.mantle, s:c.yellow ] ] + +let g:lightline#colorscheme#catppuccin#palette = lightline#colorscheme#fill(s:p) diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-frappe.lua b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-frappe.lua new file mode 100644 index 00000000..ecebd5ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-frappe.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue" "frappe" diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-latte.lua b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-latte.lua new file mode 100644 index 00000000..e52461d8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-latte.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue" "latte" diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-macchiato.lua b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-macchiato.lua new file mode 100644 index 00000000..5778cea3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-macchiato.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue" "macchiato" diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-mocha.lua b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-mocha.lua new file mode 100644 index 00000000..044f8716 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin-mocha.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue" "mocha" diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin.lua b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin.lua new file mode 100644 index 00000000..b5848cf7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/barbecue/theme/catppuccin.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue"() diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/editor.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/editor.lua new file mode 100644 index 00000000..6a12ef66 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/editor.lua @@ -0,0 +1,77 @@ +local M = {} + +function M.get() + return { + ColorColumn = { bg = C.surface0 }, -- used for the columns set with 'colorcolumn' + Conceal = { fg = C.overlay1 }, -- placeholder characters substituted for concealed text (see 'conceallevel') + Cursor = { fg = C.base, bg = C.text }, -- character under the cursor + lCursor = { fg = C.base, bg = C.text }, -- the character under the cursor when |language-mapping| is used (see 'guicursor') + CursorIM = { fg = C.base, bg = C.text }, -- like Cursor, but used when in IME mode |CursorIM| + CursorColumn = { bg = C.mantle }, -- Screen-column at the cursor, when 'cursorcolumn' is set. + CursorLine = { + bg = U.vary_color({ latte = U.lighten(C.mantle, 0.70, C.base) }, U.darken(C.surface0, 0.64, C.base)), + }, -- Screen-line at the cursor, when 'cursorline' is set. Low-priority if forecrust (ctermfg OR guifg) is not set. + Directory = { fg = C.blue }, -- directory names (and other special names in listings) + EndOfBuffer = { fg = O.show_end_of_buffer and C.surface1 or C.base }, -- filler lines (~) after the end of the buffer. By default, this is highlighted like |hl-NonText|. + ErrorMsg = { fg = C.red, style = { "bold", "italic" } }, -- error messages on the command line + VertSplit = { fg = O.transparent_background and C.surface1 or C.crust }, -- the column separating vertically split windows + Folded = { fg = C.blue, bg = O.transparent_background and C.none or C.surface1 }, -- line used for closed folds + FoldColumn = { fg = C.overlay0 }, -- 'foldcolumn' + SignColumn = { fg = C.surface1 }, -- column where |signs| are displayed + SignColumnSB = { bg = C.crust, fg = C.surface1 }, -- column where |signs| are displayed + Substitute = { bg = C.surface1, fg = U.vary_color({ latte = C.red }, C.pink) }, -- |:substitute| replacement text highlighting + LineNr = { fg = C.surface1 }, -- Line number for ":number" and ":#" commands, and when 'number' or 'relativenumber' option is set. + CursorLineNr = { fg = C.lavender }, -- Like LineNr when 'cursorline' or 'relativenumber' is set for the cursor line. highlights the number in numberline. + MatchParen = { fg = C.peach, bg = C.surface1, style = { "bold" } }, -- The character under the cursor or just before it, if it is a paired bracket, and its match. |pi_paren.txt| + ModeMsg = { fg = C.text, style = { "bold" } }, -- 'showmode' message (e.g., "-- INSERT -- ") + -- MsgArea = { fg = C.text }, -- Area for messages and cmdline, don't set this highlight because of https://github.com/neovim/neovim/issues/17832 + MsgSeparator = {}, -- Separator for scrolled messages, `msgsep` flag of 'display' + MoreMsg = { fg = C.blue }, -- |more-prompt| + NonText = { fg = C.overlay0 }, -- '@' at the end of the window, characters from 'showbreak' and other characters that do not really exist in the text (e.g., ">" displayed when a double-wide character doesn't fit at the end of the line). See also |hl-EndOfBuffer|. + Normal = { fg = C.text, bg = O.transparent_background and C.none or C.base }, -- normal text + NormalNC = { + fg = C.text, + bg = (O.transparent_background and O.dim_inactive.enabled and C.dim) + or (O.dim_inactive.enabled and C.dim) + or (O.transparent_background and C.none) + or C.base, + }, -- normal text in non-current windows + NormalSB = { fg = C.text, bg = C.crust }, -- normal text in non-current windows + NormalFloat = { fg = C.text, bg = (O.transparent_background and vim.o.winblend == 0) and C.none or C.mantle }, -- Normal text in floating windows. + FloatBorder = { fg = C.blue }, + FloatTitle = { fg = C.subtext0 }, -- Title of floating windows + Pmenu = { + bg = (O.transparent_background and vim.o.pumblend == 0) and C.none or U.darken(C.surface0, 0.8, C.crust), + fg = C.overlay2, + }, -- Popup menu: normal item. + PmenuSel = { bg = C.surface1, style = { "bold" } }, -- Popup menu: selected item. + PmenuSbar = { bg = C.surface1 }, -- Popup menu: scrollbar. + PmenuThumb = { bg = C.overlay0 }, -- Popup menu: Thumb of the scrollbar. + Question = { fg = C.blue }, -- |hit-enter| prompt and yes/no questions + QuickFixLine = { bg = C.surface1, style = { "bold" } }, -- Current |quickfix| item in the quickfix window. Combined with |hl-CursorLine| when the cursor is there. + Search = { bg = U.darken(C.sky, 0.30, C.base), fg = C.text }, -- Last search pattern highlighting (see 'hlsearch'). Also used for similar items that need to stand out. + IncSearch = { bg = U.darken(C.sky, 0.90, C.base), fg = C.mantle }, -- 'incsearch' highlighting; also used for the text replaced with ":s///c" + CurSearch = { bg = C.red, fg = C.mantle }, -- 'cursearch' highlighting: highlights the current search you're on differently + SpecialKey = { link = "NonText" }, -- Unprintable characters: text displayed differently from what it really is. But not 'listchars' textspace. |hl-Whitespace| + SpellBad = { sp = C.red, style = { "undercurl" } }, -- Word that is not recognized by the spellchecker. |spell| Combined with the highlighting used otherwise. + SpellCap = { sp = C.yellow, style = { "undercurl" } }, -- Word that should start with a capital. |spell| Combined with the highlighting used otherwise. + SpellLocal = { sp = C.blue, style = { "undercurl" } }, -- Word that is recognized by the spellchecker as one that is used in another region. |spell| Combined with the highlighting used otherwise. + SpellRare = { sp = C.green, style = { "undercurl" } }, -- Word that is recognized by the spellchecker as one that is hardly ever used. |spell| Combined with the highlighting used otherwise. + StatusLine = { fg = C.text, bg = O.transparent_background and C.none or C.mantle }, -- status line of current window + StatusLineNC = { fg = C.surface1, bg = O.transparent_background and C.none or C.mantle }, -- status lines of not-current windows Note: if this is equal to "StatusLine" Vim will use "^^^" in the status line of the current window. + TabLine = { bg = C.mantle, fg = C.surface1 }, -- tab pages line, not active tab page label + TabLineFill = {}, -- tab pages line, where there are no labels + TabLineSel = { fg = C.green, bg = C.surface1 }, -- tab pages line, active tab page label + Title = { fg = C.blue, style = { "bold" } }, -- titles for output from ":set all", ":autocmd" etc. + Visual = { bg = C.surface1, style = { "bold" } }, -- Visual mode selection + VisualNOS = { bg = C.surface1, style = { "bold" } }, -- Visual mode selection when vim is "Not Owning the Selection". + WarningMsg = { fg = C.yellow }, -- warning messages + Whitespace = { fg = C.surface1 }, -- "nbsp", "space", "tab" and "trail" in 'listchars' + WildMenu = { bg = C.overlay0 }, -- current match in 'wildmenu' completion + WinBar = { fg = C.rosewater }, + WinBarNC = { link = "WinBar" }, + WinSeparator = { fg = O.transparent_background and C.surface1 or C.crust }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/NormalNvim.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/NormalNvim.lua new file mode 100644 index 00000000..7e711efd --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/NormalNvim.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + MarkdownTask = { fg = C.teal, style = { "bold" } }, + MarkdownTodo = { fg = C.flamingo, style = { "bold" } }, + MarkdownNote = { fg = C.red, style = { "bold" } }, + MarkdownSee = { fg = C.blue, style = { "bold" } }, + MarkdownCheck = { fg = C.green, style = { "bold" } }, + MarkdownURL = { fg = C.lavender, style = { "bold" } }, + MarkdownExample = { fg = C.mauve, style = { "bold" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/aerial.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/aerial.lua new file mode 100644 index 00000000..a0f5b0cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/aerial.lua @@ -0,0 +1,36 @@ +local M = {} + +function M.get() + return { + AerialLine = { fg = C.yellow, bg = C.none }, + AerialGuide = { fg = C.overlay2 }, + AerialBooleanIcon = { link = "@boolean" }, + AerialClassIcon = { link = "@type" }, + AerialConstantIcon = { link = "@constant" }, + AerialConstructorIcon = { link = "@constructor" }, + AerialFieldIcon = { link = "@field" }, + AerialFunctionIcon = { link = "@function" }, + AerialMethodIcon = { link = "@method" }, + AerialNamespaceIcon = { link = "@namespace" }, + AerialNumberIcon = { link = "@number" }, + AerialOperatorIcon = { link = "@operator" }, + AerialTypeParameterIcon = { link = "@type" }, + AerialPropertyIcon = { link = "@property" }, + AerialStringIcon = { link = "@string" }, + AerialVariableIcon = { link = "@constant" }, + AerialEnumMemberIcon = { link = "@field" }, + AerialEnumIcon = { link = "@type" }, + AerialFileIcon = { link = "@text.uri" }, + AerialModuleIcon = { link = "@namespace" }, + AerialPackageIcon = { link = "@namespace" }, + AerialInterfaceIcon = { link = "@type" }, + AerialStructIcon = { link = "@type" }, + AerialEventIcon = { link = "@type" }, + AerialArrayIcon = { link = "@constant" }, + AerialObjectIcon = { link = "@type" }, + AerialKeyIcon = { link = "@type" }, + AerialNullIcon = { link = "@type" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/alpha.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/alpha.lua new file mode 100644 index 00000000..8839b11f --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/alpha.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + return { + AlphaShortcut = { fg = C.green }, + AlphaHeader = { fg = C.blue }, + AlphaHeaderLabel = { fg = C.peach }, + AlphaButtons = { fg = C.lavender }, + AlphaFooter = { fg = C.yellow, style = { "italic" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/barbar.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/barbar.lua new file mode 100644 index 00000000..6926ccf8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/barbar.lua @@ -0,0 +1,25 @@ +local M = {} + +function M.get() + return { + BufferCurrent = { bg = C.surface1, fg = C.text }, + BufferCurrentIndex = { bg = C.surface1, fg = C.blue }, + BufferCurrentMod = { bg = C.surface1, fg = C.yellow }, + BufferCurrentSign = { bg = C.surface1, fg = C.blue }, + BufferCurrentTarget = { bg = C.surface1, fg = C.red }, + BufferVisible = { bg = C.mantle, fg = C.text }, + BufferVisibleIndex = { bg = C.mantle, fg = C.blue }, + BufferVisibleMod = { bg = C.mantle, fg = C.yellow }, + BufferVisibleSign = { bg = C.mantle, fg = C.blue }, + BufferVisibleTarget = { bg = C.mantle, fg = C.red }, + BufferInactive = { bg = C.mantle, fg = C.overlay0 }, + BufferInactiveIndex = { bg = C.mantle, fg = C.overlay0 }, + BufferInactiveMod = { bg = C.mantle, fg = C.yellow }, + BufferInactiveSign = { bg = C.mantle, fg = C.blue }, + BufferInactiveTarget = { bg = C.mantle, fg = C.red }, + BufferTabpages = { bg = C.mantle, fg = C.none }, + BufferTabpage = { bg = C.mantle, fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/beacon.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/beacon.lua new file mode 100644 index 00000000..5f8280c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/beacon.lua @@ -0,0 +1,9 @@ +local M = {} + +function M.get() + return { + Beacon = { bg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/bufferline.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/bufferline.lua new file mode 100644 index 00000000..454fe0d2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/bufferline.lua @@ -0,0 +1,117 @@ +local M = {} +local ctp = require "catppuccin" +local O = ctp.options + +function M.get(user_config) + user_config = user_config or {} + -- Backward compatibility + if O.integrations.bufferline then return {} end + return function() + local C = require("catppuccin.palettes").get_palette() + local transparent_background = O.transparent_background + local bg_highlight = (transparent_background and O.dim_inactive.enabled and C.dim) + or (transparent_background and "NONE") + or (O.dim_inactive.enabled and C.dim) + or C.crust + + local active_bg = transparent_background and "NONE" or C.base + local inactive_bg = transparent_background and "NONE" or C.mantle + + local separator_fg = O.transparent_background and C.surface1 or C.crust + + local styles = user_config.styles or { "bold", "italic" } + + local highlights = { + -- buffers + background = { bg = inactive_bg }, + buffer_visible = { fg = C.surface1, bg = inactive_bg }, + buffer_selected = { fg = C.text, bg = active_bg, style = styles }, -- current + -- Duplicate + duplicate_selected = { fg = C.text, bg = active_bg, style = styles }, + duplicate_visible = { fg = C.surface1, bg = inactive_bg, style = styles }, + duplicate = { fg = C.surface1, bg = inactive_bg, style = styles }, + -- tabs + tab = { fg = C.surface1, bg = inactive_bg }, + tab_selected = { fg = C.sky, bg = active_bg, bold = true }, + tab_separator = { fg = separator_fg, bg = inactive_bg }, + tab_separator_selected = { fg = separator_fg, bg = active_bg }, + + tab_close = { fg = C.red, bg = inactive_bg }, + indicator_selected = { fg = C.peach, bg = active_bg, style = styles }, + -- separators + separator = { fg = separator_fg, bg = inactive_bg }, + separator_visible = { fg = separator_fg, bg = inactive_bg }, + separator_selected = { fg = separator_fg, bg = active_bg }, + offset_separator = { fg = separator_fg, bg = active_bg }, + -- close buttons + close_button = { fg = C.surface1, bg = inactive_bg }, + close_button_visible = { fg = C.surface1, bg = inactive_bg }, + close_button_selected = { fg = C.red, bg = active_bg }, + -- Empty fill + fill = { bg = bg_highlight }, + -- Numbers + numbers = { fg = C.subtext0, bg = inactive_bg }, + numbers_visible = { fg = C.subtext0, bg = inactive_bg }, + numbers_selected = { fg = C.subtext0, bg = active_bg, style = styles }, + -- Errors + error = { fg = C.red, bg = inactive_bg }, + error_visible = { fg = C.red, bg = inactive_bg }, + error_selected = { fg = C.red, bg = active_bg, style = styles }, + error_diagnostic = { fg = C.red, bg = inactive_bg }, + error_diagnostic_visible = { fg = C.red, bg = inactive_bg }, + error_diagnostic_selected = { fg = C.red, bg = active_bg }, + -- Warnings + warning = { fg = C.yellow, bg = inactive_bg }, + warning_visible = { fg = C.yellow, bg = inactive_bg }, + warning_selected = { fg = C.yellow, bg = active_bg, style = styles }, + warning_diagnostic = { fg = C.yellow, bg = inactive_bg }, + warning_diagnostic_visible = { fg = C.yellow, bg = inactive_bg }, + warning_diagnostic_selected = { fg = C.yellow, bg = active_bg }, + -- Infos + info = { fg = C.sky, bg = inactive_bg }, + info_visible = { fg = C.sky, bg = inactive_bg }, + info_selected = { fg = C.sky, bg = active_bg, style = styles }, + info_diagnostic = { fg = C.sky, bg = inactive_bg }, + info_diagnostic_visible = { fg = C.sky, bg = inactive_bg }, + info_diagnostic_selected = { fg = C.sky, bg = active_bg }, + -- Hint + hint = { fg = C.teal, bg = inactive_bg }, + hint_visible = { fg = C.teal, bg = inactive_bg }, + hint_selected = { fg = C.teal, bg = active_bg, style = styles }, + hint_diagnostic = { fg = C.teal, bg = inactive_bg }, + hint_diagnostic_visible = { fg = C.teal, bg = inactive_bg }, + hint_diagnostic_selected = { fg = C.teal, bg = active_bg }, + -- Diagnostics + diagnostic = { fg = C.subtext0, bg = inactive_bg }, + diagnostic_visible = { fg = C.subtext0, bg = inactive_bg }, + diagnostic_selected = { fg = C.subtext0, bg = active_bg, style = styles }, + -- Modified + modified = { fg = C.peach, bg = inactive_bg }, + modified_selected = { fg = C.peach, bg = active_bg }, + } + + local user_highlights = user_config.custom or {} + highlights = + vim.tbl_deep_extend("keep", user_highlights[ctp.flavour] or {}, user_highlights.all or {}, highlights) + + for _, color in pairs(highlights) do + -- Because default is gui=bold,italic + color.italic = false + color.bold = false + + if color.style then + for _, style in pairs(color.style) do + color[style] = true + if O.no_italic and style == "italic" then color[style] = false end + if O.no_bold and style == "bold" then color[style] = false end + if O.no_underline and style == "underline" then color[style] = false end + end + end + color.style = nil + end + + return highlights + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/cmp.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/cmp.lua new file mode 100644 index 00000000..677a90f1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/cmp.lua @@ -0,0 +1,42 @@ +local M = {} + +function M.get() + return { + CmpItemAbbr = { fg = C.overlay2 }, + CmpItemAbbrDeprecated = { fg = C.overlay0, style = { "strikethrough" } }, + CmpItemKind = { fg = C.blue }, + CmpItemMenu = { fg = C.text }, + CmpItemAbbrMatch = { fg = C.text, style = { "bold" } }, + CmpItemAbbrMatchFuzzy = { fg = C.text, style = { "bold" } }, + + -- kind support + CmpItemKindSnippet = { fg = C.mauve }, + CmpItemKindKeyword = { fg = C.red }, + CmpItemKindText = { fg = C.teal }, + CmpItemKindMethod = { fg = C.blue }, + CmpItemKindConstructor = { fg = C.blue }, + CmpItemKindFunction = { fg = C.blue }, + CmpItemKindFolder = { fg = C.blue }, + CmpItemKindModule = { fg = C.blue }, + CmpItemKindConstant = { fg = C.peach }, + CmpItemKindField = { fg = C.green }, + CmpItemKindProperty = { fg = C.green }, + CmpItemKindEnum = { fg = C.green }, + CmpItemKindUnit = { fg = C.green }, + CmpItemKindClass = { fg = C.yellow }, + CmpItemKindVariable = { fg = C.flamingo }, + CmpItemKindFile = { fg = C.blue }, + CmpItemKindInterface = { fg = C.yellow }, + CmpItemKindColor = { fg = C.red }, + CmpItemKindReference = { fg = C.red }, + CmpItemKindEnumMember = { fg = C.red }, + CmpItemKindStruct = { fg = C.blue }, + CmpItemKindValue = { fg = C.peach }, + CmpItemKindEvent = { fg = C.blue }, + CmpItemKindOperator = { fg = C.blue }, + CmpItemKindTypeParameter = { fg = C.blue }, + CmpItemKindCopilot = { fg = C.teal }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/coc_nvim.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/coc_nvim.lua new file mode 100644 index 00000000..d9d8c3a4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/coc_nvim.lua @@ -0,0 +1,19 @@ +local M = {} + +function M.get() + return { + -- These groups are for the coc.nvim, see ":h coc-highlights" + -- Custom popup menu since coc v0.0.82 + CocMenuSel = { link = "PmenuSel" }, + CocPumSearch = { fg = C.sky, style = { "bold" } }, + + -- Inlay hints + CocInlayHint = { + -- fg of `Comment` + fg = C.overlay0, + style = { "italic" }, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/colorful_winsep.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/colorful_winsep.lua new file mode 100644 index 00000000..d4d5f885 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/colorful_winsep.lua @@ -0,0 +1,9 @@ +local M = {} + +function M.get() + return { + NvimSeparator = { bg = C.base, fg = C[O.integrations.colorful_winsep.color] }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dap.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dap.lua new file mode 100644 index 00000000..94c5f297 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dap.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + return { + DapBreakpoint = { fg = C.red }, + DapBreakpointCondition = { fg = C.yellow }, + DapBreakpointRejected = { fg = C.mauve }, + DapLogPoint = { fg = C.sky }, + DapStopped = { fg = C.maroon }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dap_ui.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dap_ui.lua new file mode 100644 index 00000000..88643e96 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dap_ui.lua @@ -0,0 +1,39 @@ +local M = {} + +function M.get() + return { + DapUIScope = { fg = C.sky }, + DapUIType = { fg = C.mauve }, + DapUIValue = { fg = C.sky }, + DapUIVariable = { fg = C.text }, + DapUIModifiedValue = { fg = C.peach }, + DapUIDecoration = { fg = C.sky }, + DapUIThread = { fg = C.green }, + DapUIStoppedThread = { fg = C.sky }, + DapUISource = { fg = C.lavender }, + DapUILineNumber = { fg = C.sky }, + DapUIFloatBorder = { fg = C.sky }, + + DapUIWatchesEmpty = { fg = C.maroon }, + DapUIWatchesValue = { fg = C.green }, + DapUIWatchesError = { fg = C.maroon }, + + DapUIBreakpointsPath = { fg = C.sky }, + DapUIBreakpointsInfo = { fg = C.green }, + DapUIBreakpointsCurrentLine = { fg = C.green, style = { "bold" } }, + DapUIBreakpointsDisabledLine = { fg = C.surface2 }, + + DapUIStepOver = { fg = C.blue }, + DapUIStepInto = { fg = C.blue }, + DapUIStepBack = { fg = C.blue }, + DapUIStepOut = { fg = C.blue }, + DapUIStop = { fg = C.red }, + DapUIPlayPause = { fg = C.green }, + DapUIRestart = { fg = C.green }, + DapUIUnavailable = { fg = C.surface1 }, + + DapUIWinSelect = { fg = C.peach }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dashboard.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dashboard.lua new file mode 100644 index 00000000..52953db4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dashboard.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return { + DashboardShortCut = { fg = C.pink }, + DashboardHeader = { fg = C.blue }, + DashboardCenter = { fg = C.green }, + DashboardFooter = { fg = C.yellow, style = { "italic" } }, + DashboardMruTitle = { fg = C.sky }, + DashboardProjectTitle = { fg = C.sky }, + DashboardFiles = { fg = C.lavender }, + DashboardKey = { fg = C.peach }, + DashboardDesc = { fg = C.blue }, + DashboardIcon = { fg = C.pink, bold = true }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/diffview.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/diffview.lua new file mode 100644 index 00000000..b14b482d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/diffview.lua @@ -0,0 +1,41 @@ +local M = {} + +function M.get() + return { + DiffviewDim1 = { link = "Comment" }, + DiffviewPrimary = { fg = C.blue }, + DiffviewSecondary = { fg = C.green }, + DiffviewNormal = { fg = C.text, bg = O.transparent_background and C.none or C.mantle }, + DiffviewWinSeparator = { + fg = O.transparent_background and C.surface1 or C.base, + bg = O.transparent_background and C.none or C.base, + }, + DiffviewFilePanelTitle = { fg = C.blue, style = { "bold" } }, + DiffviewFilePanelCounter = { fg = C.text }, + DiffviewFilePanelRootPath = { fg = C.lavender, style = { "bold" } }, + DiffviewFilePanelFileName = { fg = C.text }, + DiffviewFilePanelSelected = { fg = C.yellow }, + DiffviewFilePanelPath = { link = "Comment" }, + DiffviewFilePanelInsertions = { fg = C.green }, + DiffviewFilePanelDeletions = { fg = C.red }, + DiffviewFilePanelConflicts = { fg = C.yellow }, + DiffviewFolderName = { fg = C.blue, style = { "bold" } }, + DiffviewFolderSign = { fg = C.blue }, + DiffviewHash = { fg = C.flamingo }, + DiffviewReference = { fg = C.blue, style = { "bold" } }, + DiffviewReflogSelector = { fg = C.pink }, + DiffviewStatusAdded = { fg = C.green }, + DiffviewStatusUntracked = { fg = C.green }, + DiffviewStatusModified = { fg = C.yellow }, + DiffviewStatusRenamed = { fg = C.yellow }, + DiffviewStatusCopied = { fg = C.yellow }, + DiffviewStatusTypeChange = { fg = C.yellow }, + DiffviewStatusUnmerged = { fg = C.yellow }, + DiffviewStatusUnknown = { fg = C.red }, + DiffviewStatusDeleted = { fg = C.red }, + DiffviewStatusBroken = { fg = C.red }, + DiffviewStatusIgnored = { fg = C.overlay0 }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dropbar.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dropbar.lua new file mode 100644 index 00000000..712a9148 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/dropbar.lua @@ -0,0 +1,69 @@ +local M = {} + +function M.get() + local color = O.integrations.dropbar.color_mode + return { + DropBarMenuHoverEntry = { link = "Visual" }, + DropBarMenuHoverIcon = { reverse = true }, + DropBarMenuHoverSymbol = { bold = true }, + DropBarIconUISeparator = { fg = C.overlay1 }, + DropBarKindArray = color and { link = "DropBarIconKindArray" } or { fg = C.text }, + DropBarKindBoolean = color and { link = "DropBarIconKindBoolean" } or { fg = C.text }, + DropBarKindBreakStatement = color and { link = "DropBarIconKindBreakStatement" } or { fg = C.text }, + DropBarKindCall = color and { link = "DropBarIconKindCall" } or { fg = C.text }, + DropBarKindCaseStatement = color and { link = "DropBarIconKindCaseStatement" } or { fg = C.text }, + DropBarKindClass = color and { link = "DropBarIconKindClass" } or { fg = C.text }, + DropBarKindConstant = color and { link = "DropBarIconKindConstant" } or { fg = C.text }, + DropBarKindConstructor = color and { link = "DropBarIconKindConstructor" } or { fg = C.text }, + DropBarKindContinueStatement = color and { link = "DropBarIconKindContinueStatement" } or { fg = C.text }, + DropBarKindDeclaration = color and { link = "DropBarIconKindDeclaration" } or { fg = C.text }, + DropBarKindDelete = color and { link = "DropBarIconKindDelete" } or { fg = C.text }, + DropBarKindDoStatement = color and { link = "DropBarIconKindDoStatement" } or { fg = C.text }, + DropBarKindElseStatement = color and { link = "DropBarIconKindElseStatement" } or { fg = C.text }, + DropBarKindEnum = color and { link = "DropBarIconKindEnum" } or { fg = C.text }, + DropBarKindEnumMember = color and { link = "DropBarIconKindEnumMember" } or { fg = C.text }, + DropBarKindEvent = color and { link = "DropBarIconKindEvent" } or { fg = C.text }, + DropBarKindField = color and { link = "DropBarIconKindField" } or { fg = C.text }, + DropBarKindFile = color and { link = "DropBarIconKindFile" } or { fg = C.text }, + DropBarKindFolder = color and { link = "DropBarIconKindFolder" } or { fg = C.text }, + DropBarKindForStatement = color and { link = "DropBarIconKindForStatement" } or { fg = C.text }, + DropBarKindFunction = color and { link = "DropBarIconKindFunction" } or { fg = C.text }, + DropBarKindIdentifier = color and { link = "DropBarIconKindIdentifier" } or { fg = C.text }, + DropBarKindIfStatement = color and { link = "DropBarIconKindIfStatement" } or { fg = C.text }, + DropBarKindInterface = color and { link = "DropBarIconKindInterface" } or { fg = C.text }, + DropBarKindKeyword = color and { link = "DropBarIconKindKeyword" } or { fg = C.text }, + DropBarKindList = color and { link = "DropBarIconKindList" } or { fg = C.text }, + DropBarKindMacro = color and { link = "DropBarIconKindMacro" } or { fg = C.text }, + DropBarKindMarkdownH1 = color and { link = "DropBarIconKindMarkdownH1" } or { fg = C.text }, + DropBarKindMarkdownH2 = color and { link = "DropBarIconKindMarkdownH2" } or { fg = C.text }, + DropBarKindMarkdownH3 = color and { link = "DropBarIconKindMarkdownH3" } or { fg = C.text }, + DropBarKindMarkdownH4 = color and { link = "DropBarIconKindMarkdownH4" } or { fg = C.text }, + DropBarKindMarkdownH5 = color and { link = "DropBarIconKindMarkdownH5" } or { fg = C.text }, + DropBarKindMarkdownH6 = color and { link = "DropBarIconKindMarkdownH6" } or { fg = C.text }, + DropBarKindMethod = color and { link = "DropBarIconKindMethod" } or { fg = C.text }, + DropBarKindModule = color and { link = "DropBarIconKindModule" } or { fg = C.text }, + DropBarKindNamespace = color and { link = "DropBarIconKindNamespace" } or { fg = C.text }, + DropBarKindNull = color and { link = "DropBarIconKindNull" } or { fg = C.text }, + DropBarKindNumber = color and { link = "DropBarIconKindNumber" } or { fg = C.text }, + DropBarKindObject = color and { link = "DropBarIconKindObject" } or { fg = C.text }, + DropBarKindOperator = color and { link = "DropBarIconKindOperator" } or { fg = C.text }, + DropBarKindPackage = color and { link = "DropBarIconKindPackage" } or { fg = C.text }, + DropBarKindProperty = color and { link = "DropBarIconKindProperty" } or { fg = C.text }, + DropBarKindReference = color and { link = "DropBarIconKindReference" } or { fg = C.text }, + DropBarKindRepeat = color and { link = "DropBarIconKindRepeat" } or { fg = C.text }, + DropBarKindScope = color and { link = "DropBarIconKindScope" } or { fg = C.text }, + DropBarKindSpecifier = color and { link = "DropBarIconKindSpecifier" } or { fg = C.text }, + DropBarKindStatement = color and { link = "DropBarIconKindStatement" } or { fg = C.text }, + DropBarKindString = color and { link = "DropBarIconKindString" } or { fg = C.text }, + DropBarKindStruct = color and { link = "DropBarIconKindStruct" } or { fg = C.text }, + DropBarKindSwitchStatement = color and { link = "DropBarIconKindSwitchStatement" } or { fg = C.text }, + DropBarKindType = color and { link = "DropBarIconKindType" } or { fg = C.text }, + DropBarKindTypeParameter = color and { link = "DropBarIconKindTypeParameter" } or { fg = C.text }, + DropBarKindUnit = color and { link = "DropBarIconKindUnit" } or { fg = C.text }, + DropBarKindValue = color and { link = "DropBarIconKindValue" } or { fg = C.text }, + DropBarKindVariable = color and { link = "DropBarIconKindVariable" } or { fg = C.text }, + DropBarKindWhileStatement = color and { link = "DropBarIconKindWhileStatement" } or { fg = C.text }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/feline.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/feline.lua new file mode 100644 index 00000000..f0d9c024 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/feline.lua @@ -0,0 +1,501 @@ +local M = {} + +local C = require("catppuccin.palettes").get_palette() +local lsp = require "feline.providers.lsp" + +local assets = { + left_separator = "", + right_separator = "", + mode_icon = "", + dir = "󰉖", + file = "󰈙", + lsp = { + server = "󰅡", + error = "", + warning = "", + info = "", + hint = "", + }, + git = { + branch = "", + added = "", + changed = "", + removed = "", + }, +} + +local sett = { + text = C.mantle, + bkg = C.crust, + diffs = C.mauve, + extras = C.overlay1, + curr_file = C.maroon, + curr_dir = C.flamingo, + show_modified = false, +} + +if require("catppuccin").flavour == "latte" then + local latte = require("catppuccin.palettes").get_palette "latte" + sett.text = latte.base + sett.bkg = latte.crust +end + +if require("catppuccin").options.transparent_background then sett.bkg = "NONE" end + +local mode_colors = { + ["n"] = { "NORMAL", C.lavender }, + ["no"] = { "N-PENDING", C.lavender }, + ["i"] = { "INSERT", C.green }, + ["ic"] = { "INSERT", C.green }, + ["t"] = { "TERMINAL", C.green }, + ["v"] = { "VISUAL", C.flamingo }, + ["V"] = { "V-LINE", C.flamingo }, + [""] = { "V-BLOCK", C.flamingo }, + ["R"] = { "REPLACE", C.maroon }, + ["Rv"] = { "V-REPLACE", C.maroon }, + ["s"] = { "SELECT", C.maroon }, + ["S"] = { "S-LINE", C.maroon }, + [""] = { "S-BLOCK", C.maroon }, + ["c"] = { "COMMAND", C.peach }, + ["cv"] = { "COMMAND", C.peach }, + ["ce"] = { "COMMAND", C.peach }, + ["r"] = { "PROMPT", C.teal }, + ["rm"] = { "MORE", C.teal }, + ["r?"] = { "CONFIRM", C.mauve }, + ["!"] = { "SHELL", C.green }, +} + +local view = { + lsp = { + progress = true, + name = false, + exclude_lsp_names = {}, + separator = "|", + }, +} + +local is_lsp_in_excluded_list = function(lsp_name) + for _, excluded_lsp in ipairs(view.lsp.exclude_lsp_names) do + if lsp_name == excluded_lsp then return true end + end + return false +end + +function M.setup(opts) + if opts then + opts.assets = opts.assets or {} + opts.sett = opts.sett or {} + opts.mode_colors = opts.mode_colors or {} + opts.view = opts.view or {} + else + opts = { + assets = {}, + sett = {}, + mode_colors = {}, + view = {}, + } + end + assets = vim.tbl_deep_extend("force", assets, opts.assets) + sett = vim.tbl_deep_extend("force", sett, opts.sett) + mode_colors = vim.tbl_deep_extend("force", mode_colors, opts.mode_colors) + view = vim.tbl_deep_extend("force", view, opts.view) +end + +function M.get() + local shortline = false + + local components = { + active = { {}, {}, {} }, -- left, center, right + inactive = { {} }, + } + + local function is_enabled(min_width) + if shortline then return true end + + return vim.api.nvim_win_get_width(0) > min_width + end + + -- global components + local invi_sep = { + str = " ", + hl = { + fg = sett.bkg, + bg = sett.bkg, + }, + } + + -- helpers + local function any_git_changes() + local gst = vim.b.gitsigns_status_dict -- git stats + if gst then + if + gst["added"] and gst["added"] > 0 + or gst["removed"] and gst["removed"] > 0 + or gst["changed"] and gst["changed"] > 0 + then + return true + end + end + return false + end + + -- #################### STATUSLINE -> + + -- ######## Left + + -- Current vi mode ------> + local vi_mode_hl = function() + return { + fg = sett.text, + bg = mode_colors[vim.fn.mode()][2], + style = "bold", + } + end + + components.active[1][1] = { + provider = " " .. assets.mode_icon .. " ", + hl = function() + return { + fg = sett.text, + bg = mode_colors[vim.fn.mode()][2], + } + end, + } + + components.active[1][2] = { + provider = function() return mode_colors[vim.fn.mode()][1] .. " " end, + hl = vi_mode_hl, + } + + -- there is a dilemma: we need to hide Diffs if there is no git info. We can do that, but this will + -- leave the right_separator colored with purple, and since we can't change the color conditonally + -- then the solution is to create two right_separators: one with a mauve sett.bkg and the other one normal + -- sett.bkg; both have the same fg (vi mode). The mauve one appears if there is git info, else the one with + -- the normal sett.bkg appears. Fixed :) + + -- enable if git diffs are not available + components.active[1][3] = { + provider = assets.right_separator, + hl = function() + return { + fg = mode_colors[vim.fn.mode()][2], + bg = sett.bkg, + } + end, + enabled = function() return not any_git_changes() end, + } + + -- enable if git diffs are available + components.active[1][4] = { + provider = assets.right_separator, + hl = function() + return { + fg = mode_colors[vim.fn.mode()][2], + bg = sett.diffs, + } + end, + enabled = function() return any_git_changes() end, + } + -- Current vi mode ------> + + -- Diffs ------> + components.active[1][5] = { + provider = "git_diff_added", + hl = { + fg = sett.text, + bg = sett.diffs, + }, + icon = " " .. assets.git.added .. " ", + } + + components.active[1][6] = { + provider = "git_diff_changed", + hl = { + fg = sett.text, + bg = sett.diffs, + }, + icon = " " .. assets.git.changed .. " ", + } + + components.active[1][7] = { + provider = "git_diff_removed", + hl = { + fg = sett.text, + bg = sett.diffs, + }, + icon = " " .. assets.git.removed .. " ", + } + + components.active[1][8] = { + provider = " ", + hl = { + fg = sett.bkg, + bg = sett.diffs, + }, + enabled = function() return any_git_changes() end, + } + + components.active[1][9] = { + provider = assets.right_separator, + hl = { + fg = sett.diffs, + bg = sett.bkg, + }, + enabled = function() return any_git_changes() end, + } + -- Diffs ------> + + -- Extras ------> + + -- file progress + components.active[1][10] = { + provider = function() + local current_line = vim.fn.line "." + local total_line = vim.fn.line "$" + + if current_line == 1 then + return "Top" + elseif current_line == vim.fn.line "$" then + return "Bot" + end + local result, _ = math.modf((current_line / total_line) * 100) + return result .. "%%" + end, + -- enabled = shortline or function(winid) + -- return vim.api.nvim_win_get_width(winid) > 90 + -- end, + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + left_sep = invi_sep, + } + + -- position + components.active[1][11] = { + provider = "position", + -- enabled = shortline or function(winid) + -- return vim.api.nvim_win_get_width(winid) > 90 + -- end, + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + left_sep = invi_sep, + } + + -- macro + components.active[1][12] = { + provider = "macro", + enabled = function() return vim.api.nvim_get_option "cmdheight" == 0 end, + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + left_sep = invi_sep, + } + + -- search count + components.active[1][13] = { + provider = "search_count", + enabled = function() return vim.api.nvim_get_option "cmdheight" == 0 end, + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + left_sep = invi_sep, + } + -- Extras ------> + + -- ######## Left + + -- ######## Center + + -- Diagnostics ------> + -- workspace loader + components.active[2][1] = { + provider = function() + if vim.lsp.status then return "" end + local Lsp = vim.lsp.util.get_progress_messages()[1] + + if Lsp then + local msg = Lsp.message or "" + local percentage = Lsp.percentage + if not percentage then return "" end + local title = Lsp.title or "" + local spinners = { + "", + "󰀚", + "", + } + local success_icon = { + "", + "", + "", + } + local ms = vim.loop.hrtime() / 1000000 + local frame = math.floor(ms / 120) % #spinners + + if percentage >= 70 then + return string.format(" %%<%s %s %s (%s%%%%) ", success_icon[frame + 1], title, msg, percentage) + end + + return string.format(" %%<%s %s %s (%s%%%%) ", spinners[frame + 1], title, msg, percentage) + end + + return "" + end, + enabled = is_enabled(80) and view.lsp.progress == true, + hl = { + fg = C.rosewater, + bg = sett.bkg, + }, + } + + -- general diagnostics (errors, warnings. info and hints) + components.active[2][2] = { + provider = "diagnostic_errors", + enabled = function() return lsp.diagnostics_exist(vim.diagnostic.severity.ERROR) end, + + hl = { + fg = C.red, + bg = sett.bkg, + }, + icon = " " .. assets.lsp.error .. " ", + } + + components.active[2][3] = { + provider = "diagnostic_warnings", + enabled = function() return lsp.diagnostics_exist(vim.diagnostic.severity.WARN) end, + hl = { + fg = C.yellow, + bg = sett.bkg, + }, + icon = " " .. assets.lsp.warning .. " ", + } + + components.active[2][4] = { + provider = "diagnostic_info", + enabled = function() return lsp.diagnostics_exist(vim.diagnostic.severity.INFO) end, + hl = { + fg = C.sky, + bg = sett.bkg, + }, + icon = " " .. assets.lsp.info .. " ", + } + + components.active[2][5] = { + provider = "diagnostic_hints", + enabled = function() return lsp.diagnostics_exist(vim.diagnostic.severity.HINT) end, + hl = { + fg = C.rosewater, + bg = sett.bkg, + }, + icon = " " .. assets.lsp.hint .. " ", + } + -- Diagnostics ------> + + -- ######## Center + + -- ######## Right + + components.active[3][1] = { + provider = "git_branch", + enabled = is_enabled(70), + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + icon = assets.git.branch .. " ", + right_sep = invi_sep, + } + + components.active[3][2] = { + provider = function() + local active_clients = vim.lsp.get_active_clients { bufnr = 0 } + + -- show an indicator that we have running lsps + if view.lsp.name == false and next(active_clients) ~= nil then return assets.lsp.server .. " " .. "Lsp" end + + -- show the actual name of the runing lsps + local index = 0 + local lsp_names = "" + for _, lsp_config in ipairs(active_clients) do + if is_lsp_in_excluded_list(lsp_config.name) then goto continue end + + index = index + 1 + if index == 1 then + lsp_names = assets.lsp.server .. " " .. lsp_config.name + else + lsp_names = lsp_names .. view.lsp.separator .. lsp_config.name + end + + ::continue:: + end + + return lsp_names + end, + + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + + right_sep = invi_sep, + } + + components.active[3][3] = { + provider = function() + local filename = vim.fn.expand "%:t" + local extension = vim.fn.expand "%:e" + local present, icons = pcall(require, "nvim-web-devicons") + local icon = present and icons.get_icon(filename, extension) or assets.file + return (sett.show_modified and "%m" or "") .. " " .. icon .. " " .. filename .. " " + end, + enabled = is_enabled(70), + hl = { + fg = sett.text, + bg = sett.curr_file, + }, + left_sep = { + str = assets.left_separator, + hl = { + fg = sett.curr_file, + bg = sett.bkg, + }, + }, + } + + components.active[3][4] = { + provider = function() + local dir_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":t") + return " " .. assets.dir .. " " .. dir_name .. " " + end, + enabled = is_enabled(80), + hl = { + fg = sett.text, + bg = sett.curr_dir, + }, + left_sep = { + str = assets.left_separator, + hl = { + fg = sett.curr_dir, + bg = sett.curr_file, + }, + }, + } + -- ######## Right + + -- Inanctive components + components.inactive[1][1] = { + provider = function() return " " .. string.upper(vim.bo.ft) .. " " end, + hl = { + fg = C.overlay2, + bg = C.mantle, + }, + } + + return components +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/fern.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/fern.lua new file mode 100644 index 00000000..4e951d9d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/fern.lua @@ -0,0 +1,9 @@ +local M = {} + +function M.get() + return { + FernBranchText = { fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/fidget.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/fidget.lua new file mode 100644 index 00000000..90882323 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/fidget.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + return { + FidgetTask = { + bg = O.transparent_background and C.none or C.mantle, + fg = C.text, + }, + FidgetTitle = { fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/flash.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/flash.lua new file mode 100644 index 00000000..1a746b6b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/flash.lua @@ -0,0 +1,14 @@ +local M = {} + +function M.get() + local bg = O.transparent_background and C.none or C.base + return { + FlashBackdrop = { fg = C.overlay0 }, + FlashLabel = { fg = C.green, bg = bg, style = { "bold" } }, + FlashMatch = { fg = C.lavender, bg = bg }, + FlashCurrent = { fg = C.peach, bg = bg }, + FlashPrompt = { link = "NormalFloat" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/gitgutter.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/gitgutter.lua new file mode 100644 index 00000000..6a8c4fc8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/gitgutter.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.get() + return { + GitGutterAdd = { fg = C.green }, + GitGutterChange = { fg = C.yellow }, + GitGutterDelete = { fg = C.red }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/gitsigns.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/gitsigns.lua new file mode 100644 index 00000000..6bc89403 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/gitsigns.lua @@ -0,0 +1,53 @@ +local M = {} + +function M.get() + if type(O.integrations.gitsigns) == "boolean" then + O.integrations.gitsigns = { enabled = true, transparent = false } + end + + if O.transparent_background then + return { + GitSignsAdd = { fg = C.green }, -- diff mode: Added line |diff.txt| + GitSignsChange = { fg = C.yellow }, -- diff mode: Changed line |diff.txt| + GitSignsDelete = { fg = C.red }, -- diff mode: Deleted line |diff.txt| + + GitSignsCurrentLineBlame = { fg = C.surface1 }, + + GitSignsAddPreview = O.transparent_background and { fg = U.darken(C.green, 0.72, C.base), bg = C.none } + or { link = "DiffAdd" }, + GitSignsDeletePreview = O.transparent_background and { fg = U.darken(C.red, 0.72, C.base), bg = C.none } + or { link = "DiffDelete" }, + -- for word diff in previews + GitSignsAddInline = O.transparent_background and { + fg = C.green, + bg = C.none, + style = { "bold" }, + } or { link = "DiffAdd" }, + GitSignsDeleteInline = O.transparent_background and { + fg = C.red, + bg = C.none, + style = { "bold" }, + } or { link = "DiffDelete" }, + GitSignsChangeInline = O.transparent_background and { + fg = C.yellow, + bg = C.none, + style = { "bold" }, + } or { link = "DiffChange" }, + + GitSignsDeleteVirtLn = O.transparent_background and { bg = C.none, fg = C.red } or { link = "DiffDelete" }, + } + else + return { + GitSignsAdd = { fg = C.green }, -- diff mode: Added line |diff.txt| + GitSignsChange = { fg = C.yellow }, -- diff mode: Changed line |diff.txt| + GitSignsDelete = { fg = C.red }, -- diff mode: Deleted line |diff.txt| + + GitSignsCurrentLineBlame = { fg = C.surface1 }, + + GitSignsAddPreview = O.transparent_background and { fg = C.green, bg = C.none } or { link = "DiffAdd" }, + GitSignsDeletePreview = O.transparent_background and { fg = C.red, bg = C.none } or { link = "DiffDelete" }, + } + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/harpoon.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/harpoon.lua new file mode 100644 index 00000000..29bc2078 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/harpoon.lua @@ -0,0 +1,10 @@ +local M = {} + +function M.get() + return { + HarpoonWindow = { fg = C.text, bg = O.transparent_background and C.none or C.base }, + HarpoonBorder = { fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/headlines.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/headlines.lua new file mode 100644 index 00000000..2b4f49c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/headlines.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return { + Dash = { fg = C.overlay2, style = { "bold" } }, + Quote = { link = "@markup.strong" }, + CodeBlock = { bg = C.mantle }, + Headline = { link = "Headline1" }, + Headline1 = { bg = C.surface0, fg = C.red }, + Headline2 = { bg = C.surface0, fg = C.peach }, + Headline3 = { bg = C.surface0, fg = C.yellow }, + Headline4 = { bg = C.surface0, fg = C.green }, + Headline5 = { bg = C.surface0, fg = C.sapphire }, + Headline6 = { bg = C.surface0, fg = C.lavender }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/hop.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/hop.lua new file mode 100644 index 00000000..1f989837 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/hop.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + local bg = O.transparent_background and C.none or C.base + return { + HopNextKey = { bg = bg, fg = C.peach, style = { "bold", "underline" } }, + HopNextKey1 = { bg = bg, fg = C.blue, style = { "bold" } }, + HopNextKey2 = { bg = bg, fg = C.teal, style = { "bold", "italic" } }, + HopUnmatched = { bg = bg, fg = C.overlay0 }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/illuminate.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/illuminate.lua new file mode 100644 index 00000000..f214affe --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/illuminate.lua @@ -0,0 +1,14 @@ +local M = {} + +function M.get() + return { + IlluminatedWordText = { bg = U.darken(C.surface1, 0.7, C.base) }, + IlluminatedWordRead = { bg = U.darken(C.surface1, 0.7, C.base) }, + IlluminatedWordWrite = { + bg = U.darken(C.surface1, 0.7, C.base), + style = O.integrations.illuminate.lsp and { "standout" } or {}, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/indent_blankline.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/indent_blankline.lua new file mode 100644 index 00000000..15b94e69 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/indent_blankline.lua @@ -0,0 +1,24 @@ +local M = {} + +function M.get() + local scope_color = O.integrations.indent_blankline.scope_color + + local hi = { + IblIndent = { fg = C.surface0 }, + IblScope = { fg = C[scope_color] or C.text }, + } + + if O.integrations.indent_blankline.colored_indent_levels then + hi["RainbowRed"] = { blend = 0, fg = C.red } + hi["RainbowYellow"] = { blend = 0, fg = C.yellow } + hi["RainbowBlue"] = { blend = 0, fg = C.blue } + hi["RainbowOrange"] = { blend = 0, fg = C.peach } + hi["RainbowGreen"] = { blend = 0, fg = C.green } + hi["RainbowViolet"] = { blend = 0, fg = C.mauve } + hi["RainbowCyan"] = { blend = 0, fg = C.teal } + end + + return hi +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/leap.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/leap.lua new file mode 100644 index 00000000..b9bb2943 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/leap.lua @@ -0,0 +1,24 @@ +local M = {} + +function M.get() + return { + LeapMatch = { + fg = O.transparent_background and C.pink or U.vary_color({ latte = "#222222" }, U.brighten(C.green, 0.3)), + style = { "underline", "nocombine", O.transparent_background and "bold" or nil }, + }, + LeapLabelPrimary = { + fg = O.transparent_background and C.green or U.vary_color({ latte = "#222222" }, C.base), + bg = O.transparent_background and C.none + or U.vary_color({ latte = U.brighten(C.red, 0.4) }, U.brighten(C.green, 0.3)), + style = { "nocombine", O.transparent_background and "bold" or nil }, + }, + LeapLabelSecondary = { + fg = O.transparent_background and C.blue or U.vary_color({ latte = "#222222" }, C.base), + bg = O.transparent_background and C.none or U.vary_color({ latte = U.brighten(C.sky, 0.3) }, C.sky), + style = { "nocombine", O.transparent_background and "bold" or nil }, + }, + LeapBackdrop = { fg = O.transparent_background and C.overlay0 or C.none }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lightspeed.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lightspeed.lua new file mode 100644 index 00000000..c7039ee1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lightspeed.lua @@ -0,0 +1,27 @@ +local M = {} + +function M.get() + return { + LightspeedLabel = { fg = C.red, style = { "bold", "underline" } }, + LightspeedLabelDistant = { + fg = U.vary_color({ latte = C.blue }, C.sky), + style = { "bold", "underline" }, + }, + LightspeedShortcut = { + fg = "#ffffff", + bg = U.increase_saturation(C.red, 0.7), + style = { "bold" }, + }, + LightspeedMaskedChar = { + fg = U.vary_color({ latte = U.lighten(C.pink, 0.7, C.text) }, U.darken(C.pink, 0.7, C.base)), + }, + LightspeedUnlabeledMatch = { fg = C.text, style = { "bold" } }, + LightspeedGreyWash = { link = "Comment" }, + LightspeedUniqueChar = { link = "LightspeedUnlabeledMatch" }, + LightspeedOneCharMatch = { link = "LightspeedShortcut" }, + LightspeedPendingOpArea = { link = "IncSearch" }, + LightspeedCursor = { link = "Cursor" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lsp_saga.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lsp_saga.lua new file mode 100644 index 00000000..28d71212 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lsp_saga.lua @@ -0,0 +1,132 @@ +local M = {} + +function M.get() + return { + SagaTitle = { fg = C.teal, style = { "bold" } }, + SagaBorder = { fg = C.blue, bg = C.none }, + SagaNormal = { bg = O.transparent_background and C.none or C.base }, + SagaToggle = { fg = C.green }, + SagaCount = { fg = C.overlay1 }, + SagaBeacon = { bg = U.darken(C.surface0, 0.8, C.crust) }, + SagaVirtLine = { fg = C.surface2 }, + SagaSpinner = { fg = C.mauve, style = { "bold" } }, + SagaSpinnerTitle = { fg = C.mauve, style = { "bold" } }, + SagaText = { fg = C.text }, + SagaSelect = { fg = C.blue, style = { "bold" } }, + SagaSearch = { link = "Search" }, + SagaFinderFname = { fg = C.subtext0, style = { "bold" } }, + ActionFix = { fg = C.pink }, + ActionPreviewTitle = { fg = C.mauve, bg = O.transparent_background and C.none or C.base }, + CodeActionText = { fg = C.green }, + CodeActionNumber = { fg = C.pink }, + SagaImpIcon = { fg = C.pink }, + SagaLightBulb = { link = "DiagnosticSignHint" }, + RenameNormal = { fg = C.text }, + RenameMatch = { link = "Search" }, + DiagnosticText = { fg = C.text }, + SagaWinbarSep = { fg = C.flamingo }, + SagaDetail = { link = "Comment" }, + SagaFileName = { fg = C.overlay2, style = { "bold" } }, + SagaFolderName = { fg = C.overlay2, style = { "bold" } }, + SagaInCurrent = { fg = C.peach }, + + -- Lspkind icons support + LspKindClass = { fg = C.yellow }, + LspKindConstant = { fg = C.peach }, + LspKindConstructor = { fg = C.sapphire }, + LspKindEnum = { fg = C.yellow }, + LspKindEnumMember = { fg = C.teal }, + LspKindEvent = { fg = C.yellow }, + LspKindField = { fg = C.teal }, + LspKindFile = { fg = C.rosewater }, + LspKindFunction = { fg = C.blue }, + LspKindInterface = { fg = C.yellow }, + LspKindKey = { fg = C.red }, + LspKindMethod = { fg = C.blue }, + LspKindModule = { fg = C.blue }, + LspKindNamespace = { fg = C.blue }, + LspKindNumber = { fg = C.peach }, + LspKindOperator = { fg = C.sky }, + LspKindPackage = { fg = C.blue }, + LspKindProperty = { fg = C.teal }, + LspKindStruct = { fg = C.yellow }, + LspKindTypeParameter = { fg = C.blue }, + LspKindVariable = { fg = C.peach }, + LspKindArray = { fg = C.peach }, + LspKindBoolean = { fg = C.peach }, + LspKindNull = { fg = C.yellow }, + LspKindObject = { fg = C.yellow }, + LspKindString = { fg = C.green }, + -- ccls-specific icons. + LspKindTypeAlias = { fg = C.green }, + LspKindParameter = { fg = C.blue }, + LspKindStaticMethod = { fg = C.peach }, + -- Microsoft-specific icons. + LspKindText = { fg = C.green }, + LspKindSnippet = { fg = C.mauve }, + LspKindFolder = { fg = C.blue }, + LspKindUnit = { fg = C.green }, + LspKindValue = { fg = C.peach }, + } +end + +function M.custom_kind() + return { + File = { "󰈙 ", "LspKindFile" }, + Module = { " ", "LspKindModule" }, + Namespace = { "󰌗 ", "LspKindNamespace" }, + Package = { "󰏖 ", "LspKindPackage" }, + Class = { "󰌗 ", "LspKindClass" }, + Method = { " ", "LspKindMethod" }, + Property = { "󰆧 ", "LspKindProperty" }, + Field = { " ", "LspKindField" }, + Constructor = { " ", "LspKindConstructor" }, + Enum = { "󰒻", "LspKindEnum" }, + Interface = { " ", "LspKindInterface" }, + Function = { "󰊕 ", "LspKindFunction" }, + Variable = { " ", "LspKindVariable" }, + Constant = { " ", "LspKindConstant" }, + String = { "󰀬 ", "LspKindString" }, + Number = { "󰎠 ", "LspKindNumber" }, + Boolean = { " ", "LspKindBoolean" }, + Array = { "󰅪 ", "LspKindArray" }, + Object = { "󰅩 ", "LspKindObject" }, + Key = { "󰌋 ", "LspKindKey" }, + Null = { " ", "LspKindNull" }, + EnumMember = { " ", "LspKindEnumMember" }, + Struct = { "󰌗 ", "LspKindStruct" }, + Event = { " ", "LspKindEvent" }, + Operator = { "󰆕 ", "LspKindOperator" }, + TypeParameter = { "󰊄 ", "LspKindTypeParameter" }, + TypeAlias = { " ", "LspKindTypeAlias" }, + Parameter = { " ", "LspKindParameter" }, + StaticMethod = { "󰠄 ", "LspKindStaticMethod" }, + Macro = { " ", "LspKindMacro" }, + Text = { "󰉿 ", "LspKindText" }, + Snippet = { " ", "LspKindSnippet" }, + Folder = { " ", "LspKindFolder" }, + Unit = { "󰉻 ", "LspKindUnit" }, + Value = { " ", "LspKindValue" }, + } +end + +-- Backwards compatibility +function M.custom_colors() + local C = require("catppuccin.palettes").get_palette() + return { + normal_bg = C.base, + title_bg = C.green, + red = C.red, + magenta = C.maroon, + orange = C.peach, + yellow = C.yellow, + green = C.green, + cyan = C.sky, + blue = C.blue, + purple = C.mauve, + white = C.text, + black = C.crust, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lsp_trouble.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lsp_trouble.lua new file mode 100644 index 00000000..4a5005ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/lsp_trouble.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.get() + return { + TroubleText = { fg = C.green }, + TroubleCount = { fg = C.pink, bg = O.transparent_background and C.none or C.surface1 }, + TroubleNormal = { fg = C.text, bg = O.transparent_background and C.none or C.crust }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/markdown.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/markdown.lua new file mode 100644 index 00000000..a852f709 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/markdown.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return { + markdownHeadingDelimiter = { fg = C.peach, style = { "bold" } }, + markdownCode = { fg = C.flamingo }, + markdownCodeBlock = { fg = C.flamingo }, + markdownLinkText = { fg = C.blue, style = { "underline" } }, + markdownH1 = { link = "rainbow1" }, + markdownH2 = { link = "rainbow2" }, + markdownH3 = { link = "rainbow3" }, + markdownH4 = { link = "rainbow4" }, + markdownH5 = { link = "rainbow5" }, + markdownH6 = { link = "rainbow6" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/mason.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/mason.lua new file mode 100644 index 00000000..ea510773 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/mason.lua @@ -0,0 +1,38 @@ +local M = {} + +function M.get() + local bg = O.transparent_background and C.none or C.lavender + local fg = O.transparent_background and C.lavender or C.base + + local secondary_bg = O.transparent_background and C.none or C.blue + local secondary_fg = O.transparent_background and C.blue or C.base + + local muted_bg = O.transparent_background and C.none or C.overlay0 + local muted_fg = O.transparent_background and C.overlay0 or C.base + + return { + MasonHeader = { fg = fg, bg = bg, style = { "bold" } }, + MasonHeaderSecondary = { fg = secondary_fg, bg = secondary_bg, style = { "bold" } }, + + MasonHighlight = { fg = C.green }, + MasonHighlightBlock = { + bg = O.transparent_background and C.none or C.green, + fg = O.transparent_background and C.green or C.base, + }, + MasonHighlightBlockBold = { bg = secondary_bg, fg = secondary_fg, bold = true }, + + MasonHighlightSecondary = { fg = C.mauve }, + MasonHighlightBlockSecondary = { fg = secondary_fg, bg = secondary_bg }, + MasonHighlightBlockBoldSecondary = { fg = fg, bg = bg, bold = true }, + + MasonMuted = { fg = C.overlay0 }, + MasonMutedBlock = { bg = muted_bg, fg = muted_fg }, + MasonMutedBlockBold = { bg = C.yellow, fg = C.base, bold = true }, + + MasonError = { fg = C.red }, + + MasonHeading = { fg = C.lavender, bold = true }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/mini.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/mini.lua new file mode 100644 index 00000000..07596da0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/mini.lua @@ -0,0 +1,63 @@ +local M = {} + +function M.get() + local transparent_background = require("catppuccin").options.transparent_background + local bg_highlight = transparent_background and "NONE" or C.base + + local inactive_bg = transparent_background and "NONE" or C.mantle + + local indentscope_color = O.integrations.mini.indentscope_color + return { + MiniCompletionActiveParameter = { style = { "underline" } }, + + MiniCursorword = { style = { "underline" } }, + MiniCursorwordCurrent = { style = { "underline" } }, + + MiniIndentscopeSymbol = { fg = C[indentscope_color] or C.text }, + MiniIndentscopePrefix = { style = { "nocombine" } }, -- Make it invisible + + MiniJump = { fg = C.overlay2, bg = C.pink }, + + MiniJump2dSpot = { bg = C.base, fg = C.peach, style = { "bold", "underline" } }, + + MiniStarterCurrent = {}, + MiniStarterFooter = { fg = C.yellow, style = { "italic" } }, + MiniStarterHeader = { fg = C.blue }, + MiniStarterInactive = { fg = C.surface2, style = O.styles.comments }, + MiniStarterItem = { fg = C.text }, + MiniStarterItemBullet = { fg = C.blue }, + MiniStarterItemPrefix = { fg = C.pink }, + MiniStarterSection = { fg = C.flamingo }, + MiniStarterQuery = { fg = C.green }, + + MiniStatuslineDevinfo = { fg = C.subtext1, bg = C.surface1 }, + MiniStatuslineFileinfo = { fg = C.subtext1, bg = C.surface1 }, + MiniStatuslineFilename = { fg = C.text, bg = C.mantle }, + MiniStatuslineInactive = { fg = C.blue, bg = C.mantle }, + MiniStatuslineModeCommand = { fg = C.base, bg = C.peach, style = { "bold" } }, + MiniStatuslineModeInsert = { fg = C.base, bg = C.green, style = { "bold" } }, + MiniStatuslineModeNormal = { fg = C.mantle, bg = C.blue, style = { "bold" } }, + MiniStatuslineModeOther = { fg = C.base, bg = C.teal, style = { "bold" } }, + MiniStatuslineModeReplace = { fg = C.base, bg = C.red, style = { "bold" } }, + MiniStatuslineModeVisual = { fg = C.base, bg = C.mauve, style = { "bold" } }, + + MiniSurround = { bg = C.pink, fg = C.surface1 }, + + MiniTablineCurrent = { fg = C.text, bg = C.base, sp = C.red, style = { "bold", "italic", "underline" } }, + MiniTablineFill = { bg = bg_highlight }, + MiniTablineHidden = { fg = C.text, bg = inactive_bg }, + MiniTablineModifiedCurrent = { fg = C.red, bg = C.none, style = { "bold", "italic" } }, + MiniTablineModifiedHidden = { fg = C.red, bg = C.none }, + MiniTablineModifiedVisible = { fg = C.red, bg = C.none }, + MiniTablineTabpagesection = { fg = C.surface1, bg = C.base }, + MiniTablineVisible = { bg = C.none }, + + MiniTestEmphasis = { style = { "bold" } }, + MiniTestFail = { fg = C.red, style = { "bold" } }, + MiniTestPass = { fg = C.green, style = { "bold" } }, + + MiniTrailspace = { bg = C.red }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/native_lsp.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/native_lsp.lua new file mode 100644 index 00000000..e4ae10a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/native_lsp.lua @@ -0,0 +1,100 @@ +local M = {} + +function M.get() + local virtual_text = O.integrations.native_lsp.virtual_text + local underlines = O.integrations.native_lsp.underlines + local inlay_hints = O.integrations.native_lsp.inlay_hints + + local error = C.red + local warning = C.yellow + local info = C.sky + local hint = C.teal + local darkening_percentage = 0.095 + + return { + -- These groups are for the native LSP client. Some other LSP clients may + -- use these groups, or use their own. Consult your LSP client's + -- documentation. + LspReferenceText = { bg = C.surface1 }, -- used for highlighting "text" references + LspReferenceRead = { bg = C.surface1 }, -- used for highlighting "read" references + LspReferenceWrite = { bg = C.surface1 }, -- used for highlighting "write" references + -- highlight diagnostics in numberline + + DiagnosticVirtualTextError = { + bg = O.transparent_background and C.none or U.darken(error, darkening_percentage, C.base), + fg = error, + style = virtual_text.errors, + }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticVirtualTextWarn = { + bg = O.transparent_background and C.none or U.darken(warning, darkening_percentage, C.base), + fg = warning, + style = virtual_text.warnings, + }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticVirtualTextInfo = { + bg = O.transparent_background and C.none or U.darken(info, darkening_percentage, C.base), + fg = info, + style = virtual_text.information, + }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticVirtualTextHint = { + bg = O.transparent_background and C.none or U.darken(hint, darkening_percentage, C.base), + fg = hint, + style = virtual_text.hints, + }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + + DiagnosticError = { bg = C.none, fg = error, style = virtual_text.errors }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticWarn = { bg = C.none, fg = warning, style = virtual_text.warnings }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticInfo = { bg = C.none, fg = info, style = virtual_text.information }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticHint = { bg = C.none, fg = hint, style = virtual_text.hints }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + + -- for nvim nightly + DiagnosticUnderlineError = { style = underlines.errors, sp = error }, -- Used to underline "Error" diagnostics + DiagnosticUnderlineWarn = { style = underlines.warnings, sp = warning }, -- Used to underline "Warn" diagnostics + DiagnosticUnderlineInfo = { style = underlines.information, sp = info }, -- Used to underline "Info" diagnostics + DiagnosticUnderlineHint = { style = underlines.hints, sp = hint }, -- Used to underline "Hint" diagnostics + + DiagnosticFloatingError = { fg = error }, -- Used to color "Error" diagnostic messages in diagnostics float + DiagnosticFloatingWarn = { fg = warning }, -- Used to color "Warn" diagnostic messages in diagnostics float + DiagnosticFloatingInfo = { fg = info }, -- Used to color "Info" diagnostic messages in diagnostics float + DiagnosticFloatingHint = { fg = hint }, -- Used to color "Hint" diagnostic messages in diagnostics float + + DiagnosticSignError = { fg = error }, -- Used for "Error" signs in sign column + DiagnosticSignWarn = { fg = warning }, -- Used for "Warn" signs in sign column + DiagnosticSignInfo = { fg = info }, -- Used for "Info" signs in sign column + DiagnosticSignHint = { fg = hint }, -- Used for "Hint" signs in sign column + + LspDiagnosticsDefaultError = { fg = error }, -- Used as the mantle highlight group. Other LspDiagnostic highlights link to this by default (except Underline) + LspDiagnosticsDefaultWarning = { fg = warning }, -- Used as the mantle highlight group. Other LspDiagnostic highlights link to this by default (except Underline) + LspDiagnosticsDefaultInformation = { fg = info }, -- Used as the mantle highlight group. Other LspDiagnostic highlights link to this by default (except Underline) + LspDiagnosticsDefaultHint = { fg = hint }, -- Used as the mantle highlight group. Other LspDiagnostic highlights link to this by default (except Underline) + LspSignatureActiveParameter = { fg = C.peach }, + -- LspDiagnosticsFloatingError = { }, -- Used to color "Error" diagnostic messages in diagnostics float + -- LspDiagnosticsFloatingWarning = { }, -- Used to color "Warning" diagnostic messages in diagnostics float + -- LspDiagnosticsFloatingInformation = { }, -- Used to color "Information" diagnostic messages in diagnostics float + -- LspDiagnosticsFloatingHint = { }, -- Used to color "Hint" diagnostic messages in diagnostics float + + LspDiagnosticsError = { fg = error }, + LspDiagnosticsWarning = { fg = warning }, + LspDiagnosticsInformation = { fg = info }, + LspDiagnosticsHint = { fg = hint }, + LspDiagnosticsVirtualTextError = { fg = error, style = virtual_text.errors }, -- Used for "Error" diagnostic virtual text + LspDiagnosticsVirtualTextWarning = { fg = warning, style = virtual_text.warnings }, -- Used for "Warning" diagnostic virtual text + LspDiagnosticsVirtualTextInformation = { fg = info, style = virtual_text.warnings }, -- Used for "Information" diagnostic virtual text + LspDiagnosticsVirtualTextHint = { fg = hint, style = virtual_text.hints }, -- Used for "Hint" diagnostic virtual text + LspDiagnosticsUnderlineError = { style = underlines.errors, sp = error }, -- Used to underline "Error" diagnostics + LspDiagnosticsUnderlineWarning = { style = underlines.warnings, sp = warning }, -- Used to underline "Warning" diagnostics + LspDiagnosticsUnderlineInformation = { style = underlines.information, sp = info }, -- Used to underline "Information" diagnostics + LspDiagnosticsUnderlineHint = { style = underlines.hints, sp = hint }, -- Used to underline "Hint" diagnostics + LspCodeLens = { fg = C.overlay0 }, -- virtual text of the codelens + LspCodeLensSeparator = { link = "LspCodeLens" }, -- virtual text of the codelens separators + LspInlayHint = { + -- fg of `Comment` + fg = C.overlay0, + -- bg of `CursorLine` + bg = (O.transparent_background or not inlay_hints.background) and C.none + or U.vary_color({ latte = U.lighten(C.mantle, 0.70, C.base) }, U.darken(C.surface0, 0.64, C.base)), + }, -- virtual text of the inlay hints + LspInfoBorder = { link = "FloatBorder" }, -- LspInfo border + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/navic.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/navic.lua new file mode 100644 index 00000000..da6bcbcf --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/navic.lua @@ -0,0 +1,39 @@ +local M = {} + +function M.get() + local background = O.integrations.navic.custom_bg and O.integrations.navic.custom_bg or C.none + if O.integrations.navic.custom_bg == "lualine" then background = C.mantle end + + return { + NavicIconsFile = { fg = C.blue, bg = background }, + NavicIconsModule = { fg = C.blue, bg = background }, + NavicIconsNamespace = { fg = C.blue, bg = background }, + NavicIconsPackage = { fg = C.blue, bg = background }, + NavicIconsClass = { fg = C.yellow, bg = background }, + NavicIconsMethod = { fg = C.blue, bg = background }, + NavicIconsProperty = { fg = C.green, bg = background }, + NavicIconsField = { fg = C.green, bg = background }, + NavicIconsConstructor = { fg = C.blue, bg = background }, + NavicIconsEnum = { fg = C.green, bg = background }, + NavicIconsInterface = { fg = C.yellow, bg = background }, + NavicIconsFunction = { fg = C.blue, bg = background }, + NavicIconsVariable = { fg = C.flamingo, bg = background }, + NavicIconsConstant = { fg = C.peach, bg = background }, + NavicIconsString = { fg = C.green, style = O.styles.strings, bg = background }, + NavicIconsNumber = { fg = C.peach, bg = background }, + NavicIconsBoolean = { fg = C.peach, bg = background }, + NavicIconsArray = { fg = C.peach, bg = background }, + NavicIconsObject = { fg = C.peach, bg = background }, + NavicIconsKey = { fg = C.pink, style = O.styles.keywords, bg = background }, + NavicIconsNull = { fg = C.peach, bg = background }, + NavicIconsEnumMember = { fg = C.red, bg = background }, + NavicIconsStruct = { fg = C.blue, bg = background }, + NavicIconsEvent = { fg = C.blue, bg = background }, + NavicIconsOperator = { fg = C.sky, bg = background }, + NavicIconsTypeParameter = { fg = C.blue, bg = background }, + NavicText = { fg = C.sapphire, bg = background }, + NavicSeparator = { fg = C.text, bg = background }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neogit.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neogit.lua new file mode 100644 index 00000000..b0499d60 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neogit.lua @@ -0,0 +1,244 @@ +local M = {} + +function M.get() + return { + NeogitBranch = { + fg = C.peach, + style = { "bold" }, + }, + NeogitRemote = { + fg = C.green, + style = { "bold" }, + }, + NeogitUnmergedInto = { + link = "Function", + }, + NeogitUnpulledFrom = { + link = "Function", + }, + NeogitObjectId = { + link = "Comment", + }, + NeogitStash = { + link = "Comment", + }, + NeogitRebaseDone = { + link = "Comment", + }, + NeogitHunkHeader = { + bg = U.darken(C.blue, 0.095, C.base), + fg = U.darken(C.blue, 0.5, C.base), + }, + NeogitHunkHeaderHighlight = { + bg = U.darken(C.blue, 0.215, C.base), + fg = C.blue, + }, + NeogitDiffContextHighlight = { + bg = C.surface0, + }, + NeogitDiffDeleteHighlight = { + bg = U.darken(C.red, 0.345, C.base), + fg = U.lighten(C.red, 0.850, C.text), + }, + NeogitDiffAddHighlight = { + bg = U.darken(C.green, 0.345, C.base), + fg = U.lighten(C.green, 0.850, C.text), + }, + NeogitDiffDelete = { + bg = U.darken(C.red, 0.095, C.base), + fg = U.darken(C.red, 0.800, C.base), + }, + NeogitDiffAdd = { + bg = U.darken(C.green, 0.095, C.base), + fg = U.darken(C.green, 0.800, C.base), + }, + NeogitCommitViewHeader = { + bg = U.darken(C.blue, 0.300, C.base), + fg = U.lighten(C.blue, 0.800, C.text), + }, + NeogitChangeModified = { + fg = C.blue, + style = { "bold" }, + }, + NeogitChangeDeleted = { + fg = C.red, + style = { "bold" }, + }, + NeogitChangeAdded = { + fg = C.green, + style = { "bold" }, + }, + NeogitChangeRenamed = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitChangeUpdated = { + fg = C.peach, + style = { "bold" }, + }, + NeogitChangeCopied = { + fg = C.pink, + style = { "bold" }, + }, + NeogitChangeBothModified = { + fg = C.yellow, + style = { "bold" }, + }, + NeogitChangeNewFile = { + fg = C.green, + style = { "bold" }, + }, + NeogitUntrackedfiles = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitUnstagedchanges = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitUnmergedchanges = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitUnpulledchanges = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitRecentcommits = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitStagedchanges = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitStashes = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitRebasing = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitNotificationInfo = { + fg = C.blue, + }, + NeogitNotificationWarning = { + fg = C.yellow, + }, + NeogitNotificationError = { + fg = C.red, + }, + NeogitGraphRed = { + fg = C.red, + }, + NeogitGraphWhite = { + fg = C.base, + }, + NeogitGraphYellow = { + fg = C.yellow, + }, + NeogitGraphGreen = { + fg = C.green, + }, + NeogitGraphCyan = { + fg = C.blue, + }, + NeogitGraphBlue = { + fg = C.blue, + }, + NeogitGraphPurple = { + fg = C.lavender, + }, + NeogitGraphGray = { + fg = C.subtext1, + }, + NeogitGraphOrange = { + fg = C.peach, + }, + NeogitGraphBoldRed = { + fg = C.red, + style = { "bold" }, + }, + NeogitGraphBoldWhite = { + fg = C.white, + style = { "bold" }, + }, + NeogitGraphBoldYellow = { + fg = C.yellow, + style = { "bold" }, + }, + NeogitGraphBoldGreen = { + fg = C.green, + style = { "bold" }, + }, + NeogitGraphBoldCyan = { + fg = C.blue, + style = { "bold" }, + }, + NeogitGraphBoldBlue = { + fg = C.blue, + style = { "bold" }, + }, + NeogitGraphBoldPurple = { + fg = C.lavender, + style = { "bold" }, + }, + NeogitGraphBoldGray = { + fg = C.subtext1, + style = { "bold" }, + }, + NeogitDiffContext = { + bg = C.base, + }, + NeogitPopupBold = { + style = { "bold" }, + }, + NeogitPopupSwitchKey = { + fg = C.lavender, + }, + NeogitPopupOptionKey = { + fg = C.lavender, + }, + NeogitPopupConfigKey = { + fg = C.lavender, + }, + NeogitPopupActionKey = { + fg = C.lavender, + }, + NeogitFilePath = { + fg = C.blue, + style = { "italic" }, + }, + NeogitDiffHeader = { + bg = C.base, + fg = C.blue, + style = { "bold" }, + }, + NeogitDiffHeaderHighlight = { + bg = C.base, + fg = C.peach, + style = { "bold" }, + }, + NeogitUnpushedTo = { + fg = C.lavender, + style = { "bold" }, + }, + NeogitFold = { + fg = C.none, + bg = C.none, + }, + NeogitSectionHeader = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitTagName = { + fg = C.yellow, + }, + NeogitTagDistance = { + fg = C.blue, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neotest.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neotest.lua new file mode 100644 index 00000000..5385cc09 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neotest.lua @@ -0,0 +1,24 @@ +local M = {} + +function M.get() + return { + NeotestPassed = { fg = C.green }, + NeotestFailed = { fg = C.red }, + NeotestRunning = { fg = C.yellow }, + NeotestSkipped = { fg = C.blue }, + NeotestTest = { fg = C.text }, + NeotestNamespace = { fg = C.mauve }, + NeotestFocused = { style = { "bold", "underline" } }, + NeotestFile = { fg = C.blue }, + NeotestDir = { fg = C.blue }, + NeotestIndent = { fg = C.overlay1 }, + NeotestExpandMarker = { fg = C.overlay1 }, + NeotestAdapterName = { fg = C.maroon }, + NeotestWinSelect = { fg = C.blue, style = { "bold" } }, + NeotestMarked = { fg = C.peach, style = { "bold" } }, + NeotestTarget = { fg = C.red }, + NeotestUnknown = { fg = C.text }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neotree.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neotree.lua new file mode 100644 index 00000000..c5e7f0fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/neotree.lua @@ -0,0 +1,46 @@ +local M = {} + +function M.get() + local active_bg = O.transparent_background and C.none or C.mantle + local inactive_bg = O.transparent_background and C.none or C.base + return { + NeoTreeDirectoryName = { fg = C.blue }, + NeoTreeDirectoryIcon = { fg = C.blue }, + NeoTreeNormal = { fg = C.text, bg = active_bg }, + NeoTreeNormalNC = { fg = C.text, bg = active_bg }, + NeoTreeExpander = { fg = C.overlay0 }, + NeoTreeIndentMarker = { fg = C.overlay0 }, + NeoTreeRootName = { fg = C.blue, style = { "bold" } }, + NeoTreeSymbolicLinkTarget = { fg = C.pink }, + NeoTreeModified = { fg = C.peach }, + + NeoTreeGitAdded = { fg = C.green }, + NeoTreeGitConflict = { fg = C.red }, + NeoTreeGitDeleted = { fg = C.red }, + NeoTreeGitIgnored = { fg = C.overlay0 }, + NeoTreeGitModified = { fg = C.yellow }, + NeoTreeGitUnstaged = { fg = C.red }, + NeoTreeGitUntracked = { fg = C.mauve }, + NeoTreeGitStaged = { fg = C.green }, + + NeoTreeFloatBorder = { link = "FloatBorder" }, + NeoTreeFloatTitle = { link = "FloatTitle" }, + NeoTreeTitleBar = { fg = C.mantle, bg = C.blue }, + + NeoTreeFileNameOpened = { fg = C.pink }, + NeoTreeDimText = { fg = C.overlay1 }, + NeoTreeFilterTerm = { fg = C.green, style = { "bold" } }, + NeoTreeTabActive = { bg = active_bg, fg = C.lavender, style = { "bold" } }, + NeoTreeTabInactive = { bg = inactive_bg, fg = C.overlay0 }, + NeoTreeTabSeparatorActive = { fg = active_bg, bg = active_bg }, + NeoTreeTabSeparatorInactive = { fg = inactive_bg, bg = inactive_bg }, + NeoTreeVertSplit = { fg = C.base, bg = inactive_bg }, + NeoTreeWinSeparator = { + fg = O.transparent_background and C.surface1 or C.base, + bg = O.transparent_background and C.none or C.base, + }, + NeoTreeStatusLineNC = { fg = C.mantle, bg = C.mantle }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/noice.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/noice.lua new file mode 100644 index 00000000..45bf6b55 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/noice.lua @@ -0,0 +1,26 @@ +local M = {} + +local virtual_text = O.integrations.native_lsp.virtual_text + +function M.get() + return { -- Personally the default integration is already pretty good + NoiceCmdline = { fg = C.text }, + NoiceCmdlineIcon = { fg = C.sky, style = virtual_text.information }, + NoiceCmdlineIconSearch = { fg = C.yellow }, + NoiceCmdlinePopupBorder = { fg = C.lavender }, + NoiceCmdlinePopupBorderSearch = { fg = C.yellow }, + NoiceConfirmBorder = { fg = C.blue }, + NoiceMini = { fg = C.subtext0, blend = 0 }, + NoiceFormatProgressDone = { + bg = O.transparent_background and C.none or U.darken(C.sky, 0.30, C.base), + fg = C.subtext0, + }, + NoiceFormatProgressTodo = { + bg = O.transparent_background and C.none + or U.vary_color({ latte = U.lighten(C.mantle, 0.70, C.base) }, U.darken(C.surface0, 0.64, C.base)), + fg = C.subtext0, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/notifier.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/notifier.lua new file mode 100644 index 00000000..12ecfe40 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/notifier.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.get() + return { + NotifierIcon = { fg = C.mauve }, + NotifierContent = { fg = C.overlay2, blend = 0, style = { "italic" } }, + NotifierContentDim = { fg = C.overlay1, blend = 0, style = { "italic" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/notify.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/notify.lua new file mode 100644 index 00000000..fcd5002c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/notify.lua @@ -0,0 +1,25 @@ +local M = {} + +function M.get() + return { + NotifyBackground = { bg = (O.transparent_background and vim.o.winblend == 0) and "#000000" or C.mantle }, + + NotifyERRORBorder = { fg = C.red }, + NotifyERRORIcon = { fg = C.red }, + NotifyERRORTitle = { fg = C.red, style = { "italic" } }, + NotifyWARNBorder = { fg = C.yellow }, + NotifyWARNIcon = { fg = C.yellow }, + NotifyWARNTitle = { fg = C.yellow, style = { "italic" } }, + NotifyINFOBorder = { fg = C.blue }, + NotifyINFOIcon = { fg = C.blue }, + NotifyINFOTitle = { fg = C.blue, style = { "italic" } }, + NotifyDEBUGBorder = { fg = C.peach }, + NotifyDEBUGIcon = { fg = C.peach }, + NotifyDEBUGTitle = { fg = C.peach, style = { "italic" } }, + NotifyTRACEBorder = { fg = C.rosewater }, + NotifyTRACEIcon = { fg = C.rosewater }, + NotifyTRACETitle = { fg = C.rosewater, style = { "italic" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/nvimtree.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/nvimtree.lua new file mode 100644 index 00000000..725196ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/nvimtree.lua @@ -0,0 +1,27 @@ +local M = {} + +function M.get() + return { + NvimTreeFolderName = { fg = C.blue }, + NvimTreeFolderIcon = { fg = C.blue }, + NvimTreeNormal = { fg = C.text, bg = O.transparent_background and C.none or C.mantle }, + NvimTreeOpenedFolderName = { fg = C.blue }, + NvimTreeEmptyFolderName = { fg = C.blue }, + NvimTreeIndentMarker = { fg = C.overlay0 }, + NvimTreeWinSeparator = { + fg = O.transparent_background and C.surface1 or C.base, + bg = O.transparent_background and C.none or C.base, + }, + NvimTreeRootFolder = { fg = C.lavender, style = { "bold" } }, + NvimTreeSymlink = { fg = C.pink }, + NvimTreeStatuslineNc = { fg = C.mantle, bg = C.mantle }, + NvimTreeGitDirty = { fg = C.yellow }, + NvimTreeGitNew = { fg = C.blue }, + NvimTreeGitDeleted = { fg = C.red }, + NvimTreeSpecialFile = { fg = C.flamingo }, + NvimTreeImageFile = { fg = C.text }, + NvimTreeOpenedFile = { fg = C.pink }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/octo.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/octo.lua new file mode 100644 index 00000000..cd1fd64f --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/octo.lua @@ -0,0 +1,78 @@ +local M = {} + +function M.get() + return { + -- colors + OctoGrey = { fg = C.base }, + OctoBlue = { fg = C.blue }, + OctoGreen = { fg = C.green }, + OctoRed = { fg = C.red }, + OctoPurple = { fg = C.mauve }, + OctoYellow = { fg = C.yellow }, + -- highlight groups + OctoDirty = { link = "ErrorMsg" }, + OctoIssueTitle = { link = "PreProc" }, + OctoIssueId = { link = "Question" }, + OctoEmpty = { link = "Comment" }, + OctoFloat = { link = "NormalNC" }, + OctoDate = { link = "Comment" }, + OctoSymbol = { link = "Comment" }, + OctoTimelineItemHeading = { link = "Comment" }, + OctoDetailsLabel = { link = "Title" }, + OctoMissingDetails = { link = "Comment" }, + OctoDetailsValue = { link = "Identifier" }, + OctoDiffHunkPosition = { link = "NormalFloat" }, + OctoCommentLine = { link = "TabLineSel" }, + OctoViewer = { fg = C.base, bg = C.blue }, + OctoBubble = { fg = C.text, bg = C.mantle }, + OctoBubbleGrey = { fg = C.text, bg = C.mantle }, + OctoBubbleDelimiterGrey = { fg = C.mantle }, + OctoBubbleGreen = { fg = C.base, bg = C.green }, + OctoBubbleDelimiterGreen = { fg = C.green }, + OctoBubbleBlue = { fg = C.base, bg = C.sky }, + OctoBubbleDelimiterBlue = { fg = C.sky }, + OctoBubbleYellow = { fg = C.base, bg = C.yellow }, + OctoBubbleDelimiterYellow = { fg = C.yellow }, + OctoBubbleRed = { fg = C.base, bg = C.red }, + OctoBubbleDelimiterRed = { fg = C.red }, + OctoBubblePurple = { fg = C.base, bg = C.mauve }, + OctoBubbleDelimiterPurple = { fg = C.mauve }, + OctoUser = { link = "OctoBubble" }, + OctoUserViewer = { link = "OctoViewer" }, + OctoReaction = { link = "OctoBubble" }, + OctoReactionViewer = { link = "OctoViewer" }, + OctoPassingTest = { link = "OctoGreen" }, + OctoFailingTest = { link = "OctoRed" }, + OctoPullAdditions = { link = "OctoGreen" }, + OctoPullDeletions = { link = "OctoRed" }, + OctoPullModifications = { fg = C.peach }, + OctoStateOpen = { fg = C.green, bg = C.mantle }, + OctoStateOpenFloat = { fg = C.green, bg = C.mantle }, + OctoStateClosed = { fg = C.red, bg = C.mantle }, + OctoStateClosedFloat = { fg = C.red, bg = C.mantle }, + OctoStateMerged = { fg = C.mauve, bg = C.mantle }, + OctoStateMergedFloat = { fg = C.mauve, bg = C.mantle }, + OctoStatePending = { fg = C.peach, bg = C.mantle }, + OctoStatePendingFloat = { fg = C.peach, bg = C.mantle }, + OctoStateApproved = { link = "OctoStateOpen" }, + OctoStateApprovedFloat = { link = "OctoStateOpen" }, + OctoStateChangesRequested = { fg = C.maroon, bg = C.mantle }, + OctoStateChangesRequestedFloat = { fg = C.maroon, bg = C.mantle }, + OctoStateCommented = { link = "Normal" }, + OctoStateCommentedFloat = { link = "Normal" }, + OctoStateDismissed = { link = "OctoStateClosed" }, + OctoStateDismissedFloat = { link = "OctoStateClosed" }, + OctoStateSubmittedBubble = { link = "OctoBubbleGreen" }, + OctoFilePanelCounter = { fg = C.blue, style = { "bold" } }, + OctoFilePanelTitle = { fg = C.blue, style = { "bold" } }, + OctoNormalFront = { fg = C.text }, + OctoYellowFloat = { fg = C.yellow, bg = C.mantle }, + OctoPurpleFloat = { fg = C.mauve, bg = C.mantle }, + OctoRedFloat = { fg = C.red, bg = C.mantle }, + OctoGreenFloat = { fg = C.green, bg = C.mantle }, + OctoGreyFloat = { fg = C.text, bg = C.mantle }, + OctoBlueFloat = { fg = C.blue, bg = C.mantle }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/overseer.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/overseer.lua new file mode 100644 index 00000000..20f47b7b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/overseer.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return { + OverseerPENDING = { fg = C.grey }, + OverseerRUNNING = { fg = C.yellow }, + OverseerSUCCESS = { fg = C.green }, + OverseerCANCELED = { fg = C.overlay2 }, + OverseerFAILURE = { fg = C.red }, + OverseerTask = { fg = C.blue }, + OverseerTaskBorder = { fg = C.sky }, + OverseerOutput = { fg = C.text }, + OverseerComponent = { fg = C.yellow }, + OverseerField = { fg = C.green }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/pounce.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/pounce.lua new file mode 100644 index 00000000..608c99f6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/pounce.lua @@ -0,0 +1,12 @@ +local M = {} + +function M.get() + return { + PounceMatch = { bg = U.lighten(C.green, 0.7), fg = C.base, style = { "bold" } }, + PounceGap = { bg = U.darken(C.green, 0.7), fg = C.base, style = { "bold" } }, + PounceAccept = { bg = C.peach, fg = C.base, style = { "bold" } }, + PounceAcceptBest = { bg = C.teal, fg = C.base, style = { "bold" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/rainbow_delimiters.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/rainbow_delimiters.lua new file mode 100644 index 00000000..bfaeecf7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/rainbow_delimiters.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + RainbowDelimiterRed = { fg = C.red }, + RainbowDelimiterYellow = { fg = C.yellow }, + RainbowDelimiterBlue = { fg = C.blue }, + RainbowDelimiterOrange = { fg = C.peach }, + RainbowDelimiterGreen = { fg = C.green }, + RainbowDelimiterViolet = { fg = C.mauve }, + RainbowDelimiterCyan = { fg = C.teal }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/sandwich.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/sandwich.lua new file mode 100644 index 00000000..e366c845 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/sandwich.lua @@ -0,0 +1,12 @@ +local M = {} + +function M.get() + return { + OperatorSandwichAdd = { bg = C.blue, fg = C.base }, + OperatorSandwichBuns = { bg = C.blue, fg = C.base }, + OperatorSandwichChange = { bg = C.blue, fg = C.base }, + OperatorSandwichDelete = { bg = C.blue, fg = C.base }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/semantic_tokens.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/semantic_tokens.lua new file mode 100644 index 00000000..6d9094b6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/semantic_tokens.lua @@ -0,0 +1,48 @@ +local M = {} + +function M.get() + if vim.treesitter.highlighter.hl_map then + vim.notify_once( + [[Catppuccin (info): +semantic_tokens integration requires neovim 0.8 +If you want to stay on nvim 0.7, disable the integration. +]], + vim.log.levels.INFO + ) + return {} + end + return { + ["@lsp.type.boolean"] = { link = "@boolean" }, + ["@lsp.type.builtinType"] = { link = "@type.builtin" }, + ["@lsp.type.comment"] = { link = "@comment" }, + ["@lsp.type.enum"] = { link = "@type" }, + ["@lsp.type.enumMember"] = { link = "@constant" }, + ["@lsp.type.escapeSequence"] = { link = "@string.escape" }, + ["@lsp.type.formatSpecifier"] = { link = "@punctuation.special" }, + ["@lsp.type.interface"] = { fg = C.flamingo }, + ["@lsp.type.keyword"] = { link = "@keyword" }, + ["@lsp.type.namespace"] = { link = "@module" }, + ["@lsp.type.number"] = { link = "@number" }, + ["@lsp.type.operator"] = { link = "@operator" }, + ["@lsp.type.parameter"] = { link = "@parameter" }, + ["@lsp.type.property"] = { link = "@property" }, + ["@lsp.type.selfKeyword"] = { link = "@variable.builtin" }, + ["@lsp.type.typeAlias"] = { link = "@type.definition" }, + ["@lsp.type.unresolvedReference"] = { link = "@error" }, + ["@lsp.type.variable"] = {}, -- use treesitter styles for regular variables + ["@lsp.typemod.class.defaultLibrary"] = { link = "@type.builtin" }, + ["@lsp.typemod.enum.defaultLibrary"] = { link = "@type.builtin" }, + ["@lsp.typemod.enumMember.defaultLibrary"] = { link = "@constant.builtin" }, + ["@lsp.typemod.function.defaultLibrary"] = { link = "@function.builtin" }, + ["@lsp.typemod.keyword.async"] = { link = "@keyword.coroutine" }, + ["@lsp.typemod.macro.defaultLibrary"] = { link = "@function.builtin" }, + ["@lsp.typemod.method.defaultLibrary"] = { link = "@function.builtin" }, + ["@lsp.typemod.operator.injected"] = { link = "@operator" }, + ["@lsp.typemod.string.injected"] = { link = "@string" }, + ["@lsp.typemod.type.defaultLibrary"] = { link = "@type.builtin" }, + ["@lsp.typemod.variable.defaultLibrary"] = { link = "@variable.builtin" }, + ["@lsp.typemod.variable.injected"] = { link = "@variable" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/symbols_outline.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/symbols_outline.lua new file mode 100644 index 00000000..f16b6c28 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/symbols_outline.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + return { + -- https://github.com/simrat39/symbols-outline.nvim + FocusedSymbol = { fg = C.yellow, bg = C.none }, + + -- https://github.com/hedyhli/outline.nvim + OutlineCurrent = { link = "FocusedSymbol" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/telekasten.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/telekasten.lua new file mode 100644 index 00000000..9af7a855 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/telekasten.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.get() + return { + tkLink = { fg = C.blue }, + tkBrackets = { fg = C.pink }, + tkTag = { fg = C.sky }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/telescope.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/telescope.lua new file mode 100644 index 00000000..58401704 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/telescope.lua @@ -0,0 +1,60 @@ +local M = {} + +function M.get() + if O.integrations.telescope.style == "nvchad" then + return { + TelescopeBorder = { + fg = O.transparent_background and C.blue or C.mantle, + bg = O.transparent_background and C.none or C.mantle, + }, + TelescopeMatching = { fg = C.blue }, + TelescopeNormal = { + bg = O.transparent_background and C.none or C.mantle, + }, + TelescopePromptBorder = { + fg = O.transparent_background and C.blue or C.surface0, + bg = O.transparent_background and C.none or C.surface0, + }, + TelescopePromptNormal = { + fg = C.text, + bg = O.transparent_background and C.none or C.surface0, + }, + TelescopePromptPrefix = { + fg = C.flamingo, + bg = O.transparent_background and C.none or C.surface0, + }, + TelescopePreviewTitle = { + fg = O.transparent_background and C.green or C.base, + bg = O.transparent_background and C.none or C.green, + }, + TelescopePromptTitle = { + fg = O.transparent_background and C.red or C.base, + bg = O.transparent_background and C.none or C.red, + }, + TelescopeResultsTitle = { + fg = O.transparent_background and C.lavender or C.mantle, + bg = O.transparent_background and C.none or C.lavender, + }, + TelescopeSelection = { + fg = O.transparent_background and C.flamingo or C.text, + bg = O.transparent_background and C.none or C.surface0, + style = { "bold" }, + }, + TelescopeSelectionCaret = { fg = C.flamingo }, + } + end + + return { + -- TelescopeNormal = { link = "NormalFloat" }, -- Respect telescope's default float bg + TelescopeBorder = { link = "FloatBorder" }, + TelescopeSelectionCaret = { fg = C.flamingo }, + TelescopeSelection = { + fg = O.transparent_background and C.flamingo or C.text, + bg = O.transparent_background and C.none or C.surface0, + style = { "bold" }, + }, + TelescopeMatching = { fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/treesitter.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/treesitter.lua new file mode 100644 index 00000000..cd4981ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/treesitter.lua @@ -0,0 +1,274 @@ +local M = {} + +function M.get() + if vim.treesitter.highlighter.hl_map then + vim.notify_once( + [[Catppuccin (info): +nvim-treesitter integration requires neovim 0.8 +If you want to stay on nvim 0.7, either disable the integration or pin catppuccin tag to v0.2.4 and nvim-treesitter commit to 4cccb6f494eb255b32a290d37c35ca12584c74d0. +]], + vim.log.levels.INFO + ) + return {} + end + + local colors = { -- Reference: https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md + -- Identifiers + ["@variable"] = { fg = C.text, style = O.styles.variables or {} }, -- Any variable name that does not have another highlight. + ["@variable.builtin"] = { fg = C.red, style = O.styles.properties or {} }, -- Variable names that are defined by the languages, like this or self. + ["@variable.parameter"] = { fg = C.maroon, style = O.styles.variables or {} }, -- For parameters of a function. + ["@variable.member"] = { fg = C.lavender }, -- For fields. + + ["@constant"] = { link = "Constant" }, -- For constants + ["@constant.builtin"] = { fg = C.peach, style = O.styles.keywords or {} }, -- For constant that are built in the language: nil in Lua. + ["@constant.macro"] = { link = "Macro" }, -- For constants that are defined by macros: NULL in C. + + ["@module"] = { fg = C.lavender, style = O.styles.miscs or { "italic" } }, -- For identifiers referring to modules and namespaces. + ["@label"] = { link = "Label" }, -- For labels: label: in C and :label: in Lua. + + -- Literals + ["@string"] = { link = "String" }, -- For strings. + ["@string.documentation"] = { fg = C.teal, style = O.styles.strings or {} }, -- For strings documenting code (e.g. Python docstrings). + ["@string.regexp"] = { fg = C.peach, style = O.styles.strings or {} }, -- For regexes. + ["@string.escape"] = { fg = C.pink, style = O.styles.strings or {} }, -- For escape characters within a string. + ["@string.special"] = { link = "Special" }, -- other special strings (e.g. dates) + ["@string.special.path"] = { link = "Special" }, -- filenames + ["@string.special.symbol"] = { fg = C.flamingo }, -- symbols or atoms + ["@string.special.url"] = { fg = C.rosewater, style = { "italic", "underline" } }, -- urls, links and emails + + ["@character"] = { link = "Character" }, -- character literals + ["@character.special"] = { link = "SpecialChar" }, -- special characters (e.g. wildcards) + + ["@boolean"] = { link = "Boolean" }, -- For booleans. + ["@number"] = { link = "Number" }, -- For all numbers + ["@number.float"] = { link = "Float" }, -- For floats. + + -- Types + ["@type"] = { link = "Type" }, -- For types. + ["@type.builtin"] = { fg = C.yellow, style = O.styles.properties or { "italic" } }, -- For builtin types. + ["@type.definition"] = { link = "Type" }, -- type definitions (e.g. `typedef` in C) + + ["@attribute"] = { link = "Constant" }, -- attribute annotations (e.g. Python decorators) + ["@property"] = { fg = C.lavender, style = O.styles.properties or {} }, -- Same as TSField. + + -- Functions + ["@function"] = { link = "Function" }, -- For function (calls and definitions). + ["@function.builtin"] = { fg = C.peach, style = O.styles.functions or {} }, -- For builtin functions: table.insert in Lua. + ["@function.call"] = { link = "Function" }, -- function calls + ["@function.macro"] = { fg = C.teal, style = O.styles.functions or {} }, -- For macro defined functions (calls and definitions): each macro_rules in Rust. + + ["@function.method"] = { link = "Function" }, -- For method definitions. + ["@function.method.call"] = { link = "Function" }, -- For method calls. + + ["@constructor"] = { fg = C.sapphire }, -- For constructor calls and definitions: = { } in Lua, and Java constructors. + ["@operator"] = { link = "Operator" }, -- For any operator: +, but also -> and * in C. + + -- Keywords + ["@keyword"] = { link = "Keyword" }, -- For keywords that don't fall in previous categories. + ["@keyword.modifier"] = { link = "Keyword" }, -- For keywords modifying other constructs (e.g. `const`, `static`, `public`) + ["@keyword.type"] = { link = "Keyword" }, -- For keywords describing composite types (e.g. `struct`, `enum`) + ["@keyword.coroutine"] = { link = "Keyword" }, -- For keywords related to coroutines (e.g. `go` in Go, `async/await` in Python) + ["@keyword.function"] = { fg = C.mauve, style = O.styles.keywords or {} }, -- For keywords used to define a function. + ["@keyword.operator"] = { link = "Operator" }, -- For new keyword operator + ["@keyword.import"] = { link = "Include" }, -- For includes: #include in C, use or extern crate in Rust, or require in Lua. + ["@keyword.repeat"] = { link = "Repeat" }, -- For keywords related to loops. + ["@keyword.return"] = { fg = C.mauve, style = O.styles.keywords or {} }, + ["@keyword.debug"] = { link = "Exception" }, -- For keywords related to debugging + ["@keyword.exception"] = { link = "Exception" }, -- For exception related keywords. + + ["@keyword.conditional"] = { link = "Conditional" }, -- For keywords related to conditionnals. + ["@keyword.conditional.ternary"] = { link = "Operator" }, -- For ternary operators (e.g. `?` / `:`) + + ["@keyword.directive"] = { link = "PreProc" }, -- various preprocessor directives & shebangs + ["@keyword.directive.define"] = { link = "Define" }, -- preprocessor definition directives + -- JS & derivative + ["@keyword.export"] = { fg = C.sky, style = O.styles.keywords }, + + -- Punctuation + ["@punctuation.delimiter"] = { link = "Delimiter" }, -- For delimiters (e.g. `;` / `.` / `,`). + ["@punctuation.bracket"] = { fg = C.overlay2 }, -- For brackets and parenthesis. + ["@punctuation.special"] = { link = "Special" }, -- For special punctuation that does not fall in the categories before (e.g. `{}` in string interpolation). + + -- Comment + ["@comment"] = { link = "Comment" }, + ["@comment.documentation"] = { link = "Comment" }, -- For comments documenting code + + ["@comment.error"] = { fg = C.base, bg = C.red }, + ["@comment.warning"] = { fg = C.base, bg = C.yellow }, + ["@comment.hint"] = { fg = C.base, bg = C.blue }, + ["@comment.todo"] = { fg = C.base, bg = C.flamingo }, + ["@comment.note"] = { fg = C.base, bg = C.rosewater }, + + -- Markup + ["@markup"] = { fg = C.text }, -- For strings considerated text in a markup language. + ["@markup.strong"] = { fg = C.maroon, style = { "bold" } }, -- bold + ["@markup.italic"] = { fg = C.maroon, style = { "italic" } }, -- italic + ["@markup.strikethrough"] = { fg = C.text, style = { "strikethrough" } }, -- strikethrough text + ["@markup.underline"] = { link = "Underlined" }, -- underlined text + + ["@markup.heading"] = { fg = C.blue, style = { "bold" } }, -- titles like: # Example + + ["@markup.math"] = { fg = C.blue }, -- math environments (e.g. `$ ... $` in LaTeX) + ["@markup.quote"] = { fg = C.maroon, style = { "bold" } }, -- block quotes + ["@markup.environment"] = { fg = C.pink }, -- text environments of markup languages + ["@markup.environment.name"] = { fg = C.blue }, -- text indicating the type of an environment + + ["@markup.link"] = { link = "Tag" }, -- text references, footnotes, citations, etc. + ["@markup.link.label"] = { link = "Label" }, -- link, reference descriptions + ["@markup.link.url"] = { fg = C.rosewater, style = { "italic", "underline" } }, -- urls, links and emails + + ["@markup.raw"] = { fg = C.teal }, -- used for inline code in markdown and for doc in python (""") + + ["@markup.list"] = { link = "Special" }, + ["@markup.list.checked"] = { fg = C.green }, -- todo notes + ["@markup.list.unchecked"] = { fg = C.overlay1 }, -- todo notes + + -- Diff + ["@diff.plus"] = { link = "diffAdded" }, -- added text (for diff files) + ["@diff.minus"] = { link = "diffRemoved" }, -- deleted text (for diff files) + ["@diff.delta"] = { link = "diffChanged" }, -- deleted text (for diff files) + + -- Tags + ["@tag"] = { fg = C.mauve }, -- Tags like html tag names. + ["@tag.attribute"] = { fg = C.teal, style = O.styles.miscs or { "italic" } }, -- Tags like html tag names. + ["@tag.delimiter"] = { fg = C.sky }, -- Tag delimiter like < > / + + -- Misc + ["@error"] = { link = "Error" }, + + -- Language specific: + -- bash + ["@function.builtin.bash"] = { fg = C.red, style = O.styles.miscs or { "italic" } }, + + -- markdown + ["@markup.heading.1.markdown"] = { link = "rainbow1" }, + ["@markup.heading.2.markdown"] = { link = "rainbow2" }, + ["@markup.heading.3.markdown"] = { link = "rainbow3" }, + ["@markup.heading.4.markdown"] = { link = "rainbow4" }, + ["@markup.heading.5.markdown"] = { link = "rainbow5" }, + ["@markup.heading.6.markdown"] = { link = "rainbow6" }, + + -- java + ["@constant.java"] = { fg = C.teal }, + + -- css + ["@property.css"] = { fg = C.lavender }, + ["@property.id.css"] = { fg = C.blue }, + ["@property.class.css"] = { fg = C.yellow }, + ["@type.css"] = { fg = C.lavender }, + ["@type.tag.css"] = { fg = C.mauve }, + ["@string.plain.css"] = { fg = C.peach }, + ["@number.css"] = { fg = C.peach }, + + -- toml + ["@property.toml"] = { fg = C.blue }, -- Differentiates between string and properties + + -- json + ["@label.json"] = { fg = C.blue }, -- For labels: label: in C and :label: in Lua. + + -- lua + ["@constructor.lua"] = { fg = C.flamingo }, -- For constructor calls and definitions: = { } in Lua. + + -- typescript + ["@property.typescript"] = { fg = C.lavender, style = O.styles.properties or {} }, + ["@constructor.typescript"] = { fg = C.lavender }, + + -- TSX (Typescript React) + ["@constructor.tsx"] = { fg = C.lavender }, + ["@tag.attribute.tsx"] = { fg = C.teal, style = O.styles.miscs or { "italic" } }, + + -- yaml + ["@variable.member.yaml"] = { fg = C.blue }, -- For fields. + + -- Ruby + ["@string.special.symbol.ruby"] = { fg = C.flamingo }, + + -- PHP + ["@function.method.php"] = { link = "Function" }, + ["@function.method.call.php"] = { link = "Function" }, + + -- C/CPP + ["@type.builtin.c"] = { fg = C.yellow, style = {} }, + ["@property.cpp"] = { fg = C.text }, + ["@type.builtin.cpp"] = { fg = C.yellow, style = {} }, + + -- gitcommit + ["@comment.warning.gitcommit"] = { fg = C.yellow }, + + -- Misc + gitcommitSummary = { fg = C.rosewater, style = O.styles.miscs or { "italic" } }, + zshKSHFunction = { link = "Function" }, + } + + -- Legacy highlights + colors["@parameter"] = colors["@variable.parameter"] + colors["@field"] = colors["@variable.member"] + colors["@namespace"] = colors["@module"] + colors["@float"] = colors["@number.float"] + colors["@symbol"] = colors["@string.special.symbol"] + colors["@string.regex"] = colors["@string.regexp"] + + colors["@text"] = colors["@markup"] + colors["@text.strong"] = colors["@markup.strong"] + colors["@text.emphasis"] = colors["@markup.italic"] + colors["@text.underline"] = colors["@markup.underline"] + colors["@text.strike"] = colors["@markup.strikethrough"] + colors["@text.uri"] = colors["@markup.link.url"] + colors["@text.math"] = colors["@markup.math"] + colors["@text.environment"] = colors["@markup.environment"] + colors["@text.environment.name"] = colors["@markup.environment.name"] + + colors["@text.title"] = colors["@markup.heading"] + colors["@text.literal"] = colors["@markup.raw"] + colors["@text.reference"] = colors["@markup.link"] + + colors["@text.todo.checked"] = colors["@markup.list.checked"] + colors["@text.todo.unchecked"] = colors["@markup.list.unchecked"] + + colors["@comment.note"] = colors["@comment.hint"] + + -- @text.todo is now for todo comments, not todo notes like in markdown + colors["@text.todo"] = colors["@comment.todo"] + colors["@text.warning"] = colors["@comment.warning"] + colors["@text.note"] = colors["@comment.note"] + colors["@text.danger"] = colors["@comment.error"] + + -- @text.uri is now + -- > @markup.link.url in markup links + -- > @string.special.url outside of markup + colors["@text.uri"] = colors["@markup.link.uri"] + + colors["@method"] = colors["@function.method"] + colors["@method.call"] = colors["@function.method.call"] + + colors["@text.diff.add"] = colors["@diff.plus"] + colors["@text.diff.delete"] = colors["@diff.minus"] + + colors["@type.qualifier"] = colors["@keyword.modifier"] + colors["@keyword.storage"] = colors["@keyword.modifier"] + colors["@define"] = colors["@keyword.directive.define"] + colors["@preproc"] = colors["@keyword.directive"] + colors["@storageclass"] = colors["@keyword.storage"] + colors["@conditional"] = colors["@keyword.conditional"] + colors["@exception"] = colors["@keyword.exception"] + colors["@include"] = colors["@keyword.import"] + colors["@repeat"] = colors["@keyword.repeat"] + + colors["@symbol.ruby"] = colors["@string.special.symbol.ruby"] + + colors["@variable.member.yaml"] = colors["@field.yaml"] + + colors["@text.title.1.markdown"] = colors["@markup.heading.1.markdown"] + colors["@text.title.2.markdown"] = colors["@markup.heading.2.markdown"] + colors["@text.title.3.markdown"] = colors["@markup.heading.3.markdown"] + colors["@text.title.4.markdown"] = colors["@markup.heading.4.markdown"] + colors["@text.title.5.markdown"] = colors["@markup.heading.5.markdown"] + colors["@text.title.6.markdown"] = colors["@markup.heading.6.markdown"] + + colors["@method.php"] = colors["@function.method.php"] + colors["@method.call.php"] = colors["@function.method.call.php"] + + return colors +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/treesitter_context.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/treesitter_context.lua new file mode 100644 index 00000000..a21518ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/treesitter_context.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return O.transparent_background and { + TreesitterContextBottom = { sp = C.dim, style = { "underline" } }, + } or { + TreesitterContextBottom = { + sp = C.surface0, + style = { "underline" }, + }, + TreesitterContextLineNumber = { + fg = C.surface1, + bg = C.mantle, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ts_rainbow.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ts_rainbow.lua new file mode 100644 index 00000000..c2827dd2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ts_rainbow.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + rainbowcol1 = { fg = C.red }, + rainbowcol2 = { fg = C.teal }, + rainbowcol3 = { fg = C.yellow }, + rainbowcol4 = { fg = C.blue }, + rainbowcol5 = { fg = C.pink }, + rainbowcol6 = { fg = C.flamingo }, + rainbowcol7 = { fg = C.green }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ts_rainbow2.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ts_rainbow2.lua new file mode 100644 index 00000000..651ea848 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ts_rainbow2.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + TSRainbowRed = { fg = C.red }, + TSRainbowYellow = { fg = C.yellow }, + TSRainbowBlue = { fg = C.blue }, + TSRainbowOrange = { fg = C.peach }, + TSRainbowGreen = { fg = C.green }, + TSRainbowViolet = { fg = C.mauve }, + TSRainbowCyan = { fg = C.teal }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ufo.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ufo.lua new file mode 100644 index 00000000..9e823a6d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/ufo.lua @@ -0,0 +1,10 @@ +local M = {} + +M.get = function() + return { + UfoFoldedFg = { fg = C.lavender }, + UfoFoldedEllipsis = { fg = C.crust, bg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/vim_sneak.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/vim_sneak.lua new file mode 100644 index 00000000..fa09bca2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/vim_sneak.lua @@ -0,0 +1,10 @@ +local M = {} + +function M.get() + return { + Sneak = { fg = C.overlay2, bg = C.pink }, + SneakScope = { bg = C.text }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/vimwiki.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/vimwiki.lua new file mode 100644 index 00000000..b7009716 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/vimwiki.lua @@ -0,0 +1,20 @@ +local M = {} + +function M.get() + return { + VimwikiLink = { fg = C.sky, bg = C.none }, + VimwikiHeaderChar = { fg = C.grey, bg = C.none }, + VimwikiHR = { fg = C.yellow, bg = C.none }, + VimwikiList = { fg = C.peach, bg = C.none }, + VimwikiTag = { fg = C.peach, bg = C.none }, + VimwikiMarkers = { fg = C.subtext0, bg = C.none }, + VimwikiHeader1 = { fg = C.peach, bg = C.none, style = { "bold" } }, + VimwikiHeader2 = { fg = C.green, bg = C.none, style = { "bold" } }, + VimwikiHeader3 = { fg = C.blue, bg = C.none, style = { "bold" } }, + VimwikiHeader4 = { fg = C.sky, bg = C.none, style = { "bold" } }, + VimwikiHeader5 = { fg = C.yellow, bg = C.none, style = { "bold" } }, + VimwikiHeader6 = { fg = C.mauve, bg = C.none, style = { "bold" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/which_key.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/which_key.lua new file mode 100644 index 00000000..3983d449 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/which_key.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + WhichKey = { link = "NormalFloat" }, + WhichKeyBorder = { link = "FloatBorder" }, + + WhichKeyGroup = { fg = C.blue }, + WhichKeySeparator = { fg = C.overlay0 }, + WhichKeyDesc = { fg = C.pink }, + WhichKeyValue = { fg = C.overlay0 }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/window_picker.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/window_picker.lua new file mode 100644 index 00000000..06af3e56 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/integrations/window_picker.lua @@ -0,0 +1,12 @@ +local M = {} + +function M.get() + return { + WindowPickerStatusLine = { fg = C.red, style = { "bold" } }, + WindowPickerStatusLineNC = { fg = C.red, style = { "bold" } }, + WindowPickerWinBar = { fg = C.red, style = { "bold" } }, + WindowPickerWinBarNC = { fg = C.red, style = { "bold" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/syntax.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/syntax.lua new file mode 100644 index 00000000..055ddfe1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/syntax.lua @@ -0,0 +1,102 @@ +local M = {} + +function M.get() + return { + Comment = { fg = C.overlay0, style = O.styles.comments }, -- just comments + SpecialComment = { link = "Special" }, -- special things inside a comment + Constant = { fg = C.peach }, -- (preferred) any constant + String = { fg = C.green, style = O.styles.strings or {} }, -- a string constant: "this is a string" + Character = { fg = C.teal }, -- a character constant: 'c', '\n' + Number = { fg = C.peach, style = O.styles.numbers or {} }, -- a number constant: 234, 0xff + Float = { link = "Number" }, -- a floating point constant: 2.3e10 + Boolean = { fg = C.peach, style = O.styles.booleans or {} }, -- a boolean constant: TRUE, false + Identifier = { fg = C.flamingo, style = O.styles.variables or {} }, -- (preferred) any variable name + Function = { fg = C.blue, style = O.styles.functions or {} }, -- function name (also: methods for classes) + Statement = { fg = C.mauve }, -- (preferred) any statement + Conditional = { fg = C.mauve, style = O.styles.conditionals or {} }, -- if, then, else, endif, switch, etc. + Repeat = { fg = C.mauve, style = O.styles.loops or {} }, -- for, do, while, etc. + Label = { fg = C.sapphire }, -- case, default, etc. + Operator = { fg = C.sky, style = O.styles.operators or {} }, -- "sizeof", "+", "*", etc. + Keyword = { fg = C.mauve, style = O.styles.keywords or {} }, -- any other keyword + Exception = { fg = C.mauve, style = O.styles.keywords or {} }, -- try, catch, throw + + PreProc = { fg = C.pink }, -- (preferred) generic Preprocessor + Include = { fg = C.mauve, style = O.styles.keywords or {} }, -- preprocessor #include + Define = { link = "PreProc" }, -- preprocessor #define + Macro = { fg = C.mauve }, -- same as Define + PreCondit = { link = "PreProc" }, -- preprocessor #if, #else, #endif, etc. + + StorageClass = { fg = C.yellow }, -- static, register, volatile, etc. + Structure = { fg = C.yellow }, -- struct, union, enum, etc. + Special = { fg = C.pink }, -- (preferred) any special symbol + Type = { fg = C.yellow, style = O.styles.types or {} }, -- (preferred) int, long, char, etc. + Typedef = { link = "Type" }, -- A typedef + SpecialChar = { link = "Special" }, -- special character in a constant + Tag = { fg = C.lavender, style = { "bold" } }, -- you can use CTRL-] on this + Delimiter = { fg = C.overlay2 }, -- character that needs attention + Debug = { link = "Special" }, -- debugging statements + + Underlined = { style = { "underline" } }, -- (preferred) text that stands out, HTML links + Bold = { style = { "bold" } }, + Italic = { style = { "italic" } }, + -- ("Ignore", below, may be invisible...) + -- Ignore = { }, -- (preferred) left blank, hidden |hl-Ignore| + + Error = { fg = C.red }, -- (preferred) any erroneous construct + Todo = { bg = C.flamingo, fg = C.base, style = { "bold" } }, -- (preferred) anything that needs extra attention; mostly the keywords TODO FIXME and XXX + qfLineNr = { fg = C.yellow }, + qfFileName = { fg = C.blue }, + htmlH1 = { fg = C.pink, style = { "bold" } }, + htmlH2 = { fg = C.blue, style = { "bold" } }, + -- mkdHeading = { fg = C.peach, style = { "bold" } }, + -- mkdCode = { bg = C.terminal_black, fg = C.text }, + mkdCodeDelimiter = { bg = C.base, fg = C.text }, + mkdCodeStart = { fg = C.flamingo, style = { "bold" } }, + mkdCodeEnd = { fg = C.flamingo, style = { "bold" } }, + -- mkdLink = { fg = C.blue, style = { "underline" } }, + + -- debugging + debugPC = { bg = O.transparent_background and C.none or C.crust }, -- used for highlighting the current line in terminal-debug + debugBreakpoint = { bg = C.base, fg = C.overlay0 }, -- used for breakpoint colors in terminal-debug + -- illuminate + illuminatedWord = { bg = C.surface1 }, + illuminatedCurWord = { bg = C.surface1 }, + -- diff + diffAdded = { fg = C.green }, + diffRemoved = { fg = C.red }, + diffChanged = { fg = C.blue }, + diffOldFile = { fg = C.yellow }, + diffNewFile = { fg = C.peach }, + diffFile = { fg = C.blue }, + diffLine = { fg = C.overlay0 }, + diffIndexLine = { fg = C.teal }, + DiffAdd = { bg = U.darken(C.green, 0.18, C.base) }, -- diff mode: Added line |diff.txt| + DiffChange = { bg = U.darken(C.blue, 0.07, C.base) }, -- diff mode: Changed line |diff.txt| + DiffDelete = { bg = U.darken(C.red, 0.18, C.base) }, -- diff mode: Deleted line |diff.txt| + DiffText = { bg = U.darken(C.blue, 0.30, C.base) }, -- diff mode: Changed text within a changed line |diff.txt| + -- NeoVim + healthError = { fg = C.red }, + healthSuccess = { fg = C.teal }, + healthWarning = { fg = C.yellow }, + -- misc + + -- glyphs + GlyphPalette1 = { fg = C.red }, + GlyphPalette2 = { fg = C.teal }, + GlyphPalette3 = { fg = C.yellow }, + GlyphPalette4 = { fg = C.blue }, + GlyphPalette6 = { fg = C.teal }, + GlyphPalette7 = { fg = C.text }, + GlyphPalette9 = { fg = C.red }, + + -- rainbow + rainbow1 = { fg = C.red }, + rainbow2 = { fg = C.peach }, + rainbow3 = { fg = C.yellow }, + rainbow4 = { fg = C.green }, + rainbow5 = { fg = C.sapphire }, + rainbow6 = { fg = C.lavender }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/terminal.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/terminal.lua new file mode 100644 index 00000000..26f4fd30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/groups/terminal.lua @@ -0,0 +1,31 @@ +local M = {} + +function M.get() + return { + terminal_color_0 = C.overlay0, + terminal_color_8 = C.overlay1, + + terminal_color_1 = C.red, + terminal_color_9 = C.red, + + terminal_color_2 = C.green, + terminal_color_10 = C.green, + + terminal_color_3 = C.yellow, + terminal_color_11 = C.yellow, + + terminal_color_4 = C.blue, + terminal_color_12 = C.blue, + + terminal_color_5 = C.pink, + terminal_color_13 = C.pink, + + terminal_color_6 = C.sky, + terminal_color_14 = C.sky, + + terminal_color_7 = C.text, + terminal_color_15 = C.text, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/init.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/init.lua new file mode 100644 index 00000000..ad205f95 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/init.lua @@ -0,0 +1,231 @@ +local is_vim = vim.fn.has "nvim" ~= 1 +if is_vim then require "catppuccin.lib.vim" end + +---@type Catppuccin +local M = { + default_options = { + flavour = "auto", + background = { + light = "latte", + dark = "mocha", + }, + compile_path = vim.fn.stdpath "cache" .. "/catppuccin", + transparent_background = false, + show_end_of_buffer = false, + term_colors = false, + kitty = vim.env.KITTY_WINDOW_ID and true or false, + dim_inactive = { + enabled = false, + shade = "dark", + percentage = 0.15, + }, + no_italic = false, + no_bold = false, + no_underline = false, + styles = { + comments = { "italic" }, + conditionals = { "italic" }, + loops = {}, + functions = {}, + keywords = {}, + strings = {}, + variables = {}, + numbers = {}, + booleans = {}, + properties = {}, + types = {}, + operators = {}, + }, + default_integrations = true, + integrations = { + alpha = true, + cmp = true, + dap = true, + dap_ui = true, + dashboard = true, + diffview = false, + flash = true, + gitsigns = true, + markdown = true, + neogit = true, + neotree = true, + nvimtree = true, + ufo = true, + rainbow_delimiters = true, + semantic_tokens = not is_vim, + telescope = { enabled = true }, + treesitter = not is_vim, + treesitter_context = true, + barbecue = { + dim_dirname = true, + bold_basename = true, + dim_context = false, + alt_background = false, + }, + illuminate = { + enabled = true, + lsp = false, + }, + indent_blankline = { + enabled = true, + scope_color = "", + colored_indent_levels = false, + }, + native_lsp = { + enabled = true, + virtual_text = { + errors = { "italic" }, + hints = { "italic" }, + warnings = { "italic" }, + information = { "italic" }, + }, + underlines = { + errors = { "underline" }, + hints = { "underline" }, + warnings = { "underline" }, + information = { "underline" }, + }, + inlay_hints = { + background = true, + }, + }, + navic = { + enabled = false, + custom_bg = "NONE", + }, + dropbar = { + enabled = true, + color_mode = false, + }, + colorful_winsep = { + enabled = false, + color = "red", + }, + }, + color_overrides = {}, + highlight_overrides = {}, + }, + flavours = { latte = 1, frappe = 2, macchiato = 3, mocha = 4 }, + path_sep = jit and (jit.os == "Windows" and "\\" or "/") or package.config:sub(1, 1), +} + +M.options = M.default_options + +function M.compile() + local user_flavour = M.flavour + for flavour, _ in pairs(M.flavours) do + M.flavour = flavour + require("catppuccin.lib." .. (is_vim and "vim." or "") .. "compiler").compile(flavour) + end + M.flavour = user_flavour -- Restore user flavour after compile +end + +local function get_flavour(default) + local flavour + if default and default == M.flavour and vim.o.background ~= (M.flavour == "latte" and "light" or "dark") then + flavour = M.options.background[vim.o.background] + else + flavour = default + end + + if flavour and not M.flavours[flavour] then + vim.notify( + string.format( + "Catppuccin (error): Invalid flavour '%s', flavour must be 'latte', 'frappe', 'macchiato', 'mocha' or 'auto'", + flavour + ), + vim.log.levels.ERROR + ) + flavour = nil + end + return flavour or M.options.flavour or vim.g.catppuccin_flavour or M.options.background[vim.o.background] +end + +local did_setup = false + +function M.load(flavour) + if M.options.flavour == "auto" then -- set colorscheme based on o:background + M.options.flavour = nil -- ensure that this will only run once on startup + end + if not did_setup then M.setup() end + M.flavour = get_flavour(flavour) + local compiled_path = M.options.compile_path .. M.path_sep .. M.flavour + local f = loadfile(compiled_path) + if not f then + M.compile() + f = assert(loadfile(compiled_path), "could not load cache") + end + f(flavour or M.options.flavour or vim.g.catppuccin_flavour) +end + +---@type fun(user_conf: CatppuccinOptions?) +function M.setup(user_conf) + did_setup = true + -- Parsing user config + user_conf = user_conf or {} + + if user_conf.default_integrations == false then M.default_options.integrations = {} end + + M.options = vim.tbl_deep_extend("keep", user_conf, M.default_options) + M.options.highlight_overrides.all = user_conf.custom_highlights or M.options.highlight_overrides.all + + -- Get cached hash + local cached_path = M.options.compile_path .. M.path_sep .. "cached" + local file = io.open(cached_path) + local cached = nil + if file then + cached = file:read() + file:close() + end + + -- Get current hash + local git_path = debug.getinfo(1).source:sub(2, -24) .. ".git" + local git = vim.fn.getftime(git_path) -- 2x faster vim.loop.fs_stat + local hash = require("catppuccin.lib.hashing").hash(user_conf) + .. (git == -1 and git_path or git) -- no .git in /nix/store -> cache path + .. (vim.o.winblend == 0 and 1 or 0) -- :h winblend + .. (vim.o.pumblend == 0 and 1 or 0) -- :h pumblend + + -- Recompile if hash changed + if cached ~= hash then + M.compile() + file = io.open(cached_path, "wb") + if file then + file:write(hash) + file:close() + end + end +end + +if is_vim then return M end + +vim.api.nvim_create_user_command( + "Catppuccin", + function(inp) vim.api.nvim_command("colorscheme catppuccin-" .. get_flavour(inp.args)) end, + { + nargs = 1, + complete = function(line) + return vim.tbl_filter(function(val) return vim.startswith(val, line) end, vim.tbl_keys(M.flavours)) + end, + } +) + +vim.api.nvim_create_user_command("CatppuccinCompile", function() + for name, _ in pairs(package.loaded) do + if name:match "^catppuccin." then package.loaded[name] = nil end + end + M.compile() + vim.notify("Catppuccin (info): compiled cache!", vim.log.levels.INFO) + vim.cmd.colorscheme "catppuccin" +end, {}) + +if vim.g.catppuccin_debug then + vim.api.nvim_create_autocmd("BufWritePost", { + pattern = "*/catppuccin/*", + callback = function() + vim.schedule(function() vim.cmd "CatppuccinCompile" end) + end, + }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/compiler.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/compiler.lua new file mode 100644 index 00000000..5c844567 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/compiler.lua @@ -0,0 +1,104 @@ +local path_sep = require("catppuccin").path_sep +local O = require("catppuccin").options +local M = {} + +-- Credit: https://github.com/EdenEast/nightfox.nvim +local fmt = string.format + +local function inspect(t) + local list = {} + for k, v in pairs(t) do + local tv = type(v) + if tv == "string" then + table.insert(list, fmt([[%s = "%s"]], k, v)) + elseif tv == "table" then + table.insert(list, fmt([[%s = %s]], k, inspect(v))) + else + table.insert(list, fmt([[%s = %s]], k, tostring(v))) + end + end + return fmt([[{ %s }]], table.concat(list, ", ")) +end + +function M.compile(flavour) + local theme = require("catppuccin.lib.mapper").apply(flavour) + local lines = { + string.format( + [[ +return string.dump(function(flavour) +vim.o.termguicolors = true +if vim.g.colors_name then vim.cmd("hi clear") end +if flavour then vim.o.background = "%s" end +vim.g.colors_name = "catppuccin-%s" +local h = vim.api.nvim_set_hl]], + flavour == "latte" and "light" or "dark", + flavour + ), + } + if path_sep == "\\" then O.compile_path = O.compile_path:gsub("/", "\\") end + + local tbl = vim.tbl_deep_extend("keep", theme.custom_highlights, theme.integrations, theme.syntax, theme.editor) + + if O.term_colors == true then + for k, v in pairs(theme.terminal) do + table.insert(lines, fmt('vim.g.%s = "%s"', k, v)) + end + end + + for group, color in pairs(tbl) do + if color.style then + for _, style in pairs(color.style) do + color[style] = true + if O.no_italic and style == "italic" then color[style] = false end + if O.no_bold and style == "bold" then color[style] = false end + if O.no_underline and style == "underline" then color[style] = false end + end + end + color.style = nil + if color.link and (theme.custom_highlights[group] and not theme.custom_highlights[group].link) then + color.link = nil + end + table.insert(lines, fmt([[h(0, "%s", %s)]], group, inspect(color))) + end + table.insert(lines, "end, true)") + if vim.fn.isdirectory(O.compile_path) == 0 then vim.fn.mkdir(O.compile_path, "p") end + + if vim.g.catppuccin_debug then -- Debugging purpose + local deb = io.open(O.compile_path .. path_sep .. flavour .. ".lua", "wb") + if deb then + deb:write(table.concat(lines, "\n")) + deb:close() + end + end + + local f = loadstring(table.concat(lines, "\n")) + if not f then + local err_path = (path_sep == "/" and "/tmp" or os.getenv "TMP") .. "/catppuccin_error.lua" + print(string.format( + [[Catppuccin (error): Most likely some mistake made in your catppuccin config +You can open %s for debugging + +If you think this is a bug, kindly open an issue and attach %s file +Below is the error message that we captured: +]], + err_path, + err_path + )) + local err = io.open(err_path, "wb") + if err then + err:write(table.concat(lines, "\n")) + err:close() + end + dofile(err_path) + return + end + + local file = assert( + io.open(O.compile_path .. path_sep .. flavour, "wb"), + "Permission denied while writing compiled file to " .. O.compile_path .. path_sep .. flavour + ) + file:write(f()) + file:close() +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/hashing.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/hashing.lua new file mode 100644 index 00000000..d2272f15 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/hashing.lua @@ -0,0 +1,26 @@ +local M = {} +local B = bit or bit32 or require "catppuccin.lib.vim.bit" + +local hash_str = function(str) -- djb2, https://theartincode.stanis.me/008-djb2/ + local hash = 5381 + for i = 1, #str do + hash = B.lshift(hash, 5) + hash + string.byte(str, i) + end + return hash +end + +function M.hash(v) -- Xor hashing: https://codeforces.com/blog/entry/85900 + local t = type(v) + if t == "table" then + local hash = 0 + for p, u in next, v do + hash = B.bxor(hash, hash_str(p .. M.hash(u))) + end + return hash + elseif t == "function" then + return M.hash(v(require("catppuccin.palettes").get_palette())) + end + return tostring(v) +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/highlighter.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/highlighter.lua new file mode 100644 index 00000000..d6887437 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/highlighter.lua @@ -0,0 +1,20 @@ +-- DEPRECATED MODULE +local M = {} + +function M.highlight(group, color) + if color.style then + for _, style in ipairs(color.style) do + color[style] = true + end + end + color.style = nil + vim.api.nvim_set_hl(0, group, color) +end + +function M.syntax(tbl) + for group, colors in pairs(tbl) do + M.highlight(group, colors) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/hsluv.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/hsluv.lua new file mode 100644 index 00000000..36354164 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/hsluv.lua @@ -0,0 +1,290 @@ +--[[ +Lua implementation of HSLuv and HPLuv color spaces +Homepage: http://www.hsluv.org/ + +Copyright (C) 2019 Alexei Boronine + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] +local hsluv = {} + +hsluv.hexChars = "0123456789abcdef" + +local distance_line_from_origin = function(line) return math.abs(line.intercept) / math.sqrt((line.slope ^ 2) + 1) end + +local length_of_ray_until_intersect = function(theta, line) + return line.intercept / (math.sin(theta) - line.slope * math.cos(theta)) +end + +hsluv.get_bounds = function(l) + local result = {} + local sub2 + local sub1 = ((l + 16) ^ 3) / 1560896 + if sub1 > hsluv.epsilon then + sub2 = sub1 + else + sub2 = l / hsluv.kappa + end + + for i = 1, 3 do + local m1 = hsluv.m[i][1] + local m2 = hsluv.m[i][2] + local m3 = hsluv.m[i][3] + + for t = 0, 1 do + local top1 = (284517 * m1 - 94839 * m3) * sub2 + local top2 = (838422 * m3 + 769860 * m2 + 731718 * m1) * l * sub2 - 769860 * t * l + local bottom = (632260 * m3 - 126452 * m2) * sub2 + 126452 * t + table.insert(result, { slope = top1 / bottom, intercept = top2 / bottom }) + end + end + return result +end + +hsluv.max_safe_chroma_for_l = function(l) + local bounds = hsluv.get_bounds(l) + local min = 1.7976931348623157e+308 + + for i = 1, 6 do + local length = distance_line_from_origin(bounds[i]) + if length >= 0 then min = math.min(min, length) end + end + return min +end + +hsluv.max_safe_chroma_for_lh = function(l, h) + local hrad = h / 360 * math.pi * 2 + local bounds = hsluv.get_bounds(l) + local min = 1.7976931348623157e+308 + + for i = 1, 6 do + local bound = bounds[i] + local length = length_of_ray_until_intersect(hrad, bound) + if length >= 0 then min = math.min(min, length) end + end + return min +end + +hsluv.dot_product = function(a, b) + local sum = 0 + for i = 1, 3 do + sum = sum + a[i] * b[i] + end + return sum +end + +hsluv.from_linear = function(c) + if c <= 0.0031308 then + return 12.92 * c + else + return 1.055 * (c ^ 0.416666666666666685) - 0.055 + end +end + +hsluv.to_linear = function(c) + if c > 0.04045 then + return ((c + 0.055) / 1.055) ^ 2.4 + else + return c / 12.92 + end +end + +hsluv.xyz_to_rgb = function(tuple) + return { + hsluv.from_linear(hsluv.dot_product(hsluv.m[1], tuple)), + hsluv.from_linear(hsluv.dot_product(hsluv.m[2], tuple)), + hsluv.from_linear(hsluv.dot_product(hsluv.m[3], tuple)), + } +end + +hsluv.rgb_to_xyz = function(tuple) + local rgbl = { hsluv.to_linear(tuple[1]), hsluv.to_linear(tuple[2]), hsluv.to_linear(tuple[3]) } + return { + hsluv.dot_product(hsluv.minv[1], rgbl), + hsluv.dot_product(hsluv.minv[2], rgbl), + hsluv.dot_product(hsluv.minv[3], rgbl), + } +end + +hsluv.y_to_l = function(Y) + if Y <= hsluv.epsilon then + return Y / hsluv.refY * hsluv.kappa + else + return 116 * ((Y / hsluv.refY) ^ 0.333333333333333315) - 16 + end +end + +hsluv.l_to_y = function(L) + if L <= 8 then + return hsluv.refY * L / hsluv.kappa + else + return hsluv.refY * (((L + 16) / 116) ^ 3) + end +end + +hsluv.xyz_to_luv = function(tuple) + local X = tuple[1] + local Y = tuple[2] + local divider = X + 15 * Y + 3 * tuple[3] + local varU = 4 * X + local varV = 9 * Y + if divider ~= 0 then + varU = varU / divider + varV = varV / divider + else + varU = 0 + varV = 0 + end + local L = hsluv.y_to_l(Y) + if L == 0 then return { 0, 0, 0 } end + return { L, 13 * L * (varU - hsluv.refU), 13 * L * (varV - hsluv.refV) } +end + +hsluv.luv_to_xyz = function(tuple) + local L = tuple[1] + local U = tuple[2] + local V = tuple[3] + if L == 0 then return { 0, 0, 0 } end + local varU = U / (13 * L) + hsluv.refU + local varV = V / (13 * L) + hsluv.refV + local Y = hsluv.l_to_y(L) + local X = 0 - (9 * Y * varU) / (((varU - 4) * varV) - varU * varV) + return { X, Y, (9 * Y - 15 * varV * Y - varV * X) / (3 * varV) } +end + +hsluv.luv_to_lch = function(tuple) + local L = tuple[1] + local U = tuple[2] + local V = tuple[3] + local C = math.sqrt(U * U + V * V) + local H + if C < 0.00000001 then + H = 0 + else + H = math.atan2(V, U) * 180.0 / 3.1415926535897932 + if H < 0 then H = 360 + H end + end + return { L, C, H } +end + +hsluv.lch_to_luv = function(tuple) + local L = tuple[1] + local C = tuple[2] + local Hrad = tuple[3] / 360.0 * 2 * math.pi + return { L, math.cos(Hrad) * C, math.sin(Hrad) * C } +end + +hsluv.hsluv_to_lch = function(tuple) + local H = tuple[1] + local S = tuple[2] + local L = tuple[3] + if L > 99.9999999 then return { 100, 0, H } end + if L < 0.00000001 then return { 0, 0, H } end + return { L, hsluv.max_safe_chroma_for_lh(L, H) / 100 * S, H } +end + +hsluv.lch_to_hsluv = function(tuple) + local L = tuple[1] + local C = tuple[2] + local H = tuple[3] + local max_chroma = hsluv.max_safe_chroma_for_lh(L, H) + if L > 99.9999999 then return { H, 0, 100 } end + if L < 0.00000001 then return { H, 0, 0 } end + + return { H, C / max_chroma * 100, L } +end + +hsluv.hpluv_to_lch = function(tuple) + local H = tuple[1] + local S = tuple[2] + local L = tuple[3] + if L > 99.9999999 then return { 100, 0, H } end + if L < 0.00000001 then return { 0, 0, H } end + return { L, hsluv.max_safe_chroma_for_l(L) / 100 * S, H } +end + +hsluv.lch_to_hpluv = function(tuple) + local L = tuple[1] + local C = tuple[2] + local H = tuple[3] + if L > 99.9999999 then return { H, 0, 100 } end + if L < 0.00000001 then return { H, 0, 0 } end + return { H, C / hsluv.max_safe_chroma_for_l(L) * 100, L } +end + +hsluv.rgb_to_hex = function(tuple) + local h = "#" + for i = 1, 3 do + local c = math.floor(tuple[i] * 255 + 0.5) + local digit2 = math.fmod(c, 16) + local x = (c - digit2) / 16 + local digit1 = math.floor(x) + h = h .. string.sub(hsluv.hexChars, digit1 + 1, digit1 + 1) + h = h .. string.sub(hsluv.hexChars, digit2 + 1, digit2 + 1) + end + return h +end + +hsluv.hex_to_rgb = function(hex) + hex = string.lower(hex) + local ret = {} + for i = 0, 2 do + local char1 = string.sub(hex, i * 2 + 2, i * 2 + 2) + local char2 = string.sub(hex, i * 2 + 3, i * 2 + 3) + local digit1 = string.find(hsluv.hexChars, char1) - 1 + local digit2 = string.find(hsluv.hexChars, char2) - 1 + ret[i + 1] = (digit1 * 16 + digit2) / 255.0 + end + return ret +end + +hsluv.lch_to_rgb = function(tuple) return hsluv.xyz_to_rgb(hsluv.luv_to_xyz(hsluv.lch_to_luv(tuple))) end + +hsluv.rgb_to_lch = function(tuple) return hsluv.luv_to_lch(hsluv.xyz_to_luv(hsluv.rgb_to_xyz(tuple))) end + +hsluv.hsluv_to_rgb = function(tuple) return hsluv.lch_to_rgb(hsluv.hsluv_to_lch(tuple)) end + +hsluv.rgb_to_hsluv = function(tuple) return hsluv.lch_to_hsluv(hsluv.rgb_to_lch(tuple)) end + +hsluv.hpluv_to_rgb = function(tuple) return hsluv.lch_to_rgb(hsluv.hpluv_to_lch(tuple)) end + +hsluv.rgb_to_hpluv = function(tuple) return hsluv.lch_to_hpluv(hsluv.rgb_to_lch(tuple)) end + +hsluv.hsluv_to_hex = function(tuple) return hsluv.rgb_to_hex(hsluv.hsluv_to_rgb(tuple)) end + +hsluv.hpluv_to_hex = function(tuple) return hsluv.rgb_to_hex(hsluv.hpluv_to_rgb(tuple)) end + +hsluv.hex_to_hsluv = function(s) return hsluv.rgb_to_hsluv(hsluv.hex_to_rgb(s)) end + +hsluv.hex_to_hpluv = function(s) return hsluv.rgb_to_hpluv(hsluv.hex_to_rgb(s)) end + +hsluv.m = { + { 3.240969941904521, -1.537383177570093, -0.498610760293 }, + { -0.96924363628087, 1.87596750150772, 0.041555057407175 }, + { 0.055630079696993, -0.20397695888897, 1.056971514242878 }, +} +hsluv.minv = { + { 0.41239079926595, 0.35758433938387, 0.18048078840183 }, + { 0.21263900587151, 0.71516867876775, 0.072192315360733 }, + { 0.019330818715591, 0.11919477979462, 0.95053215224966 }, +} +hsluv.refY = 1.0 +hsluv.refU = 0.19783000664283 +hsluv.refV = 0.46831999493879 +hsluv.kappa = 903.2962962 +hsluv.epsilon = 0.0088564516 + +return hsluv diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/mapper.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/mapper.lua new file mode 100644 index 00000000..2f60323d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/mapper.lua @@ -0,0 +1,72 @@ +local M = {} + +function M.apply(flavour) + flavour = flavour or require("catppuccin").flavour + + local _O, _C, _U = O, C, U -- Borrowing global var (setfenv doesn't work with require) + O = require("catppuccin").options + C = require("catppuccin.palettes").get_palette(flavour) + U = require "catppuccin.utils.colors" + + C.none = "NONE" + + local dim_percentage = O.dim_inactive.percentage + C.dim = O.dim_inactive.shade == "dark" + and U.vary_color( + { latte = U.darken(C.base, dim_percentage, C.mantle) }, + U.darken(C.base, dim_percentage, C.mantle) + ) + or U.vary_color( + { latte = U.lighten("#FBFCFD", dim_percentage, C.base) }, + U.lighten(C.surface0, dim_percentage, C.base) + ) + + local theme = {} + theme.syntax = require("catppuccin.groups.syntax").get() + theme.editor = require("catppuccin.groups.editor").get() + local final_integrations = {} + + -- https://github.com/catppuccin/nvim/pull/624 + if type(O.integrations.dap) == "table" and O.integrations.dap.enable_ui ~= nil then + O.integrations.dap_ui = O.integrations.dap.enable_ui + O.integrations.dap.enable_ui = nil + end + + for integration in pairs(O.integrations) do + local cot = false + if type(O.integrations[integration]) == "table" then + if O.integrations[integration].enabled == true then cot = true end + else + if O.integrations[integration] == true then + local default = require("catppuccin").default_options.integrations[integration] + O.integrations[integration] = type(default) == "table" and default or {} + O.integrations[integration].enabled = true + cot = true + end + end + + if cot then + final_integrations = vim.tbl_deep_extend( + "force", + final_integrations, + require("catppuccin.groups.integrations." .. integration).get() + ) + end + end + + theme.integrations = final_integrations -- plugins + theme.terminal = require("catppuccin.groups.terminal").get() -- terminal colors + local user_highlights = O.highlight_overrides + if type(user_highlights[flavour]) == "function" then user_highlights[flavour] = user_highlights[flavour](C) end + theme.custom_highlights = vim.tbl_deep_extend( + "keep", + user_highlights[flavour] or {}, + type(user_highlights.all) == "function" and user_highlights.all(C) or user_highlights.all or {} + ) + + O, C, U = _O, _C, _U -- Returning global var + + return theme +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/bit.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/bit.lua new file mode 100644 index 00000000..ea565fe5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/bit.lua @@ -0,0 +1,79 @@ +-- Reference(stripped down): https://github.com/davidm/lua-bit-numberlua/blob/master/lmod/bit/numberlua.lua +local M = {} + +local floor = math.floor + +local MOD = 2 ^ 32 + +local function memoize(f) + local mt = {} + local t = setmetatable({}, mt) + function mt:__index(k) + local v = f(k) + t[k] = v + return v + end + + return t +end + +local function make_bitop_uncached(t, m) + local function bitop(a, b) + local res, p = 0, 1 + while a ~= 0 and b ~= 0 do + local am, bm = a % m, b % m + res = res + t[am][bm] * p + a = (a - am) / m + b = (b - bm) / m + p = p * m + end + res = res + (a + b) * p + return res + end + + return bitop +end + +local function make_bitop(t) + local op1 = make_bitop_uncached(t, 2 ^ 1) + local op2 = memoize(function(a) + return memoize(function(b) return op1(a, b) end) + end) + return make_bitop_uncached(op2, 2 ^ (t.n or 1)) +end + +M.bxor = make_bitop { [0] = { [0] = 0, [1] = 1 }, [1] = { [0] = 1, [1] = 0 }, n = 4 } +local bxor = M.bxor + +local lshift, rshift + +lshift = function(a, disp) -- Lua5.2 inspired + if disp < 0 then return rshift(a, -disp) end + return (a * 2 ^ disp) % 2 ^ 32 +end + +rshift = function(a, disp) -- Lua5.2 inspired + if disp < 0 then return lshift(a, -disp) end + return floor(a % 2 ^ 32 / 2 ^ disp) +end + +local function bit_tobit(x) + x = x % MOD + if x >= 0x80000000 then x = x - MOD end + return x +end + +local function bit_bxor(a, b, c, ...) + if c then + return bit_bxor(bit_bxor(a, b), c, ...) + elseif b then + return bit_tobit(bxor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end + +return { + bxor = bit_bxor, + lshift = function(x, n) return bit_tobit(lshift(x % MOD, n % 32)) end, +} diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/compiler.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/compiler.lua new file mode 100644 index 00000000..cf2bbdd2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/compiler.lua @@ -0,0 +1,107 @@ +local path_sep = require("catppuccin").path_sep +local O = require("catppuccin").options +local M = {} + +-- Reference: https://github.com/EdenEast/nightfox.nvim +local fmt = string.format + +function M.compile(flavour) + local theme = require("catppuccin.lib.mapper").apply(flavour) + local lines = { + string.format( + [=[ +return string.dump(function() +vim.command[[ +if exists("colors_name") + hi clear +endif +set termguicolors +set background=%s +let g:colors_name = "catppuccin-%s"]=], + (flavour == "latte" and "light" or "dark"), + flavour + ), + } + + local tbl = vim.tbl_deep_extend("keep", theme.custom_highlights, theme.integrations, theme.syntax, theme.editor) + + if O.term_colors == true then + for k, v in pairs(theme.terminal) do + table.insert(lines, fmt("let g:%s = '%s'", k, v)) + end + end + + for group, color in pairs(tbl) do + if color.link and (theme.custom_highlights[group] and not theme.custom_highlights[group].link) then + color.link = nil + end + if color.link then + table.insert(lines, fmt([[highlight! link %s %s]], group, color.link)) + else + if color.style then + local rstyle = {} + for _, style in pairs(color.style) do + if O.no_italic and style == "italic" then style = nil end + if O.no_bold and style == "bold" then style = nil end + if O.no_underline and style == "underline" then style = nil end + if style then rstyle[#rstyle + 1] = style end + end + color.style = table.concat(rstyle, ",") + end + if color.style == "" then color.style = nil end + table.insert( + lines, + fmt( + [[highlight %s guifg=%s guibg=%s gui=%s guisp=%s]], + group, + color.fg or "NONE", + color.bg or "NONE", + color.style or "NONE", + color.sp or "NONE" + ) + ) + end + end + table.insert(lines, "]]end, true)") + if vim.fn.isdirectory(O.compile_path) == 0 then vim.fn.mkdir(O.compile_path, "p") end + local ls = loadstring or load + + if vim.g.catppuccin_debug then -- Debugging purpose + local deb = io.open(O.compile_path .. path_sep .. flavour .. ".lua", "wb") + if deb then + deb:write(table.concat(lines, "\n")) + deb:close() + end + end + + local f = ls(table.concat(lines, "\n")) + if not f then + local err_path = (path_sep == "/" and "/tmp" or os.getenv "TMP") .. "/catppuccin_error.lua" + print(string.format( + [[Catppuccin (error): Most likely some mistake made in your catppuccin config +You can open %s for debugging + +If you think this is a bug, kindly open an issue and attach %s file +Below is the error message that we captured: +]], + err_path, + err_path + )) + local err = io.open(err_path, "wb") + if err then + err:write(table.concat(lines, "\n")) + err:close() + end + dofile(err_path) + return + end + + local file = assert( + io.open(O.compile_path .. path_sep .. flavour, "wb"), + "Permission denied while writing compiled file to " .. O.compile_path .. path_sep .. flavour + ) + file:write(f()) + file:close() +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/init.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/init.lua new file mode 100644 index 00000000..3ea1a1b7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/lib/vim/init.lua @@ -0,0 +1,83 @@ +-- TODO: private _G.vim +vim.command [[command! CatppuccinCompile lua require('catppuccin').compile() print("Catppuccin (info): compiled cache!")]] + +vim.o = setmetatable({}, { + __index = function(_, k) + if k == "background" then return vim.eval "&background" end + end, +}) + +vim.fn.stdpath = function(what) + if what ~= "cache" then return end + if package.config:sub(1, 1) == "\\" then + return vim.fn.expand "%localappdata%" .. [[Temp\vim]] + else + return (os.getenv "XDG_CACHE_HOME" or vim.fn.expand "$HOME/.cache") .. "/vim" + end +end + +vim.env = setmetatable({}, { + __index = function(_, k) return os.getenv(k) end, +}) + +-- Reference: https://github.com/neovim/neovim/blob/master/runtime/lua/vim/shared.lua +local function tbl_isempty(t) + assert(type(t) == "table", string.format("Expected table, got %s", type(t))) + return next(t) == nil +end + +local function tbl_islist(t) + if type(t) ~= "table" then return false end + + local count = 0 + + for k, _ in pairs(t) do + if type(k) == "number" then + count = count + 1 + else + return false + end + end + + if count > 0 then + return true + else + -- TODO(bfredl): in the future, we will always be inside nvim + -- then this check can be deleted. + if vim._empty_dict_mt == nil then return false end + return getmetatable(t) ~= vim._empty_dict_mt + end +end + +local function can_merge(v) return type(v) == "table" and (tbl_isempty(v) or not tbl_islist(v)) end + +local function tbl_extend(behavior, deep_extend, ...) + if behavior ~= "error" and behavior ~= "keep" and behavior ~= "force" then + error('invalid "behavior": ' .. tostring(behavior)) + end + + if select("#", ...) < 2 then + error("wrong number of arguments (given " .. tostring(1 + select("#", ...)) .. ", expected at least 3)") + end + + local ret = {} + if vim._empty_dict_mt ~= nil and getmetatable(select(1, ...)) == vim._empty_dict_mt then ret = vim.empty_dict() end + + for i = 1, select("#", ...) do + local tbl = select(i, ...) + if tbl then + for k, v in pairs(tbl) do + if deep_extend and can_merge(v) and can_merge(ret[k]) then + ret[k] = tbl_extend(behavior, true, ret[k], v) + elseif behavior ~= "force" and ret[k] ~= nil then + if behavior == "error" then error("key found in more than one map: " .. k) end -- Else behavior is "keep". + else + ret[k] = v + end + end + end + end + return ret +end + +function vim.tbl_deep_extend(behavior, ...) return tbl_extend(behavior, true, ...) end diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/frappe.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/frappe.lua new file mode 100644 index 00000000..911d0af4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/frappe.lua @@ -0,0 +1,28 @@ +return { + rosewater = "#f2d5cf", + flamingo = "#eebebe", + pink = "#f4b8e4", + mauve = "#ca9ee6", + red = "#e78284", + maroon = "#ea999c", + peach = "#ef9f76", + yellow = "#e5c890", + green = "#a6d189", + teal = "#81c8be", + sky = "#99d1db", + sapphire = "#85c1dc", + blue = "#8caaee", + lavender = "#babbf1", + text = "#c6d0f5", + subtext1 = "#b5bfe2", + subtext0 = "#a5adce", + overlay2 = "#949cbb", + overlay1 = "#838ba7", + overlay0 = "#737994", + surface2 = "#626880", + surface1 = "#51576d", + surface0 = "#414559", + base = "#303446", + mantle = "#292c3c", + crust = "#232634", +} diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/init.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/init.lua new file mode 100644 index 00000000..68030c45 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/init.lua @@ -0,0 +1,21 @@ +local M = {} + +function M.get_palette(flavour) + local flvr = flavour or require("catppuccin").flavour or vim.g.catppuccin_flavour or "mocha" + local _, palette = pcall(require, "catppuccin.palettes." .. flvr) + local O = require("catppuccin").options + local ans = vim.tbl_deep_extend("keep", O.color_overrides.all or {}, O.color_overrides[flvr] or {}, palette or {}) + if O.kitty then -- https://github.com/kovidgoyal/kitty/issues/2917 + for accent, hex in pairs(ans) do + local red_green_string = hex:sub(1, 5) + local blue_value = tonumber(hex:sub(6, 7), 16) + + -- Slightly increase or decrease brightness of the blue channel + blue_value = blue_value == 255 and blue_value - 1 or blue_value + 1 + ans[accent] = string.format("%s%.2x", red_green_string, blue_value) + end + end + return ans +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/latte.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/latte.lua new file mode 100644 index 00000000..d03f661e --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/latte.lua @@ -0,0 +1,28 @@ +return { + rosewater = "#dc8a78", + flamingo = "#dd7878", + pink = "#ea76cb", + mauve = "#8839ef", + red = "#d20f39", + maroon = "#e64553", + peach = "#fe640b", + yellow = "#df8e1d", + green = "#40a02b", + teal = "#179299", + sky = "#04a5e5", + sapphire = "#209fb5", + blue = "#1e66f5", + lavender = "#7287fd", + text = "#4c4f69", + subtext1 = "#5c5f77", + subtext0 = "#6c6f85", + overlay2 = "#7c7f93", + overlay1 = "#8c8fa1", + overlay0 = "#9ca0b0", + surface2 = "#acb0be", + surface1 = "#bcc0cc", + surface0 = "#ccd0da", + base = "#eff1f5", + mantle = "#e6e9ef", + crust = "#dce0e8", +} diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/macchiato.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/macchiato.lua new file mode 100644 index 00000000..f5d95c02 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/macchiato.lua @@ -0,0 +1,28 @@ +return { + rosewater = "#f4dbd6", + flamingo = "#f0c6c6", + pink = "#f5bde6", + mauve = "#c6a0f6", + red = "#ed8796", + maroon = "#ee99a0", + peach = "#f5a97f", + yellow = "#eed49f", + green = "#a6da95", + teal = "#8bd5ca", + sky = "#91d7e3", + sapphire = "#7dc4e4", + blue = "#8aadf4", + lavender = "#b7bdf8", + text = "#cad3f5", + subtext1 = "#b8c0e0", + subtext0 = "#a5adcb", + overlay2 = "#939ab7", + overlay1 = "#8087a2", + overlay0 = "#6e738d", + surface2 = "#5b6078", + surface1 = "#494d64", + surface0 = "#363a4f", + base = "#24273a", + mantle = "#1e2030", + crust = "#181926", +} diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/mocha.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/mocha.lua new file mode 100644 index 00000000..296c0008 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/palettes/mocha.lua @@ -0,0 +1,28 @@ +return { + rosewater = "#f5e0dc", + flamingo = "#f2cdcd", + pink = "#f5c2e7", + mauve = "#cba6f7", + red = "#f38ba8", + maroon = "#eba0ac", + peach = "#fab387", + yellow = "#f9e2af", + green = "#a6e3a1", + teal = "#94e2d5", + sky = "#89dceb", + sapphire = "#74c7ec", + blue = "#89b4fa", + lavender = "#b4befe", + text = "#cdd6f4", + subtext1 = "#bac2de", + subtext0 = "#a6adc8", + overlay2 = "#9399b2", + overlay1 = "#7f849c", + overlay0 = "#6c7086", + surface2 = "#585b70", + surface1 = "#45475a", + surface0 = "#313244", + base = "#1e1e2e", + mantle = "#181825", + crust = "#11111b", +} diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/types.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/types.lua new file mode 100644 index 00000000..8b98a293 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/types.lua @@ -0,0 +1,278 @@ +---@class Catppuccin +---@field options CatppuccinOptions +---@field setup fun(opts: CatppuccinOptions?) + +---@alias CtpFlavor "mocha" | "macchiato" | "frappe" | "latte" | "auto" +---@alias CtpColor "rosewater" | "flamingo" | "pink" | "mauve" | "red" | "maroon" | "peach" | "yellow" | "green" | "teal" | "sky" | "sapphire" | "blue" | "lavender" | "text" | "subtext1" | "subtext0" | "overlay2" | "overlay1" | "overlay0" | "surface2" | "surface1" | "surface0" | "base" | "mantle" | "crust" +---@class CtpFlavors: {all: T, mocha: T, macchiato: T, frappe: T, latte: T } +---@class CtpColors: {rosewater: T, flamingo: T, pink: T, mauve: T, red: T, maroon: T, peach: T, yellow: T, green: T, teal: T, sky: T, sapphire: T, blue: T, lavender: T, text: T, subtext1: T, subtext0: T, overlay2: T, overlay1: T, overlay0: T, surface2: T, surface1: T, surface0: T, base: T, mantle: T, crust: T, none: T } + +---@class CatppuccinOptions +-- Changes the flavor based on the background. See `:h background` for more info. +---@field background CtpBackground? +-- By default catppuccin writes the compiled results into the system's cache directory. +-- You can change the cache dir by changing this value. +---@field compile_path string? +-- Whether to enable transparency. +---@field transparent_background boolean? +-- Toggle the `~` characters after the end of buffers. +---@field show_end_of_buffer boolean? +-- If true, sets terminal colors (e.g. `g:terminal_color_0`). +---@field term_colors boolean? +-- Workaround for kitty transparency issue: https://github.com/kovidgoyal/kitty/issues/2917 +---@field kitty boolean? +-- Settings for dimming of inactive windows. +---@field dim_inactive CtpDimInactive? +-- Disables all italic styles. +---@field no_italic boolean? +-- Disables all bold styles. +---@field no_bold boolean? +-- Disables all underline styles. +---@field no_underline boolean? +-- Handles the style of general hl groups (see `:h highlight-groups`). +---@field styles CtpStyles? +-- Should default integrations be used. +---@field default_integrations boolean? +-- Toggle integrations. Integrations allow Catppuccin to set the theme of various plugins. +---@field integrations CtpIntegrations? +-- Catppuccin colors can be overwritten here. +---@field color_overrides CtpColors | CtpFlavors> | nil +-- Catppuccin highlights can be overwritten here. +---@field highlight_overrides CtpHighlightOverrides? +-- Global highlight overrides. +---@field custom_highlights CtpHighlightOverrideFn | {[string]: CtpHighlight} | nil +-- The default flavor to use on startup. +---@field flavour CtpFlavor? + +---@class CtpBackground +-- Catppuccin flavor to use when `:set background=dark` is set. +---@field dark CtpFlavor? +-- Catppuccin flavor to use when `:set background=light` is set. +---@field light CtpFlavor? + +---@class CtpDimInactive +-- Whether to dim inactive windows. +---@field enabled boolean +-- Whether to darken or lighten inactive windows. +---@field shade "dark" | "light" | nil +-- Percentage of the shade to apply to the inactive window +---@field percentage number? + +---@class CtpStyles +-- Change the style of comments. +---@field comments CtpHighlightArgs[]? +-- Change the style of conditionals. +---@field conditionals CtpHighlightArgs[]? +-- Change the style of loops. +---@field loops CtpHighlightArgs[]? +-- Change the style of functions. +---@field functions CtpHighlightArgs[]? +-- Change the style of keywords. +---@field keywords CtpHighlightArgs[]? +-- Change the style of strings. +---@field strings CtpHighlightArgs[]? +-- Change the style of variables. +---@field variables CtpHighlightArgs[]? +-- Change the style of numbers. +---@field numbers CtpHighlightArgs[]? +-- Change the style of booleans. +---@field booleans CtpHighlightArgs[]? +-- Change the style of properties. +---@field properties CtpHighlightArgs[]? +-- Change the style of types. +---@field types CtpHighlightArgs[]? +-- Change the style of operators. +---@field operators CtpHighlightArgs[]? +-- Change the style of miscs. +---@field miscs CtpHighlightArgs[]? + +---@class CtpNativeLspStyles +-- Change the style of LSP errors. +---@field errors CtpHighlightArgs[]? +-- Change the style of LSP hints. +---@field hints CtpHighlightArgs[]? +-- Change the style of LSP warnings. +---@field warnings CtpHighlightArgs[]? +-- Change the style of LSP information. +---@field information CtpHighlightArgs[]? + +---@class CtpNativeLspInlayHints +-- Toggle the background of inlay hints. +---@field background boolean? + +---@class CtpIntegrations +---@field aerial boolean? +---@field alpha boolean? +---@field barbar boolean? +-- Use this to set it up: +-- +-- ```lua +-- require("barbecue").setup { +-- -- valid options: +-- -- "catppuccin-latte" +-- -- "catppuccin-frappe" +-- -- "catppuccin-macchiato" +-- -- "catppuccin-mocha" +-- theme = "catppuccin", +-- } +---``` +---@field barbecue CtpIntegrationBarbecue | boolean? +---@field beacon boolean? +---@field colorful_winsep CtpIntegrationColorfulWinsep | boolean? +---@field cmp boolean? +-- `coc.nvim` links to `native_lsp` highlight groups, so you can use +-- `native_lsp.virtual_text` and `native_lsp.underlines` to style diagnostics. +---@field coc_nvim boolean? +-- ```lua +-- local sign = vim.fn.sign_define +-- +-- sign("DapBreakpoint", { text = "●", texthl = "DapBreakpoint", linehl = "", numhl = ""}) +-- sign("DapBreakpointCondition", { text = "●", texthl = "DapBreakpointCondition", linehl = "", numhl = ""}) +-- sign("DapLogPoint", { text = "◆", texthl = "DapLogPoint", linehl = "", numhl = ""}) +-- ``` +---@field dap boolean? +---@field dap_ui boolean? +---@field dashboard boolean? +---@field diffview boolean? +---@field dropbar CtpIntegrationDropbar | boolean? +---@field fern boolean? +-- Set `notification.window.winblend` to `0` in your `fidget` config: +-- +-- ```lua +-- require("fidget").setup { +-- notification = { +-- window = { winblend = 0 }, +-- } +-- } +-- ``` +---@field fidget boolean? +---@field flash boolean? +---@field gitgutter boolean? +---@field gitsigns boolean? +---@field harpoon boolean? +---@field headlines boolean? +---@field hop boolean? +---@field illuminate CtpIntegrationIlluminate | boolean? +---@field indent_blankline CtpIntegrationIndentBlankline | boolean? +---@field leap boolean? +---@field lightspeed boolean? +-- For custom Lsp kind icon and colors, adjust your `lspsaga` config: +-- +-- ```lua +-- require("lspsaga").setup { +-- ui = { +-- kind = require("catppuccin.groups.integrations.lsp_saga").custom_kind(), +-- }, +-- } +-- ``` +---@field lsp_saga boolean? +---@field lsp_trouble boolean? +---@field markdown boolean? +---@field mason boolean? +---@field native_lsp CtpIntegrationNativeLsp | boolean? +-- You **NEED** to enable highlight in your `nvim-navic` config or it won't work: +-- +-- ```lua +-- require("nvim-navic").setup { +-- highlight = true +-- } +-- ``` +---@field navic CtpIntegrationNavic | boolean? +---@field neogit boolean? +---@field neotest boolean? +---@field neotree boolean? +---@field noice boolean? +---@field notify boolean? +---@field nvimtree boolean? +---@field octo boolean? +---@field overseer boolean? +---@field pounce boolean? +---@field rainbow_delimiters boolean? +---@field sandwich boolean? +---@field semantic_tokens boolean? +---@field symbols_outline boolean? +---@field telekasten boolean? +---@field telescope CtpIntegrationTelescope | boolean? +---@field treesitter boolean? +---@field treesitter_context boolean? +---@field ts_rainbow boolean? +---@field ts_rainbow2 boolean? +---@field vim_sneak boolean? +---@field vimwiki boolean? +---@field which_key boolean? +---@field window_picker boolean? + +---@class CtpIntegrationBarbecue +-- Whether to use the alternative background. +---@field alt_background boolean? +-- Whether the basename should be bold. +---@field bold_basename boolean? +-- Whether the context should be dimmed. +---@field dim_context boolean? +-- Whether the directory name should be dimmed. +---@field dim_dirname boolean? + +---@class CtpIntegrationColorfulWinsep +-- Whether to enable the colorful-winsep integration. +---@field enabled boolean +-- Set to a Catppuccin color name to use for the split separator. +---@field color CtpColor? + +---@class CtpIntegrationDropbar +-- Whether to enable the dropbar integration. +---@field enabled boolean +-- Set to true to apply color to the text in dropbar, false to only apply it to the icons. +---@field color_mode boolean? + +---@class CtpIntegrationIndentBlankline +-- Whether to enable the integration. +---@field enabled boolean +-- Sets the color of the scope line +---@field scope_color CtpColor? +-- Enables char highlights per indent level. +-- Follow the instructions on the plugins GitHub repo to set it up. +---@field colored_indent_levels boolean? + +---@class CtpIntegrationMini +-- Whether to enable the integration. +---@field enabled boolean +-- Sets the color of the scope line +---@field indentscope_color CtpColor? + +---@class CtpIntegrationNativeLsp +-- Whether to enable the Native LSP integration. +---@field enabled boolean +-- Styles to apply to virtual text. +---@field virtual_text CtpNativeLspStyles? +-- Styles to apply to underlines. +---@field underlines CtpNativeLspStyles? +-- Inlay hints options. +---@field inlay_hints CtpNativeLspInlayHints? + +---@class CtpIntegrationNavic +-- Whether to enable the navic integration. +---@field enabled boolean +-- Override the background color for navic. +---@field custom_bg CtpColor | "NONE" | "lualine" | nil + +---@class CtpIntegrationTelescope +-- Whether to enable the telescope integration +---@field enabled boolean? +-- The style of Telescope +---@field style "classic" | "nvchad" | nil + +---@class CtpIntegrationIlluminate +-- Whether to enable the vim-illuminate integration +---@field enabled boolean? +-- Whether to standout IlluminatedWordWrite hl group +---@field lsp boolean? + +---@alias CtpHighlightArgs "bold" | "underline" | "undercurl" | "underdouble" | "underdotted" | "underdashed" | "strikethrough" | "reverse" | "inverse" | "italic" | "standout" | "altfont" | "nocombine" | "NONE" +---@alias CtpHighlightOverrideFn fun(colors: CtpColors): { [string]: CtpHighlight} +---@alias CtpHighlightOverrides CtpFlavors + +---@class CtpHighlight +---@field fg string? +---@field bg string? +---@field style CtpHighlightArgs[]? +---@field link string? diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/barbecue.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/barbecue.lua new file mode 100644 index 00000000..cfef00b9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/barbecue.lua @@ -0,0 +1,53 @@ +return function(flavour) + local C = require("catppuccin.palettes").get_palette(flavour) + local O = require("catppuccin").options + + local transparent_bg = O.transparent_background and C.none or C.mantle + + local dirname_color = O.integrations.barbecue.dim_dirname and C.overlay1 or C.text + local basename_bold = O.integrations.barbecue.bold_basename + local context_color = O.integrations.barbecue.dim_context and C.overlay1 or C.text + local background_color = O.integrations.barbecue.alt_background and transparent_bg or C.none + + local M = { + normal = { fg = C.text, bg = background_color }, + + ellipsis = { fg = C.overlay1 }, + separator = { fg = C.overlay1 }, + modified = { fg = C.peach }, + + dirname = { fg = dirname_color }, + basename = { fg = C.text, bold = basename_bold }, + context = { fg = context_color }, + + -- Same keys as navic + context_file = { fg = C.blue }, + context_module = { fg = C.blue }, + context_namespace = { fg = C.blue }, + context_package = { fg = C.blue }, + context_class = { fg = C.yellow }, + context_method = { fg = C.blue }, + context_property = { fg = C.green }, + context_field = { fg = C.green }, + context_constructor = { fg = C.blue }, + context_enum = { fg = C.green }, + context_interface = { fg = C.yellow }, + context_function = { fg = C.blue }, + context_variable = { fg = C.flamingo }, + context_constant = { fg = C.peach }, + context_string = { fg = C.green }, + context_number = { fg = C.peach }, + context_boolean = { fg = C.peach }, + context_array = { fg = C.blue }, + context_object = { fg = C.blue }, + context_key = { fg = C.flamingo }, + context_null = { fg = C.peach }, + context_enum_member = { fg = C.red }, + context_struct = { fg = C.blue }, + context_event = { fg = C.blue }, + context_operator = { fg = C.blue }, + context_type_parameter = { fg = C.blue }, + } + + return M +end diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/colors.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/colors.lua new file mode 100644 index 00000000..ddf0ff17 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/colors.lua @@ -0,0 +1,170 @@ +local M = {} + +local hsluv = require "catppuccin.lib.hsluv" + +M.bg = "#000000" +M.fg = "#ffffff" +M.day_brightness = 0.3 + +---@param hex_str string hexadecimal value of a color +local hex_to_rgb = function(hex_str) + local hex = "[abcdef0-9][abcdef0-9]" + local pat = "^#(" .. hex .. ")(" .. hex .. ")(" .. hex .. ")$" + hex_str = string.lower(hex_str) + + assert(string.find(hex_str, pat) ~= nil, "hex_to_rgb: invalid hex_str: " .. tostring(hex_str)) + + local red, green, blue = string.match(hex_str, pat) + return { tonumber(red, 16), tonumber(green, 16), tonumber(blue, 16) } +end + +---@param fg string forecrust color +---@param bg string background color +---@param alpha number number between 0 and 1. 0 results in bg, 1 results in fg +function M.blend(fg, bg, alpha) + bg = hex_to_rgb(bg) + fg = hex_to_rgb(fg) + + local blendChannel = function(i) + local ret = (alpha * fg[i] + ((1 - alpha) * bg[i])) + return math.floor(math.min(math.max(0, ret), 255) + 0.5) + end + + return string.format("#%02X%02X%02X", blendChannel(1), blendChannel(2), blendChannel(3)) +end + +function M.darken(hex, amount, bg) return M.blend(hex, bg or M.bg, math.abs(amount)) end + +function M.lighten(hex, amount, fg) return M.blend(hex, fg or M.fg, math.abs(amount)) end + +function M.brighten(color, percentage) + local hsl = hsluv.hex_to_hsluv(color) + local larpSpace = 100 - hsl[3] + if percentage < 0 then larpSpace = hsl[3] end + hsl[3] = hsl[3] + larpSpace * percentage + return hsluv.hsluv_to_hex(hsl) +end + +function M.invertColor(color) + if color ~= "NONE" then + local hsl = hsluv.hex_to_hsluv(color) + hsl[3] = 100 - hsl[3] + if hsl[3] < 40 then hsl[3] = hsl[3] + (100 - hsl[3]) * M.day_brightness end + return hsluv.hsluv_to_hex(hsl) + end + return color +end + +function M.string_to_color(colors, value, default) + if not value or value == "" then return default end + + -- If the value is a hex color code then return it + local hex = "[abcdef0-9][abcdef0-9]" + local pat = "^#" .. hex .. hex .. hex .. "$" + if string.match(value, pat) then return value end + + local acceptable_colors = { "black", "red", "green", "blue", "magenta", "cyan", "text", "orange", "pink" } + for _, ac in ipairs(acceptable_colors) do + if string.match(value, ac) then return colors[value] end + end + + -- Did not match anything to return default + return default +end + +function M.color_is_bright(r, g, b) + -- Counting the perceptive luminance - human eye favors green color + local luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 + if luminance > 0.5 then + return true -- Bright colors, black font + else + return false -- Dark colors, text font + end +end + +function M.hex2rgb(hex) + return tonumber("0x" .. hex:sub(1, 2)), tonumber("0x" .. hex:sub(3, 4)), tonumber("0x" .. hex:sub(5, 6)) +end + +function M.assert_brightness(color) + local hex = color:gsub("#", "") + local r = M.hex2rgb(string.sub(hex, 1, 2)) + local g = M.hex2rgb(string.sub(hex, 3, 4)) + local b = M.hex2rgb(string.sub(hex, 5, 6)) + + if M.color_is_bright(tonumber(r), tonumber(g), tonumber(b)) == true then + return true -- bright + end + + return false -- dull +end + +function M.vary_color(palettes, default) + local flvr = require("catppuccin").flavour + + if palettes[flvr] ~= nil then return palettes[flvr] end + return default +end + +local function rgb2Hex(rgb) + local hexadecimal = "#" + + for _, value in pairs(rgb) do + local hex = "" + + while value > 0 do + local index = math.fmod(value, 16) + 1 + value = math.floor(value / 16) + hex = string.sub(hsluv.hexChars, index, index) .. hex + end + + if string.len(hex) == 0 then + hex = "00" + elseif string.len(hex) == 1 then + hex = "0" .. hex + end + + hexadecimal = hexadecimal .. hex + end + + return hexadecimal +end + +function M.increase_saturation(hex, percentage) + local rgb = hex_to_rgb(hex) + + local saturation_float = percentage + + table.sort(rgb) + local rgb_intensity = { + min = rgb[1] / 255, + mid = rgb[2] / 255, + max = rgb[3] / 255, + } + + if rgb_intensity.max == rgb_intensity.min then + -- all colors have same intensity, which means + -- the original color is gray, so we can't change saturation. + return hex + end + + local new_intensities = {} + new_intensities.max = rgb_intensity.max + new_intensities.min = rgb_intensity.max * (1 - saturation_float) + + if rgb_intensity.mid == rgb_intensity.min then + new_intensities.mid = new_intensities.min + else + local intensity_proportion = (rgb_intensity.max - rgb_intensity.mid) / (rgb_intensity.mid - rgb_intensity.min) + new_intensities.mid = (intensity_proportion * new_intensities.min + rgb_intensity.max) + / (intensity_proportion + 1) + end + + for i, v in pairs(new_intensities) do + new_intensities[i] = math.floor(v * 255) + end + table.sort(new_intensities) + return (rgb2Hex { new_intensities.max, new_intensities.min, new_intensities.mid }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/lualine.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/lualine.lua new file mode 100644 index 00000000..8f0b1f0b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/lualine.lua @@ -0,0 +1,46 @@ +return function(flavour) + local C = require("catppuccin.palettes").get_palette(flavour) + local O = require("catppuccin").options + local catppuccin = {} + + local transparent_bg = O.transparent_background and "NONE" or C.mantle + + catppuccin.normal = { + a = { bg = C.blue, fg = C.mantle, gui = "bold" }, + b = { bg = C.surface0, fg = C.blue }, + c = { bg = transparent_bg, fg = C.text }, + } + + catppuccin.insert = { + a = { bg = C.green, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.green }, + } + + catppuccin.terminal = { + a = { bg = C.green, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.green }, + } + + catppuccin.command = { + a = { bg = C.peach, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.peach }, + } + + catppuccin.visual = { + a = { bg = C.mauve, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.mauve }, + } + + catppuccin.replace = { + a = { bg = C.red, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.red }, + } + + catppuccin.inactive = { + a = { bg = transparent_bg, fg = C.blue }, + b = { bg = transparent_bg, fg = C.surface1, gui = "bold" }, + c = { bg = transparent_bg, fg = C.overlay0 }, + } + + return catppuccin +end diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/reactive.lua b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/reactive.lua new file mode 100644 index 00000000..ca8bfc26 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/catppuccin/utils/reactive.lua @@ -0,0 +1,135 @@ +local M = {} + +function M.cursor(name, palette) + return { + name = name, + init = function() vim.opt.guicursor:append "a:ReactiveCursor" end, + modes = { + i = { + hl = { + ReactiveCursor = { bg = palette.teal }, + }, + }, + no = { + operators = { + d = { + hl = { + ReactiveCursor = { bg = palette.red }, + }, + }, + y = { + hl = { + ReactiveCursor = { bg = palette.peach }, + }, + }, + c = { + hl = { + ReactiveCursor = { bg = palette.blue }, + }, + }, + }, + }, + R = { + hl = { + ReactiveCursor = { bg = palette.sapphire }, + }, + }, + -- visual + [{ "v", "V", "\x16" }] = { + hl = { + ReactiveCursor = { bg = palette.mauve }, + }, + }, + -- select + [{ "s", "S", "\x13" }] = { + hl = { + ReactiveCursor = { bg = palette.pink }, + }, + }, + }, + } +end + +function M.cursorline(name, palette) + local darken = require("catppuccin.utils.colors").darken + + return { + name = name, + static = { + winhl = { + inactive = { + CursorLine = { bg = darken(palette.surface0, 0.75) }, + CursorLineNr = { bg = darken(palette.surface0, 0.75) }, + }, + }, + }, + modes = { + -- insert mode + i = { + winhl = { + CursorLine = { bg = darken(palette.sky, 0.3) }, + CursorLineNr = { bg = darken(palette.sky, 0.3) }, + }, + }, + -- normal mode + n = { + winhl = { + CursorLine = { bg = palette.surface0 }, + CursorLineNr = { bg = palette.surface0 }, + }, + }, + -- operator-pending mode + no = { + operators = { + [{ "gu", "gU", "g~", "~" }] = { + winhl = { + CursorLine = { bg = palette.surface2 }, + CursorLineNr = { bg = palette.surface2 }, + }, + }, + -- delete operator + d = { + winhl = { + CursorLine = { bg = darken(palette.red, 0.4) }, + CursorLineNr = { bg = darken(palette.red, 0.4) }, + }, + }, + -- yank operator + y = { + winhl = { + CursorLine = { bg = darken(palette.peach, 0.3) }, + CursorLineNr = { bg = darken(palette.peach, 0.3) }, + }, + }, + -- change operator + c = { + winhl = { + CursorLine = { bg = darken(palette.blue, 0.4) }, + CursorLineNr = { bg = darken(palette.blue, 0.4) }, + }, + }, + }, + }, + R = { + winhl = { + CursorLine = { bg = darken(palette.sapphire, 0.4) }, + CursorLineNr = { bg = darken(palette.sapphire, 0.4) }, + }, + }, + -- visual mode + [{ "v", "V", "\x16" }] = { + winhl = { + Visual = { bg = darken(palette.mauve, 0.4) }, + }, + }, + -- select mode + [{ "s", "S", "\x13" }] = { + winhl = { + Visual = { bg = darken(palette.pink, 0.4) }, + }, + }, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/astro/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/astro/rainbow-delimiters.scm new file mode 100644 index 00000000..9ed95ce1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/astro/rainbow-delimiters.scm @@ -0,0 +1,13 @@ +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(interpolation + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/bash/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/bash/rainbow-delimiters.scm new file mode 100644 index 00000000..b9473ccb --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/bash/rainbow-delimiters.scm @@ -0,0 +1,37 @@ +(command_substitution + "$(" @delimiter + ")" @delimiter @sentinel) @container + +(expansion + "${" @delimiter + (":-" @delimiter)? + "}" @delimiter @sentinel) @container + +;;; The double-bracket variant is a bashism +(test_command + ["[[" "["] @delimiter + ["]]" "]"] @delimiter @sentinel) @container + +(subshell + "(" @delimiter + ")" @delimiter @sentinel) @container + +(array + "(" @delimiter + ")" @delimiter @sentinel) @container + +(function_definition + "(" @delimiter + ")" @delimiter @sentinel) @container + +(arithmetic_expansion + "$((" @delimiter + "))" @delimiter @sentinel) @container + +(compound_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(subscript + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/c_sharp/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/c_sharp/rainbow-delimiters.scm new file mode 100644 index 00000000..87e2175b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/c_sharp/rainbow-delimiters.scm @@ -0,0 +1,143 @@ +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameter_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(if_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(foreach_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(while_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(do_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute_argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(switch_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(switch_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(switch_expression + "{" @delimiter + "}" @delimiter @sentinel) @container + +(default_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(catch_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(catch_filter_clause + "(" @delimiter + ")" @delimiter @sentinel) @container + +(using_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(lock_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(typeof_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(sizeof_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(checked_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(accessor_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(anonymous_object_creation_expression + "{" @delimiter + "}" @delimiter @sentinel) @container + +(enum_member_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_parameter_list + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_argument_list + "<" @delimiter + ">" @delimiter @sentinel) @container + +(initializer_expression + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_rank_specifier + "[" @delimiter + "]" @delimiter @sentinel) @container + +(bracketed_argument_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(implicit_array_creation_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(implicit_stackalloc_expression + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-frappe.lua b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-frappe.lua new file mode 100644 index 00000000..aafd00c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-frappe.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine" "frappe" diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-latte.lua b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-latte.lua new file mode 100644 index 00000000..2b11e17f --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-latte.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine" "latte" diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-macchiato.lua b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-macchiato.lua new file mode 100644 index 00000000..c8102ca3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-macchiato.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine" "macchiato" diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-mocha.lua b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-mocha.lua new file mode 100644 index 00000000..d5b190cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin-mocha.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine" "mocha" diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin.lua b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin.lua new file mode 100644 index 00000000..4e18b44c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/catppuccin.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine"() diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/clojure/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/clojure/rainbow-delimiters.scm new file mode 100644 index 00000000..a4b6a953 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/clojure/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(list_lit + "(" @delimiter + ")" @delimiter @sentinel) @container + +(vec_lit + "[" @delimiter + "]" @delimiter @sentinel) @container + +(map_lit + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set_lit + "{" @delimiter + "}" @delimiter @sentinel) @container + +(anon_fn_lit + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/commonlisp/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/commonlisp/rainbow-delimiters.scm new file mode 100644 index 00000000..3b216d0d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/commonlisp/rainbow-delimiters.scm @@ -0,0 +1,13 @@ +(list_lit + "(" @delimiter + _* + ")" @delimiter @sentinel) @container + +(defun + "(" @delimiter + _* + ")" @delimiter @sentinel) @container + +(loop_macro + "(" @delimiter + ")" @delimiter @sentinel ) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cpp/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cpp/rainbow-delimiters.scm new file mode 100644 index 00000000..81648921 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cpp/rainbow-delimiters.scm @@ -0,0 +1,61 @@ +;;; NOTE: The C and C++ grammar have diverged, so I cannot include the C query. + +(parameter_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(condition_clause + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(compound_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast_expression + "(" @delimiter + ")" @delimiter @sentinel) + +(initializer_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_declarator + "[" @delimiter + "]" @delimiter @sentinel) @container + +(subscript_argument_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(template_parameter_list + "<" @delimiter + ">" @delimiter @sentinel) @container + +(initializer_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(template_argument_list + "<" @delimiter + ">" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/css/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/css/rainbow-delimiters.scm new file mode 100644 index 00000000..cef27cbe --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/css/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parenthesized_query + "(" @delimiter + ")" @delimiter @sentinel) @container + +(feature_query + "(" @delimiter + ")" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute_selector + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cuda/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cuda/rainbow-delimiters.scm new file mode 100644 index 00000000..b910498c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cuda/rainbow-delimiters.scm @@ -0,0 +1,5 @@ +; inherits: cpp + +(kernel_call_syntax + "<<<" @delimiter + ">>>" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cue/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cue/rainbow-delimiters.scm new file mode 100644 index 00000000..17fd570e --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/cue/rainbow-delimiters.scm @@ -0,0 +1,31 @@ +(struct_lit + "{" @delimiter + "}" @delimiter @sentinel) @container + +(list_lit + "[" @delimiter + "]" @delimiter @sentinel) @container + +(label + "[" @delimiter + "]" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(import_spec_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(interpolation + "\\(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/elixir/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/elixir/rainbow-delimiters.scm new file mode 100644 index 00000000..8e84fc9c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/elixir/rainbow-delimiters.scm @@ -0,0 +1,34 @@ +(call + (arguments + "(" @delimiter + ")" @delimiter @sentinel) @container) + +(block + "(" @delimiter + ")" @delimiter @sentinel) @container + +(string + (interpolation + "#{" @delimiter + "}" @delimiter @sentinel) @container) + +(tuple + "{" @delimiter + "}" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(access_call + "[" @delimiter + "]" @delimiter @sentinel) @container + +(bitstring + "<<" @delimiter + ">>" @delimiter @sentinel) @container + +(map + "%" @delimiter + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/fish/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/fish/rainbow-delimiters.scm new file mode 100644 index 00000000..c1b238ce --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/fish/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(command_substitution + "(" @delimiter + ")" @delimiter @sentinel) @container + +(concatenation + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_element_access + "[" @delimiter + "]" @delimiter @sentinel) @container + +(brace_expansion + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/go/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/go/rainbow-delimiters.scm new file mode 100644 index 00000000..9a4a575a --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/go/rainbow-delimiters.scm @@ -0,0 +1,79 @@ +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(import_spec_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(var_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(const_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(type_assertion_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(field_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameter_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(expression_switch_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_switch_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(literal_value + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(slice_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(map_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(interface_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_parameter_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_arguments + "[" @delimiter + "]" @delimiter @sentinel) @container + +(index_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(slice_expression + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/hcl/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/hcl/rainbow-delimiters.scm new file mode 100644 index 00000000..7693e4f6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/hcl/rainbow-delimiters.scm @@ -0,0 +1,47 @@ +(tuple + (tuple_start "[") @delimiter + (tuple_end "]") @delimiter @sentinel) @container + +(for_tuple_expr + (tuple_start "[") @delimiter + (tuple_end "]") @delimiter @sentinel) @container + +(new_index + "[" @delimiter + "]" @delimiter @sentinel) @container + +(function_call + "(" @delimiter + ")" @delimiter @sentinel) @container + +(expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(binary_operation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(unary_operation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + (block_start "{") @delimiter + (block_end "}") @delimiter @sentinel) @container + +(object + (object_start "{") @delimiter + (object_end "}") @delimiter @sentinel) @container + +(for_object_expr + (object_start "{") @delimiter + (object_end "}") @delimiter @sentinel) @container + +(template_interpolation + (template_interpolation_start) @delimiter + (template_interpolation_end) @delimiter @sentinel) @container + +(_ + (template_directive_start) @delimiter + (template_directive_end) @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/html/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/html/rainbow-delimiters.scm new file mode 100644 index 00000000..24e63db6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/html/rainbow-delimiters.scm @@ -0,0 +1,69 @@ +;;; A pair of delimiter tags with any content in-between. +;;; Last tag should be a sentinel. + +;;; If instead you want rainbow-delimiters to only highlight +;;; the tag names without any of "<", "" or "/>", then +;;; you can make your own query file, e.g., +;;; 'rainbow-tag-names' +;;; and use the following with +;;; x @delimiter +;;; deleted for x equal to any of "<", "" or "/>". + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (tag_name) @delimiter + "/>" @delimiter @sentinel)) @container + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter @_tag_name + ">" @delimiter @sentinel) + (#any-of? @_tag_name + "area" + "base" + "br" + "col" + "embed" + "hr" + "img" + "input" + "link" + "meta" + "param" + "source" + "track" + "wbr") +) @container + +(style_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (element (self_closing_tag) @delimiter)* + (end_tag + "" @delimiter @sentinel)) @container + +(script_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/janet_simple/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/janet_simple/rainbow-delimiters.scm new file mode 100644 index 00000000..02947931 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/janet_simple/rainbow-delimiters.scm @@ -0,0 +1,23 @@ +(par_tup_lit + "(" @delimiter + ")" @delimiter @sentinel) @container + +(par_arr_lit + "@(" @delimiter + ")" @delimiter @sentinel) @container + +(sqr_tup_lit + "[" @delimiter + "]" @delimiter @sentinel) @container + +(sqr_arr_lit + "@[" @delimiter + "]" @delimiter @sentinel) @container + +(struct_lit + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tbl_lit + "@{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/java/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/java/rainbow-delimiters.scm new file mode 100644 index 00000000..bca00206 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/java/rainbow-delimiters.scm @@ -0,0 +1,70 @@ +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_initializer + "{" @delimiter + "}" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(resource_specification + "(" @delimiter + ")" @delimiter @sentinel) @container + +(catch_clause + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(inferred_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(annotation_argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(enhanced_for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(constructor_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +;; Treat it as a single delimiter because it will always have the same color +(dimensions) @container @delimiter @sentinel + +(dimensions_expr + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_access + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-delimiters-react.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-delimiters-react.scm new file mode 100644 index 00000000..229898a2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-delimiters-react.scm @@ -0,0 +1,102 @@ +;;; This query includes React support as well. + +;; String interpolation inside template strings +(template_substitution + "${" @delimiter + "}" @delimiter @sentinel) @container + +(object + "{" @delimiter + "}" @delimiter @sentinel) @container + +(statement_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(switch_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_in_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(named_imports + "{" @delimiter + "}" @delimiter @sentinel) @container + +(export_clause + "{" @delimiter + "}" @delimiter @sentinel) @container + +(object_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + + +;;; React.js support +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (identifier) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (member_expression) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_self_closing_element + "<" @delimiter + name: (identifier) @delimiter + "/>" @delimiter @sentinel) @container + +(jsx_self_closing_element + "<" @delimiter + name: (member_expression) @delimiter + "/>" @delimiter @sentinel) @container + +(jsx_expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-delimiters.scm new file mode 100644 index 00000000..766a017b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-delimiters.scm @@ -0,0 +1,68 @@ +;;; This query only covers Javascript without any React support. It exists so +;;; that Typescript query can inherit it. + +;; String interpolation inside template strings +(template_substitution + "${" @delimiter + "}" @delimiter @sentinel) @container + +(object + "{" @delimiter + "}" @delimiter @sentinel) @container + +(statement_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(switch_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_in_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(named_imports + "{" @delimiter + "}" @delimiter @sentinel) @container + +(export_clause + "{" @delimiter + "}" @delimiter @sentinel) @container + +(object_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-parens.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-parens.scm new file mode 100644 index 00000000..766a017b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-parens.scm @@ -0,0 +1,68 @@ +;;; This query only covers Javascript without any React support. It exists so +;;; that Typescript query can inherit it. + +;; String interpolation inside template strings +(template_substitution + "${" @delimiter + "}" @delimiter @sentinel) @container + +(object + "{" @delimiter + "}" @delimiter @sentinel) @container + +(statement_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(switch_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_in_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(named_imports + "{" @delimiter + "}" @delimiter @sentinel) @container + +(export_clause + "{" @delimiter + "}" @delimiter @sentinel) @container + +(object_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-tags-react.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-tags-react.scm new file mode 100644 index 00000000..a9c2fe81 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/javascript/rainbow-tags-react.scm @@ -0,0 +1,29 @@ +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (identifier) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (member_expression) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_self_closing_element + "<" @delimiter + name: (identifier) @delimiter + "/>" @delimiter @sentinel) @container + +(jsx_self_closing_element + "<" @delimiter + name: (member_expression) @delimiter + "/>" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/json/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/json/rainbow-delimiters.scm new file mode 100644 index 00000000..8ff0d788 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/json/rainbow-delimiters.scm @@ -0,0 +1,7 @@ +(object + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/json5/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/json5/rainbow-delimiters.scm new file mode 100644 index 00000000..41269219 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/json5/rainbow-delimiters.scm @@ -0,0 +1 @@ +; inherits: json diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/jsonc/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/jsonc/rainbow-delimiters.scm new file mode 100644 index 00000000..41269219 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/jsonc/rainbow-delimiters.scm @@ -0,0 +1 @@ +; inherits: json diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/julia/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/julia/rainbow-delimiters.scm new file mode 100644 index 00000000..e45fec43 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/julia/rainbow-delimiters.scm @@ -0,0 +1,27 @@ +(vector_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(matrix_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(comprehension_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(tuple_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(curly_expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/kotlin/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/kotlin/rainbow-delimiters.scm new file mode 100644 index 00000000..7001e259 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/kotlin/rainbow-delimiters.scm @@ -0,0 +1,55 @@ +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(function_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(control_structure_body + "{" @delimiter + "}" @delimiter @sentinel) @container + + +(lambda_literal + "{" @delimiter + "}" @delimiter @sentinel) @container + +(primary_constructor + "(" @delimiter + ")" @delimiter @sentinel) @container + +(function_value_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(value_arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(multi_variable_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(when_expression + (when_subject + "(" @delimiter + ")" @delimiter)? + "{" @delimiter + "}" @delimiter @sentinel) @container + +(indexing_suffix + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_parameters + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/latex/rainbow-blocks.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/latex/rainbow-blocks.scm new file mode 100644 index 00000000..ba71cd16 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/latex/rainbow-blocks.scm @@ -0,0 +1,11 @@ +(inline_formula + "$" @delimiter + "$" @delimiter @sentinel) @container + +(generic_environment + (begin) @delimiter + (end) @delimiter @sentinel) @container + +(math_environment + (begin) @delimiter + (end) @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/latex/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/latex/rainbow-delimiters.scm new file mode 100644 index 00000000..6189163e --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/latex/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(curly_group + "{" @delimiter + "}" @delimiter @sentinel) @container + +(curly_group_text + "{" @delimiter + "}" @delimiter @sentinel) @container + +(curly_group_text_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(inline_formula + "$" @delimiter + "$" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/lua/rainbow-blocks.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/lua/rainbow-blocks.scm new file mode 100644 index 00000000..5ac26c5c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/lua/rainbow-blocks.scm @@ -0,0 +1,69 @@ +;;; Note: Some patterns are commented out because currently Neovim can capture +;;; only one node at a time. Once it becomes possible to capture multiple +;;; nodes they should be commented back in. + + +(function_declaration + "function" @delimiter + "end" @delimiter @sentinel) @container + +(function_definition + "function" @delimiter + "end" @delimiter @sentinel) @container + +(if_statement + "if" @delimiter + "then" @delimiter + (elseif_statement + "elseif" @delimiter + "then" @delimiter)* + (else_statement + "else" @delimiter)? + "end" @delimiter @sentinel) @container + +(while_statement + "while" @delimiter + "do" @delimiter + "end" @delimiter @sentinel) @container + +(repeat_statement + "repeat" @delimiter + "until" @delimiter @sentinel) @container + +(for_statement + "for" @delimiter + (for_generic_clause + "in" @delimiter)? + "do" @delimiter + "end" @delimiter @sentinel) @container + +(do_statement + "do" @delimiter + "end" @delimiter @sentinel) @container + + +;;; Copied over from rainbow-parens + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(table_constructor + "{" @delimiter + "}" @delimiter @sentinel) @container + +(bracket_index_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/lua/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/lua/rainbow-delimiters.scm new file mode 100644 index 00000000..bb2a046d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/lua/rainbow-delimiters.scm @@ -0,0 +1,23 @@ +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(table_constructor + "{" @delimiter + "}" @delimiter @sentinel) @container + +(bracket_index_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/luadoc/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/luadoc/rainbow-delimiters.scm new file mode 100644 index 00000000..e7cdb20f --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/luadoc/rainbow-delimiters.scm @@ -0,0 +1,40 @@ +(function_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +;;; I wanted to use something like +; (union_type +; "|" @delimiter @sentinel +; ) @container +;;; too, but it doesn't fully work with the current parser + +(array_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(table_type + "<" @delimiter + ">" @delimiter @sentinel) @container + +(table_literal_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(indexed_field + "[" @delimiter + "]" @delimiter @sentinel) @container + +(tuple_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(_ + "[" @delimiter + . + field: (_) + . + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/make/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/make/rainbow-delimiters.scm new file mode 100644 index 00000000..4378129b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/make/rainbow-delimiters.scm @@ -0,0 +1 @@ +;;; Nothing I can think of, everything gets taken from the injected bash query. diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/markdown/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/markdown/rainbow-delimiters.scm new file mode 100644 index 00000000..701e470b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/markdown/rainbow-delimiters.scm @@ -0,0 +1,3 @@ +;;; Intentionally empty. Markdown can have other languages embedded and we +;;; want to let those grammars handle the highlighting. This query only exists +;;; to satisfy the requirements of this plugin. diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/nim/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/nim/rainbow-delimiters.scm new file mode 100644 index 00000000..e98aba49 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/nim/rainbow-delimiters.scm @@ -0,0 +1,47 @@ +(array_construction + "[" @delimiter + "]" @delimiter @sentinel) @container +(tuple_construction + "(" @delimiter + ")" @delimiter @sentinel) @container +(tuple_deconstruct_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container +(curly_construction + "{" @delimiter + ":"? @delimiter + "}" @delimiter @sentinel) @container + +(parenthesized + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container +(parameter_declaration_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(bracket_expression + "[" @delimiter + "]" @delimiter @sentinel) @container +(field_declaration_list + "[" @delimiter + "]" @delimiter @sentinel) @container +(generic_parameter_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(cast + "[" @delimiter + "]" @delimiter + "(" @delimiter + ")" @delimiter @sentinel) @container + +(term_rewriting_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container +(curly_expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/nix/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/nix/rainbow-delimiters.scm new file mode 100644 index 00000000..cb347941 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/nix/rainbow-delimiters.scm @@ -0,0 +1,27 @@ +(attrset_expression + ("{" @delimiter) + ("}" @delimiter @sentinel)) @container + +(rec_attrset_expression + ("{" @delimiter) + ("}" @delimiter @sentinel)) @container + +(formals + ("{" @delimiter) + ("}" @delimiter @sentinel)) @container + +(list_expression + ("[" @delimiter) + ("]" @delimiter @sentinel)) @container + +(parenthesized_expression + ("(" @delimiter) + (")" @delimiter @sentinel)) @container + +(interpolation + ("${" @delimiter) + ("}" @delimiter @sentinel)) @container + +(inherit_from + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/perl/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/perl/rainbow-delimiters.scm new file mode 100644 index 00000000..eb532196 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/perl/rainbow-delimiters.scm @@ -0,0 +1,86 @@ +(argument + "(" @delimiter + ")" @delimiter @sentinel) @container + +(array + "(" @delimiter + ")" @delimiter @sentinel) @container + +(array_dereference + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_ref + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_ref + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(hash_access_variable + "->{" @delimiter + "}" @delimiter @sentinel) @container + +(hash_access_variable + "{" @delimiter + "}" @delimiter @sentinel) @container + +(hash_ref + "+" @delimiter + "{" @delimiter + "}" @delimiter @sentinel) @container + +(multi_var_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(standalone_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parenthesized_argument + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(word_list_qw + (start_delimiter_qw) @delimiter + (end_delimiter_qw) @delimiter @sentinel) @container + +(regex_pattern_qr + (start_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(command_qx_quoted + (start_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(string_qq_quoted + (start_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(patter_matcher_m + (start_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(substitution_pattern_s + (start_delimiter) @delimiter + (separator_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(transliteration_tr_or_y + (start_delimiter) @delimiter + (separator_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/python/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/python/rainbow-delimiters.scm new file mode 100644 index 00000000..dca135fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/python/rainbow-delimiters.scm @@ -0,0 +1,75 @@ +;; NOTE: When updating this file update the Starlark test file as well if +;; applicable. + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_comprehension + "[" @delimiter + "]" @delimiter @sentinel) @container + +(dictionary + "{" @delimiter + "}" @delimiter @sentinel) @container + +(dict_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(dictionary_comprehension + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set_comprehension + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tuple + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(generator_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_parameter + "[" @delimiter + "]" @delimiter @sentinel) @container + +(import_from_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(string + (interpolation + "{" @delimiter + "}" @delimiter @sentinel) @container) diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/query/rainbow-blocks.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/query/rainbow-blocks.scm new file mode 100644 index 00000000..9c249729 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/query/rainbow-blocks.scm @@ -0,0 +1,28 @@ +;; Note: These queries are very useful when looking at a large +;; tree of queries like in `InspectTree` +(named_node + "(" @delimiter + (identifier) @delimiter + ")" @delimiter @sentinel) @container + +(grouping + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(predicate + "(" @delimiter + ")" @delimiter @sentinel) @container + +(field_definition + (identifier) @delimiter @sentinel) @container + +;; For more highlighting the following can be added too: +; (parameters +; (identifier) @delimiter @sentinel) @container +; +; (negated_field +; (identifier) @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/query/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/query/rainbow-delimiters.scm new file mode 100644 index 00000000..fff8c317 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/query/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(named_node + "(" @delimiter + ")" @delimiter @sentinel) @container + +(grouping + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(predicate + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/r/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/r/rainbow-delimiters.scm new file mode 100644 index 00000000..f76b3ac3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/r/rainbow-delimiters.scm @@ -0,0 +1,36 @@ +(call + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subset + "[" @delimiter + "]" @delimiter @sentinel) @container + +(subset2 + "[[" @delimiter + "]]" @delimiter @sentinel) @container + +(if + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for + "(" @delimiter + ")" @delimiter @sentinel) @container + +(while + "(" @delimiter + ")" @delimiter @sentinel) @container + +(switch + "(" @delimiter + ")" @delimiter @sentinel) @container + +(function_definition + (formal_parameters + "(" @delimiter + ")" @delimiter @sentinel)) @container + +(brace_list + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rasi/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rasi/rainbow-delimiters.scm new file mode 100644 index 00000000..2295469c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rasi/rainbow-delimiters.scm @@ -0,0 +1,52 @@ +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(environ_value + "$" @delimiter + "{" @delimiter + "}" @delimiter @sentinel) @container + +(environ_value + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_value + "[" @delimiter + "]" @delimiter @sentinel) @container + +(distance_calc + "(" @delimiter + ")" @delimiter @sentinel) @container + +(feature_query + "(" @delimiter + ")" @delimiter @sentinel) @container + +(reference_value + "(" @delimiter + ")" @delimiter @sentinel) @container + +(rgb_color + "(" @delimiter + ")" @delimiter @sentinel) @container + +(hsl_color + "(" @delimiter + ")" @delimiter @sentinel) @container + +(hwb_color + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cmyk_color + "(" @delimiter + ")" @delimiter @sentinel) @container + +(url_image + "(" @delimiter + ")" @delimiter @sentinel) @container + +(gradient_image + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/regex/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/regex/rainbow-delimiters.scm new file mode 100644 index 00000000..9d117932 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/regex/rainbow-delimiters.scm @@ -0,0 +1,17 @@ +(anonymous_capturing_group + "(" @delimiter + ")" @delimiter @sentinel) @container + +;;; The inversion `^` should be an opening node as well +(character_class + "[" @delimiter + "]" @delimiter @sentinel) @container + +(count_quantifier + "{" @delimiter + "}" @delimiter @sentinel) @container + +;;; We should probably include the character after `?` like `=` as well +(lookaround_assertion + "(?" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rst/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rst/rainbow-delimiters.scm new file mode 100644 index 00000000..5da7b9e5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rst/rainbow-delimiters.scm @@ -0,0 +1,3 @@ +;;; Intentionally empty. ReStructuredText can have other languages embedded +;;; and we want to let those grammars handle the highlighting. This query only +;;; exists to satisfy the requirements of this plugin. diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/ruby/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/ruby/rainbow-delimiters.scm new file mode 100644 index 00000000..48b1a664 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/ruby/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(hash + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(parenthesized_statements + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block_parameters + "|" @delimiter + "|" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rust/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rust/rainbow-delimiters.scm new file mode 100644 index 00000000..b42420c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/rust/rainbow-delimiters.scm @@ -0,0 +1,133 @@ +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(field_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(ordered_field_declaration_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(enum_variant_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(use_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(field_initializer_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(match_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tuple_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(token_tree + "{" @delimiter + "}" @delimiter @sentinel) @container + +(token_tree + "(" @delimiter + ")" @delimiter @sentinel) @container + +(token_tree + "[" @delimiter + "]" @delimiter @sentinel) @container + +(token_tree_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(token_repetition_pattern + "$" @delimiter + "(" @delimiter + ")" @delimiter @sentinel) @container + +(token_repetition + "$" @delimiter + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute_item + "[" @delimiter + "]" @delimiter @sentinel) @container + +(inner_attribute_item + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_parameters + "<" @delimiter + ">" @delimiter @sentinel) @container + +(closure_parameters + "|" @delimiter + "|" @delimiter @sentinel) @container + +(array_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(index_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(tuple_struct_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(struct_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(slice_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + +(macro_definition + "{" @delimiter + "}" @delimiter @sentinel) @container + +(visibility_modifier + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/scheme/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/scheme/rainbow-delimiters.scm new file mode 100644 index 00000000..41bbada2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/scheme/rainbow-delimiters.scm @@ -0,0 +1,7 @@ +(list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/scss/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/scss/rainbow-delimiters.scm new file mode 100644 index 00000000..af30cd70 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/scss/rainbow-delimiters.scm @@ -0,0 +1,5 @@ +; inherits: css + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/sql/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/sql/rainbow-delimiters.scm new file mode 100644 index 00000000..e6c8e03e --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/sql/rainbow-delimiters.scm @@ -0,0 +1,28 @@ +(subquery + "(" @delimiter + ")" @delimiter @sentinel) @container + +(invocation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(where + "(" @delimiter + ")" @delimiter @sentinel) @container + +(binary_expression + "(" @delimiter + ")" @delimiter @sentinel + ) @container + +; The following can cause problems with (((()))) +(term + "(" @delimiter + ; ("(" ")")* ; to fix _some_ problems, this can be uncommented + ")" @delimiter @sentinel + ) @container + diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/starlark/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/starlark/rainbow-delimiters.scm new file mode 100644 index 00000000..2f28ba57 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/starlark/rainbow-delimiters.scm @@ -0,0 +1,56 @@ +;; This is mostly identical to Python, without `generator_expression`. +;; NOTE: if you update queries for Python, please consider adding the changes +;; to this file as well, given that the tree-sitter's node types exist. See +;; https://github.com/amaanq/tree-sitter-starlark/blob/master/src/node-types.json + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_comprehension + "[" @delimiter + "]" @delimiter @sentinel) @container + +(dictionary + "{" @delimiter + "}" @delimiter @sentinel) @container + +(dictionary_comprehension + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set_comprehension + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tuple + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_parameter + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/svelte/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/svelte/rainbow-delimiters.scm new file mode 100644 index 00000000..0d483a5c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/svelte/rainbow-delimiters.scm @@ -0,0 +1,35 @@ +(script_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(style_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (tag_name) @delimiter + "/>" @delimiter @sentinel)) @container + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/svelte/rainbow-delimiters.txt b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/svelte/rainbow-delimiters.txt new file mode 100644 index 00000000..38e926b1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/svelte/rainbow-delimiters.txt @@ -0,0 +1,53 @@ +(script_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(style_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (tag_name) @delimiter + "/>" @delimiter @sentinel)) @container + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(const_expr + "{" @delimiter + "@" @delimiter + (special_block_keyword) @delimiter + "}" @delimiter @sentinel) @container + +(each_statement + (each_start_expr + "{" @delimiter + "#" @delimiter + (special_block_keyword) @delimiter + "}" @delimiter) @container + (each_end_expr + "{" @delimiter + "/" @delimiter + (special_block_keyword) @delimiter + "}" @delimiter @sentinel)) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/teal/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/teal/rainbow-delimiters.scm new file mode 100644 index 00000000..a8a0967d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/teal/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(table_constructor + "{" @delimiter + "}" @delimiter @sentinel) @container + +(table_entry + "[" @delimiter + "]" @delimiter @sentinel) @container + +(index + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/templ/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/templ/rainbow-delimiters.scm new file mode 100644 index 00000000..a28443e3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/templ/rainbow-delimiters.scm @@ -0,0 +1,61 @@ +; inherits: go + +;; HTML elements + +(element + (tag_start + "<" @delimiter + (element_identifier) @delimiter + ">" @delimiter) + (tag_end + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (element_identifier) @delimiter + "/>" @delimiter @sentinel)) @container + +(style_element + ("<" @delimiter + "style" @delimiter + ">" @delimiter) + ("" @delimiter @sentinel)) @container + +(script_element + ("<" @delimiter + "script" @delimiter + ">" @delimiter) + ("" @delimiter @sentinel)) @container + +;; Brackets + +(component_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(script_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(css_declaration + "{" @delimiter + "}" @delimiter @sentinel) @container + +(component_switch_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(component_children_expression + "{" @delimiter + "}" @delimiter @sentinel) @container + +(expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/terraform/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/terraform/rainbow-delimiters.scm new file mode 100644 index 00000000..0e5ffc2d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/terraform/rainbow-delimiters.scm @@ -0,0 +1 @@ +; inherits: hcl diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/toml/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/toml/rainbow-delimiters.scm new file mode 100644 index 00000000..46170e6c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/toml/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(table + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(inline_table + "{" @delimiter + "}" @delimiter @sentinel) @container + +(table_array_element + "[[" @delimiter + "]]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/typescript/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/typescript/rainbow-delimiters.scm new file mode 100644 index 00000000..9818cb7e --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/typescript/rainbow-delimiters.scm @@ -0,0 +1,25 @@ +; inherits: javascript + +(interface_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(enum_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_parameters + "<" @delimiter + ">" @delimiter @sentinel) @container + +(lookup_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(object_type + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/typescript/rainbow-parens.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/typescript/rainbow-parens.scm new file mode 100644 index 00000000..9818cb7e --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/typescript/rainbow-parens.scm @@ -0,0 +1,25 @@ +; inherits: javascript + +(interface_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(enum_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_parameters + "<" @delimiter + ">" @delimiter @sentinel) @container + +(lookup_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(object_type + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/verilog/rainbow-blocks.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/verilog/rainbow-blocks.scm new file mode 100644 index 00000000..e14b56cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/verilog/rainbow-blocks.scm @@ -0,0 +1,83 @@ +; match blocks + +(seq_block + "begin" @delimiter + "end" @delimiter @sentinel) @container + +; match parentheses + +(packed_dimension + "[" @delimiter + "]" @delimiter @sentinel) @container + +(data_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parameter_port_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(named_port_connection + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_of_port_declarations + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameter_value_assignment + "(" @delimiter + ")" @delimiter @sentinel) @container + +(named_parameter_assignment + "(" @delimiter + ")" @delimiter @sentinel) @container + +(hierarchical_instance + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast + "(" @delimiter + ")" @delimiter @sentinel) @container + +(conditional_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(event_control + "(" @delimiter + ")" @delimiter @sentinel) @container + +(primary + "(" @delimiter + ")" @delimiter @sentinel) @container + +(constant_primary + "(" @delimiter + ")" @delimiter @sentinel) @container + +(concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(constant_concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(multiple_concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(constant_select1 + "[" @delimiter + "]" @delimiter @sentinel) @container + +(bit_select1 + "[" @delimiter + "]" @delimiter @sentinel) @container + +(select1 + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/verilog/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/verilog/rainbow-delimiters.scm new file mode 100644 index 00000000..3743925c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/verilog/rainbow-delimiters.scm @@ -0,0 +1,75 @@ +(packed_dimension + "[" @delimiter + "]" @delimiter @sentinel) @container + +(data_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parameter_port_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(named_port_connection + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_of_port_declarations + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameter_value_assignment + "(" @delimiter + ")" @delimiter @sentinel) @container + +(named_parameter_assignment + "(" @delimiter + ")" @delimiter @sentinel) @container + +(hierarchical_instance + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast + "(" @delimiter + ")" @delimiter @sentinel) @container + +(conditional_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(event_control + "(" @delimiter + ")" @delimiter @sentinel) @container + +(primary + "(" @delimiter + ")" @delimiter @sentinel) @container + +(constant_primary + "(" @delimiter + ")" @delimiter @sentinel) @container + +(concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(constant_concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(multiple_concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(constant_select1 + "[" @delimiter + "]" @delimiter @sentinel) @container + +(bit_select1 + "[" @delimiter + "]" @delimiter @sentinel) @container + +(select1 + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vim/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vim/rainbow-delimiters.scm new file mode 100644 index 00000000..f606d228 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vim/rainbow-delimiters.scm @@ -0,0 +1,29 @@ +;;; Note: The Vim grammar places all parentheses on the same level. This means +;;; an expression like (((3))) does not have three levels of nesting, but only +;;; one. All the parentheses and the integer literal are on the same level. +;;; This makes it impossible to apply alternating highlights. +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(dictionnary ;; this is no typo, "dictionary" is misspelled in the parser + "{" @delimiter + (dictionnary_entry + ":" @delimiter) + "}" @delimiter @sentinel) @container + +(call_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(unary_operation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(binary_operation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(ternary_expression + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vimdoc/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vimdoc/rainbow-delimiters.scm new file mode 100644 index 00000000..d53527c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vimdoc/rainbow-delimiters.scm @@ -0,0 +1,3 @@ +;;; Intentionally empty. Vim help files can have other languages embedded and +;;; we want to let those grammars handle the highlighting. This query only +;;; exists to satisfy the requirements of this plugin. diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vue/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vue/rainbow-delimiters.scm new file mode 100644 index 00000000..fb7bb619 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/vue/rainbow-delimiters.scm @@ -0,0 +1,52 @@ +;;; A Vue file is made up of top-level tags which contain code written in other +;;; languages + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (tag_name) @delimiter + "/>" @delimiter @sentinel)) @container + +(template_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(script_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(style_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(interpolation + "{{" @delimiter + "}}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/xml/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/xml/rainbow-delimiters.scm new file mode 100644 index 00000000..fbe7b0a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/xml/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(element + (STag + "<" @delimiter + (Name) @delimiter + ">" @delimiter) + (ETag + "" @delimiter @sentinel))@container + +(element + (EmptyElemTag + "<" @delimiter + (Name) @delimiter + "/>" @delimiter @sentinel)) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/yaml/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/yaml/rainbow-delimiters.scm new file mode 100644 index 00000000..5d70a3e4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/lualine/themes/yaml/rainbow-delimiters.scm @@ -0,0 +1,7 @@ +(flow_mapping + "{" @delimiter + "}" @delimiter @sentinel) @container + +(flow_sequence + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-frappe-cursor.lua b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-frappe-cursor.lua new file mode 100644 index 00000000..3cbc8c67 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-frappe-cursor.lua @@ -0,0 +1,4 @@ +local palette = require("catppuccin.palettes").get_palette "frappe" +local presets = require "catppuccin.utils.reactive" + +return presets.cursor("catppuccin-frappe-cursor", palette) diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-frappe-cursorline.lua b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-frappe-cursorline.lua new file mode 100644 index 00000000..4e8a1b44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-frappe-cursorline.lua @@ -0,0 +1,78 @@ +local darken = require("catppuccin.utils.colors").darken +local palette = require("catppuccin.palettes").get_palette "frappe" + +return { + name = "catppuccin-frappe-cursorline", + static = { + winhl = { + inactive = { + CursorLine = { bg = darken(palette.surface0, 0.85) }, + CursorLineNr = { bg = darken(palette.surface0, 0.85) }, + }, + }, + }, + modes = { + i = { + winhl = { + CursorLine = { bg = darken(palette.sky, 0.4) }, + CursorLineNr = { bg = darken(palette.sky, 0.4) }, + }, + }, + n = { + winhl = { + CursorLine = { bg = palette.surface0 }, + CursorLineNr = { bg = palette.surface0 }, + }, + }, + no = { + operators = { + [{ "gu", "gU", "g~", "~" }] = { + winhl = { + CursorLine = { bg = palette.surface2 }, + CursorLineNr = { bg = palette.surface2 }, + }, + }, + -- delete operator + d = { + winhl = { + CursorLine = { bg = darken(palette.red, 0.4) }, + CursorLineNr = { bg = darken(palette.red, 0.4) }, + }, + }, + -- yank operator + y = { + winhl = { + CursorLine = { bg = darken(palette.peach, 0.4) }, + CursorLineNr = { bg = darken(palette.peach, 0.4) }, + }, + }, + -- change operator + c = { + winhl = { + CursorLine = { bg = darken(palette.blue, 0.5) }, + CursorLineNr = { bg = darken(palette.blue, 0.5) }, + }, + }, + }, + }, + -- replace mode + R = { + winhl = { + CursorLine = { bg = darken(palette.sapphire, 0.5) }, + CursorLineNr = { bg = darken(palette.sapphire, 0.5) }, + }, + }, + -- visual mode + [{ "v", "V", "\x16" }] = { + winhl = { + Visual = { bg = darken(palette.mauve, 0.4) }, + }, + }, + -- select mode + [{ "s", "S", "\x13" }] = { + winhl = { + Visual = { bg = darken(palette.pink, 0.4) }, + }, + }, + }, +} diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-latte-cursor.lua b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-latte-cursor.lua new file mode 100644 index 00000000..3c9a660e --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-latte-cursor.lua @@ -0,0 +1,8 @@ +local palette = require("catppuccin.palettes").get_palette "latte" +local presets = require "catppuccin.utils.reactive" + +local preset = presets.cursor("catppuccin-latte-cursor", palette) + +preset.modes.R.hl.ReactiveCursor = { bg = palette.flamingo } + +return preset diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-latte-cursorline.lua b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-latte-cursorline.lua new file mode 100644 index 00000000..77e647bf --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-latte-cursorline.lua @@ -0,0 +1,77 @@ +local lighten = require("catppuccin.utils.colors").lighten +local palette = require("catppuccin.palettes").get_palette "latte" + +return { + name = "catppuccin-latte-cursorline", + static = { + winhl = { + inactive = { + CursorLine = { bg = lighten(palette.surface0, 0.5) }, + CursorLineNr = { bg = lighten(palette.surface0, 0.5) }, + }, + }, + }, + modes = { + i = { + winhl = { + CursorLine = { bg = lighten(palette.teal, 0.4) }, + CursorLineNr = { bg = lighten(palette.teal, 0.4) }, + }, + }, + n = { + winhl = { + CursorLine = { bg = palette.surface0 }, + CursorLineNr = { bg = palette.surface0 }, + }, + }, + no = { + operators = { + [{ "gu", "gU", "g~", "~" }] = { + winhl = { + CursorLine = { bg = palette.surface2 }, + CursorLineNr = { bg = palette.surface2 }, + }, + }, + -- delete operator + d = { + winhl = { + CursorLine = { bg = lighten(palette.red, 0.3) }, + CursorLineNr = { bg = lighten(palette.red, 0.3) }, + }, + }, + -- yank operator + y = { + winhl = { + CursorLine = { bg = lighten(palette.peach, 0.3) }, + CursorLineNr = { bg = lighten(palette.peach, 0.3) }, + }, + }, + -- change operator + c = { + winhl = { + CursorLine = { bg = lighten(palette.blue, 0.3) }, + CursorLineNr = { bg = lighten(palette.blue, 0.3) }, + }, + }, + }, + }, + -- visual + [{ "v", "V", "\x16" }] = { + winhl = { + Visual = { bg = lighten(palette.mauve, 0.3) }, + }, + }, + -- select + [{ "s", "S", "\x13" }] = { + winhl = { + Visual = { bg = lighten(palette.pink, 0.3) }, + }, + }, + R = { + winhl = { + CursorLine = { bg = lighten(palette.flamingo, 0.2) }, + CursorLineNr = { bg = lighten(palette.flamingo, 0.2) }, + }, + }, + }, +} diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-macchiato-cursor.lua b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-macchiato-cursor.lua new file mode 100644 index 00000000..73d3b3a1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-macchiato-cursor.lua @@ -0,0 +1,4 @@ +local palette = require("catppuccin.palettes").get_palette "macchiato" +local presets = require "catppuccin.utils.reactive" + +return presets.cursor("catppuccin-macchiato-cursor", palette) diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-macchiato-cursorline.lua b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-macchiato-cursorline.lua new file mode 100644 index 00000000..a38c3fd1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-macchiato-cursorline.lua @@ -0,0 +1,10 @@ +local palette = require("catppuccin.palettes").get_palette "macchiato" +local presets = require "catppuccin.utils.reactive" +local darken = require("catppuccin.utils.colors").darken + +local preset = presets.cursorline("catppuccin-macchiato-cursorline", palette) + +preset.static.winhl.inactive.CursorLine = { bg = darken(palette.surface0, 0.8) } +preset.static.winhl.inactive.CursorLineNr = { bg = darken(palette.surface0, 0.8) } + +return preset diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-mocha-cursor.lua b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-mocha-cursor.lua new file mode 100644 index 00000000..c4e47939 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-mocha-cursor.lua @@ -0,0 +1,4 @@ +local palette = require("catppuccin.palettes").get_palette "mocha" +local presets = require "catppuccin.utils.reactive" + +return presets.cursor("catppuccin-mocha-cursor", palette) diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-mocha-cursorline.lua b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-mocha-cursorline.lua new file mode 100644 index 00000000..d511dc9f --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/reactive/presets/catppuccin-mocha-cursorline.lua @@ -0,0 +1,4 @@ +local palette = require("catppuccin.palettes").get_palette "mocha" +local presets = require "catppuccin.utils.reactive" + +return presets.cursorline("catppuccin-mocha-cursorline", palette) diff --git a/config/neovim/store/lazy-plugins/promise-async/themes/catppuccin.vim b/config/neovim/store/lazy-plugins/promise-async/themes/catppuccin.vim new file mode 100644 index 00000000..16efd509 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/themes/catppuccin.vim @@ -0,0 +1,20 @@ +let s:c = has("nvim") == 1 ? luaeval('require("catppuccin.palettes").get_palette()') : luaeval('vim.dict(require("catppuccin.palettes").get_palette())') + +let s:p = {} + +let s:p.display = { 'guibg': s:c.mantle } + +let s:p.input = s:p.display +let s:p.indicator = { 'guifg': s:c.subtext1, 'guibg': s:c.mantle } +let s:p.spinner = { 'guifg': s:c.yellow, 'guibg': s:c.mantle, 'gui': "bold" } +let s:p.search_text = { 'guifg': s:c.text, 'guibg': s:c.mantle, 'gui': "bold" } + +let s:p.preview = { 'guibg': s:c.base } + +let s:p.selected = { 'guifg': s:c.sapphire, 'gui': "bold,underline" } +let s:p.current_selection = { 'guibg': s:c.surface0, 'gui': "bold" } + +let s:p.selected_sign = { 'guifg': s:c.red } +let s:p.current_selection_sign = copy(s:p.selected_sign) + +let g:clap#themes#catppuccin#palette = s:p diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/doc/luasnip.txt b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/doc/luasnip.txt new file mode 100644 index 00000000..e0ce8a2a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/doc/luasnip.txt @@ -0,0 +1,3709 @@ +*luasnip.txt* For NVIM v0.8.0 Last change: 2024 May 24 + +============================================================================== +Table of Contents *luasnip-table-of-contents* + +1. Basics |luasnip-basics| + - Jump-Index |luasnip-basics-jump-index| + - Adding Snippets |luasnip-basics-adding-snippets| + - Snippet Insertion |luasnip-basics-snippet-insertion| +2. Node |luasnip-node| + - Api |luasnip-node-api| +3. Snippets |luasnip-snippets| + - Data |luasnip-snippets-data| +4. TextNode |luasnip-textnode| +5. InsertNode |luasnip-insertnode| +6. FunctionNode |luasnip-functionnode| +7. Node Reference |luasnip-node-reference| +8. ChoiceNode |luasnip-choicenode| +9. SnippetNode |luasnip-snippetnode| +10. IndentSnippetNode |luasnip-indentsnippetnode| +11. DynamicNode |luasnip-dynamicnode| +12. RestoreNode |luasnip-restorenode| +13. Key Indexer |luasnip-key-indexer| +14. Absolute Indexer |luasnip-absolute-indexer| +15. MultiSnippet |luasnip-multisnippet| +16. Extras |luasnip-extras| + - Lambda |luasnip-extras-lambda| + - Match |luasnip-extras-match| + - Repeat |luasnip-extras-repeat| + - Partial |luasnip-extras-partial| + - Nonempty |luasnip-extras-nonempty| + - Dynamic Lambda |luasnip-extras-dynamic-lambda| + - FMT |luasnip-extras-fmt| + - Conditions |luasnip-extras-conditions| + - On The Fly-Snippets |luasnip-extras-on-the-fly-snippets| + - select_choice |luasnip-extras-select_choice| + - Filetype-Functions |luasnip-extras-filetype-functions| + - Postfix-Snippet |luasnip-extras-postfix-snippet| + - Treesitter-Postfix-Snippet |luasnip-extras-treesitter-postfix-snippet| + - Snippet List |luasnip-extras-snippet-list| + - Snippet Location |luasnip-extras-snippet-location| +17. Extend Decorator |luasnip-extend-decorator| +18. LSP-Snippets |luasnip-lsp-snippets| + - Snipmate Parser |luasnip-lsp-snippets-snipmate-parser| + - Transformations |luasnip-lsp-snippets-transformations| +19. Variables |luasnip-variables| + - Environment Namespaces |luasnip-variables-environment-namespaces| + - LSP-Variables |luasnip-variables-lsp-variables| +20. Loaders |luasnip-loaders| + - Snippet-specific filetypes |luasnip-loaders-snippet-specific-filetypes| + - VS-Code |luasnip-loaders-vs-code| + - SNIPMATE |luasnip-loaders-snipmate| + - Lua |luasnip-loaders-lua| + - edit_snippets |luasnip-loaders-edit_snippets| +21. SnippetProxy |luasnip-snippetproxy| +22. ext_opts |luasnip-ext_opts| +23. Docstrings |luasnip-docstrings| +24. Docstring-Cache |luasnip-docstring-cache| +25. Events |luasnip-events| +26. Cleanup |luasnip-cleanup| +27. Logging |luasnip-logging| +28. Source |luasnip-source| +29. Config-Options |luasnip-config-options| +30. Troubleshooting |luasnip-troubleshooting| + - Adding Snippets |luasnip-troubleshooting-adding-snippets| +31. API |luasnip-api| +> + __ ____ + /\ \ /\ _`\ __ + \ \ \ __ __ __ \ \,\L\_\ ___ /\_\ _____ + \ \ \ __/\ \/\ \ /'__`\\/_\__ \ /' _ `\/\ \/\ '__`\ + \ \ \L\ \ \ \_\ \/\ \L\.\_/\ \L\ \/\ \/\ \ \ \ \ \L\ \ + \ \____/\ \____/\ \__/.\_\ `\____\ \_\ \_\ \_\ \ ,__/ + \/___/ \/___/ \/__/\/_/\/_____/\/_/\/_/\/_/\ \ \/ + \ \_\ + \/_/ +< + +LuaSnip is a snippet engine written entirely in Lua. It has some great features +like inserting text (`luasnip-function-node`) or nodes (`luasnip-dynamic-node`) +based on user input, parsing LSP syntax and switching nodes +(`luasnip-choice-node`). For basic setup like mappings and installing, check +the README. + +All code snippets in this help assume the following: + +>lua + local ls = require("luasnip") + local s = ls.snippet + local sn = ls.snippet_node + local isn = ls.indent_snippet_node + local t = ls.text_node + local i = ls.insert_node + local f = ls.function_node + local c = ls.choice_node + local d = ls.dynamic_node + local r = ls.restore_node + local events = require("luasnip.util.events") + local ai = require("luasnip.nodes.absolute_indexer") + local extras = require("luasnip.extras") + local l = extras.lambda + local rep = extras.rep + local p = extras.partial + local m = extras.match + local n = extras.nonempty + local dl = extras.dynamic_lambda + local fmt = require("luasnip.extras.fmt").fmt + local fmta = require("luasnip.extras.fmt").fmta + local conds = require("luasnip.extras.expand_conditions") + local postfix = require("luasnip.extras.postfix").postfix + local types = require("luasnip.util.types") + local parse = require("luasnip.util.parser").parse_snippet + local ms = ls.multi_snippet + local k = require("luasnip.nodes.key_indexer").new_key +< + +As noted in the |luasnip-loaders-lua|-section: + + + By default, the names from `luasnip.config.snip_env` + + will be used, but it’s possible to customize them by setting `snip_env` in + `setup`. +Furthermore, note that while this document assumes you have defined `ls` to be +`require("luasnip")`, it is **not** provided in the default set of variables. + + +============================================================================== +1. Basics *luasnip-basics* + +In LuaSnip, snippets are made up of `nodes`. These can contain either + +- static text (`textNode`) +- text that can be edited (`insertNode`) +- text that can be generated from the contents of other nodes (`functionNode`) +- other nodes + - `choiceNode`: allows choosing between two nodes (which might contain more + nodes) + - `restoreNode`: store and restore input to nodes +- or nodes that can be generated based on input (`dynamicNode`). + +Snippets are always created using the `s(trigger:string, +nodes:table)`-function. It is explained in more detail in |luasnip-snippets|, +but the gist is that it creates a snippet that contains the nodes specified in +`nodes`, which will be inserted into a buffer if the text before the cursor +matches `trigger` when `ls.expand` is called. + + +JUMP-INDEX *luasnip-basics-jump-index* + +Nodes that can be jumped to (`insertNode`, `choiceNode`, `dynamicNode`, +`restoreNode`, `snippetNode`) all require a "jump-index" so luasnip knows the +order in which these nodes are supposed to be visited ("jumped to"). + +>lua + s("trig", { + i(1), t"text", i(2), t"text again", i(3) + }) +< + +These indices don’t "run" through the entire snippet, like they do in +textmate-snippets (`"$1 ${2: $3 $4}"`), they restart at 1 in each nested +snippetNode: + +>lua + s("trig", { + i(1), t" ", sn(2, { + t" ", i(1), t" ", i(2) + }) + }) +< + +(roughly equivalent to the given textmate-snippet). + + +ADDING SNIPPETS *luasnip-basics-adding-snippets* + +The snippets for a given filetype have to be added to luasnip via +`ls.add_snippets(filetype, snippets)`. Snippets that should be accessible +globally (in all filetypes) have to be added to the special filetype `all`. + +>lua + ls.add_snippets("all", { + s("ternary", { + -- equivalent to "${1:cond} ? ${2:then} : ${3:else}" + i(1, "cond"), t(" ? "), i(2, "then"), t(" : "), i(3, "else") + }) + }) +< + +It is possible to make snippets from one filetype available to another using +`ls.filetype_extend`, more info on that in the section |luasnip-api|. + + +SNIPPET INSERTION *luasnip-basics-snippet-insertion* + +When a new snippet is expanded, it can be connected with the snippets that have +already been expanded in the buffer in various ways. First of all, Luasnip +distinguishes between root-snippets and child-snippets. The latter are nested +inside other snippets, so when jumping through a snippet, one may also traverse +the child-snippets expanded inside it, more or less as if the child just +contains more nodes of the parent. Root-snippets are of course characterised by +not being child-snippets. When expanding a new snippet, it becomes a child of +the snippet whose region it is expanded inside, and a root if it is not inside +any snippet’s region. If it is inside another snippet, the specific node it +is inside is determined, and the snippet then nested inside that node. + +- If that node is interactive (for example, an `insertNode`), the new snippet + will be traversed when the node is visited, as long as the + configuration-option `link_children` is enabled. If it is not enabled, it is + possible to jump from the snippet to the node, but not the other way around. +- If that node is not interactive, the snippet will be linked to the currently + active node, also such that it will not be jumped to again once it is left. + This is to prevent jumping large distances across the buffer as much as + possible. There may still be one large jump from the snippet back to the + current node it is nested inside, but that seems hard to avoid. + Thus, one should design snippets such that the regions where other snippets + may be expanded are inside `insertNodes`. + +If the snippet is not a child, but a root, it can be linked up with the roots +immediately adjacent to it by enabling `link_roots` in `setup`. Since by +default only one root is remembered, one should also set `keep_roots` if +`link_roots` is enabled. The two are separate options, since roots that are not +linked can still be reached by `ls.activate_node()`. This setup (remember +roots, but don’t jump to them) is useful for a super-tab like mapping +(`` and jump on the same key), where one would like to still enter +previous roots. Since there would almost always be more jumps if the roots are +linked, regular `` would not work almost all the time, and thus +`link_roots` has to stay disabled. + + +============================================================================== +2. Node *luasnip-node* + +Every node accepts, as its last parameter, an optional table of arguments. +There are some common ones (which are listed here), and some that only apply to +some nodes (`user_args` for function/dynamicNode). These `opts` are only +mentioned if they accept options that are not common to all nodes. + +Common opts: + +- `node_ext_opts` and `merge_node_ext_opts`: Control `ext_opts` (most likely + highlighting) of the node. Described in detail in |luasnip-ext_opts| +- `key`: The node can be reffered to by this key. Useful for either |luasnip-key-indexer| or for finding the node at runtime (See + |luasnip-snippets-api|), for example inside a `dynamicNode`. The keys + do not have to be unique across the entire lifetime of the snippet, but at any + point in time, the snippet may contain each key only once. This means it is + fine to return a keyed node from a `dynamicNode`, because even if it will be + generated multiple times, those will not be valid at the same time. +- `node_callbacks`: Define event-callbacks for this node (see + |luasnip-events|). + Accepts a table that maps an event, e.g. `events.enter` to the callback + (essentially the same as `callbacks` passed to `s`, only that there is no + first mapping from jump-index to the table of callbacks). + + +API *luasnip-node-api* + +- `get_jump_index()`: this method returns the jump-index of a node. If a node + doesn’t have a jump-index, this method returns `nil` instead. +- `get_buf_position(opts) -> {from_position, to_position}`: + Determines the range of the buffer occupied by this node. `from`- and + `to_position` are `row,column`-tuples, `0,0`-indexed (first line is 0, first + column is 0) and end-inclusive (see |api-indexing|, this is extmarks + indexing). + - `opts`: `table|nil`, options, valid keys are: + - `raw`: `bool`, default `true`. This can be used to switch between + byte-columns (`raw=true`) and visual columns (`raw=false`). This makes a + difference if the line contains characters represented by multiple bytes + in UTF, for example `ÿ`. + + +============================================================================== +3. Snippets *luasnip-snippets* + +The most direct way to define snippets is `s`: + +>lua + s({trig="trigger"}, {}) +< + +(This snippet is useless beyond serving as a minimal example) + +`s(context, nodes, opts) -> snippet` + +- `context`: Either table or a string. Passing a string is equivalent to passing + >lua + { + trig = context + } + < + The following keys are valid: + - `trig`: string, the trigger of the snippet. If the text in front of (to the + left of) the cursor when `ls.expand()` is called matches it, the snippet will + be expanded. By default, "matches" means the text in front of the cursor + matches the trigger exactly, this behaviour can be modified through + `trigEngine` + - `name`: string, can be used by e.g. `nvim-compe` to identify the snippet. + - `desc` (or `dscr`): string, description of the snippet, -separated or table for + multiple lines. + - `wordTrig`: boolean, if true, the snippet is only expanded if the word + (`[%w_]+`) before the cursor matches the trigger entirely. True by default. + - `regTrig`: boolean, whether the trigger should be interpreted as a lua pattern. + False by default. Consider setting `trigEngine` to `"pattern"` instead, it is + more expressive, and in line with other settings. + - `trigEngine`: (function|string), determines how `trig` is interpreted, and what + it means for it to "match" the text in front of the cursor. This behaviour can + be completely customized by passing a function, but the predefined ones, which + are accessible by passing their identifier, should suffice in most cases: + - `"plain"`: the default-behaviour, the trigger has to match the text before + the cursor exactly. + - `"pattern"`: the trigger is interpreted as a lua-pattern, and is a match if + `trig .. "$"` matches the line up to the cursor. Capture-groups will be + accessible as `snippet.captures`. + - `"ecma"`: the trigger is interpreted as an ECMAscript-regex, and is a + match if `trig .. "$"` matches the line up to the cursor. Capture-groups + will be accessible as `snippet.captures`. + This `trigEngine` requires `jsregexp` (see + |luasnip-lsp-snippets-transformations|) to be installed, if it + is not, this engine will behave like `"plain"`. + - `"vim"`: the trigger is interpreted as a vim-regex, and is a match if + `trig .. "$"` matches the line up to the cursor. As with the other + regex/pattern-engines, captures will be available as `snippet.captures`, + but there is one caveat: the matching is done using `matchlist`, so for + now empty-string submatches will be interpreted as unmatched, and the + corresponding `snippet.capture[i]` will be `nil` (this will most likely + change, don’t rely on this behavior). + Besides these predefined engines, it is also possible to create new ones: + Instead of a string, pass a function which satisfies `trigEngine(trigger, opts) + -> (matcher(line_to_cursor, trigger) -> whole_match, captures)` (ie. the + function receives `trig` and `trigEngineOpts` can, for example, precompile a + regex, and then returns a function responsible for determining whether the + current cursor-position (represented by the line up to the cursor) matches the + trigger (it is passed again here so engines which don’t do any + trigger-specific work (like compilation) can just return a static `matcher`), + and what the capture-groups are). The `lua`-engine, for example, can be + implemented like this: + >lua + local function matcher(line_to_cursor, trigger) + -- look for match which ends at the cursor. + -- put all results into a list, there might be many capture-groups. + local find_res = { line_to_cursor:find(trigger .. "$") } + + if #find_res > 0 then + -- if there is a match, determine matching string, and the + -- capture-groups. + local captures = {} + -- find_res[1] is `from`, find_res[2] is `to` (which we already know + -- anyway). + local from = find_res[1] + local match = line_to_cursor:sub(from, #line_to_cursor) + -- collect capture-groups. + for i = 3, #find_res do + captures[i - 2] = find_res[i] + end + return match, captures + else + return nil + end + end + + local function engine(trigger) + -- don't do any special work here, can't precompile lua-pattern. + return matcher + end + < + The predefined engines are defined in `trig_engines.lua` + , + read it for more examples. + - `trigEngineOpts`: `table`, options for the used trigEngine. The + valid options are: + - `max_len`: number, upper bound on the length of the trigger. + If this is set, the `line_to_cursor` will be truncated (from the cursor of + course) to `max_len` characters before performing the match. + This is implemented because feeding long `line_to_cursor` into eg. the + pattern-trigEngine will hurt performance quite a bit (see issue + Luasnip#1103). + This option is implemented for all `trigEngines`. + - `docstring`: string, textual representation of the snippet, specified like + `desc`. Overrides docstrings loaded from json. + - `docTrig`: string, used as `line_to_cursor` during docstring-generation. This + might be relevant if the snippet relies on specific values in the + capture-groups (for example, numbers, which won’t work with the default + `$CAPTURESN` used during docstring-generation) + - `hidden`: boolean, hint for completion-engines. If set, the snippet should not + show up when querying snippets. + - `priority`: positive number, Priority of the snippet, 1000 by default. Snippets + with high priority will be matched to a trigger before those with a lower one. + The priority for multiple snippets can also be set in `add_snippets`. + - `snippetType`: string, should be either `snippet` or `autosnippet` (ATTENTION: + singular form is used), decides whether this snippet has to be triggered by + `ls.expand()` or whether is triggered automatically (don’t forget to set + `ls.config.setup({ enable_autosnippets = true })` if you want to use this + feature). If unset it depends on how the snippet is added of which type the + snippet will be. + - `resolveExpandParams`: `fn(snippet, line_to_cursor, matched_trigger, captures) + -> table|nil`, where + - `snippet`: `Snippet`, the expanding snippet object + - `line_to_cursor`: `string`, the line up to the cursor. + - `matched_trigger`: `string`, the fully matched trigger (can be retrieved + from `line_to_cursor`, but we already have that info here :D) + - `captures`: `captures` as returned by `trigEngine`. + This function will be evaluated in `Snippet:matches()` to decide whether the + snippet can be expanded or not. Returns a table if the snippet can be expanded, + `nil` if can not. The returned table can contain any of these fields: + - `trigger`: `string`, the fully matched trigger. + - `captures`: `table`, this list could update the capture-groups from + parameter in snippet expansion. + Both `trigger` and `captures` can override the values returned via + `trigEngine`. + - `clear_region`: `{ "from": {, }, "to": {, } }`, + both (0, 0)-indexed, the region where text has to be cleared before + inserting the snippet. + - `env_override`: `map string->(string[]|string)`, override or extend + the snippet’s environment (`snip.env`) + If any of these is `nil`, the default is used (`trigger` and `captures` as + returned by `trigEngine`, `clear_region` such that exactly the trigger is + deleted, no overridden environment-variables). + A good example for the usage of `resolveExpandParams` can be found in the + implementation of `postfix` + . + - `condition`: `fn(line_to_cursor, matched_trigger, captures) -> bool`, where + - `line_to_cursor`: `string`, the line up to the cursor. + - `matched_trigger`: `string`, the fully matched trigger (can be retrieved + from `line_to_cursor`, but we already have that info here :D) + - `captures`: if the trigger is pattern, this list contains the + capture-groups. Again, could be computed from `line_to_cursor`, but we + already did so. + - `show_condition`: `f(line_to_cursor) -> bool`. + - `line_to_cursor`: `string`, the line up to the cursor. + This function is (should be) evaluated by completion engines, indicating + whether the snippet should be included in current completion candidates. + Defaults to a function returning `true`. This is different from `condition` + because `condition` is evaluated by LuaSnip on snippet expansion (and thus has + access to the matched trigger and captures), while `show_condition` is (should + be) evaluated by the completion engines when scanning for available snippet + candidates. + - `filetype`: `string`, the filetype of the snippet. This overrides the filetype + the snippet is added (via `add_snippet`) as. +- `nodes`: A single node or a list of nodes. The nodes that make up the snippet. +- `opts`: A table with the following valid keys: + - `callbacks`: Contains functions that are called upon entering/leaving a node of + this snippet. For example: to print text upon entering the _second_ node of a + snippet, `callbacks` should be set as follows: + >lua + { + -- position of the node, not the jump-index!! + -- s("trig", {t"first node", t"second node", i(1, "third node")}). + [2] = { + [events.enter] = function(node, _event_args) print("2!") end + } + } + < + To register a callback for the snippets’ own events, the key `[-1]` may be + used. More info on events in |luasnip-events| + - `child_ext_opts`, `merge_child_ext_opts`: Control `ext_opts` applied to the + children of this snippet. More info on those in the |luasnip-ext_opts|-section. + +The `opts`-table, as described here, can also be passed to e.g. `snippetNode` +and `indentSnippetNode`. It is also possible to set `condition` and +`show_condition` (described in the documentation of the `context`-table) from +`opts`. They should, however, not be set from both. + + +DATA *luasnip-snippets-data* + +Snippets contain some interesting tables during runtime: + +- `snippet.env`: Contains variables used in the LSP-protocol, for example + `TM_CURRENT_LINE` or `TM_FILENAME`. It’s possible to add customized variables + here too, check |luasnip-variables-environment-namespaces| +- `snippet.captures`: If the snippet was triggered by a pattern (`regTrig`), and + the pattern contained capture-groups, they can be retrieved here. +- `snippet.trigger`: The string that triggered this snippet. Again, only + interesting if the snippet was triggered through `regTrig`, for getting the + full match. + +These variables/tables primarily come in handy in `dynamic/functionNodes`, +where the snippet can be accessed through the immediate parent +(`parent.snippet`), which is passed to the function. (in most cases `parent == +parent.snippet`, but the `parent` of the dynamicNode is not always the +surrounding snippet, it could be a `snippetNode`). + +## Api + +- `invalidate()`: call this method to effectively remove the snippet. The + snippet will no longer be able to expand via `expand` or `expand_auto`. It + will also be hidden from lists (at least if the plugin creating the list + respects the `hidden`-key), but it might be necessary to call + `ls.refresh_notify(ft)` after invalidating snippets. +- `get_keyed_node(key)`: Returns the currently visible node associated with + `key`. + + +============================================================================== +4. TextNode *luasnip-textnode* + +The most simple kind of node; just text. + +>lua + s("trigger", { t("Wow! Text!") }) +< + +This snippet expands to + +> + Wow! Text!⎵ +< + +where ⎵ is the cursor. + +Multiline strings can be defined by passing a table of lines rather than a +string: + +>lua + s("trigger", { + t({"Wow! Text!", "And another line."}) + }) +< + +`t(text, node_opts)`: + +- `text`: `string` or `string[]` +- `node_opts`: `table`, see |luasnip-node| + + +============================================================================== +5. InsertNode *luasnip-insertnode* + +These Nodes contain editable text and can be jumped to- and from (e.g. +traditional placeholders and tabstops, like `$1` in textmate-snippets). + +The functionality is best demonstrated with an example: + +>lua + s("trigger", { + t({"After expanding, the cursor is here ->"}), i(1), + t({"", "After jumping forward once, cursor is here ->"}), i(2), + t({"", "After jumping once more, the snippet is exited there ->"}), i(0), + }) +< + +The Insert Nodes are visited in order `1,2,3,..,n,0`. (The jump-index 0 also +_has_ to belong to an `insertNode`!) So the order of InsertNode-jumps is as +follows: + +1. After expansion, the cursor is at InsertNode 1, +2. after jumping forward once at InsertNode 2, +3. and after jumping forward again at InsertNode 0. + +If no 0-th InsertNode is found in a snippet, one is automatically inserted +after all other nodes. + +The jump-order doesn’t have to follow the "textual" order of the nodes: + +>lua + s("trigger", { + t({"After jumping forward once, cursor is here ->"}), i(2), + t({"", "After expanding, the cursor is here ->"}), i(1), + t({"", "After jumping once more, the snippet is exited there ->"}), i(0), + }) +< + +The above snippet will behave as follows: + +1. After expansion, we will be at InsertNode 1. +2. After jumping forward, we will be at InsertNode 2. +3. After jumping forward again, we will be at InsertNode 0. + +An **important** (because here Luasnip differs from other snippet engines) +detail is that the jump-indices restart at 1 in nested snippets: + +>lua + s("trigger", { + i(1, "First jump"), + t(" :: "), + sn(2, { + i(1, "Second jump"), + t" : ", + i(2, "Third jump") + }) + }) +< + +as opposed to e.g. the textmate syntax, where tabstops are snippet-global: + +>snippet + ${1:First jump} :: ${2: ${3:Third jump} : ${4:Fourth jump}} +< + +(this is not exactly the same snippet of course, but as close as possible) (the +restart-rule only applies when defining snippets in lua, the above +textmate-snippet will expand correctly when parsed). + +`i(jump_index, text, node_opts)` + +- `jump_index`: `number`, this determines when this node will be jumped to (see + |luasnip-basics-jump-index|). +- `text`: `string|string[]`, a single string for just one line, a list with >1 + entries for multiple lines. + This text will be SELECTed when the `insertNode` is jumped into. +- `node_opts`: `table`, described in |luasnip-node| + +If the `jump_index` is `0`, replacing its’ `text` will leave it outside the +`insertNode` (for reasons, check out Luasnip#110). + + +============================================================================== +6. FunctionNode *luasnip-functionnode* + +Function Nodes insert text based on the content of other nodes using a +user-defined function: + +>lua + local function fn( + args, -- text from i(2) in this example i.e. { { "456" } } + parent, -- parent snippet or parent node + user_args -- user_args from opts.user_args + ) + return '[' .. args[1][1] .. user_args .. ']' + end + + s("trig", { + i(1), t '<-i(1) ', + f(fn, -- callback (args, parent, user_args) -> string + {2}, -- node indice(s) whose text is passed to fn, i.e. i(2) + { user_args = { "user_args_value" }} -- opts + ), + t ' i(2)->', i(2), t '<-i(2) i(0)->', i(0) + }) +< + +`f(fn, argnode_references, node_opts)`: - `fn`: `function(argnode_text, parent, +user_args1,...,user_argsn) -> text` - `argnode_text`: `string[][]`, the text +currently contained in the argnodes (e.g. `{{line1}, {line1, line2}}`). The +snippet indent will be removed from all lines following the first. + +- `parent`: The immediate parent of the `functionNode`. It is included here as it + allows easy access to some information that could be useful in functionNodes + (see |luasnip-snippets-data| for some examples). Many snippets access the + surrounding snippet just as `parent`, but if the `functionNode` is nested + within a `snippetNode`, the immediate parent is a `snippetNode`, not the + surrounding snippet (only the surrounding snippet contains data like `env` or + `captures`). +- `user_args`: The `user_args` passed in `opts`. Note that there may be multiple + user_args (e.g. `user_args1, ..., user_argsn`). + +`fn` shall return a string, which will be inserted as is, or a table of strings +for multiline strings, where all lines following the first will be prefixed +with the snippets’ indentation. + +- `argnode_references`: `node_reference[]|node_refernce|nil`. Either no, a + single, or multiple |luasnip-node-reference|s. Changing any of these will + trigger a re-evaluation of `fn`, and insertion of the updated text. If no node + reference is passed, the `functionNode` is evaluated once upon expansion. +- `node_opts`: `table`, see |luasnip-node|. One additional key is supported: + - `user_args`: `any[]`, these will be passed to `fn` as `user_arg1`-`user_argn`. + These make it easier to reuse similar functions, for example a functionNode + that wraps some text in different delimiters (`()`, `[]`, …). + >lua + local function reused_func(_,_, user_arg1) + return user_arg1 + end + + s("trig", { + f(reused_func, {}, { + user_args = {"text"} + }), + f(reused_func, {}, { + user_args = {"different text"} + }), + }) + < + +**Examples**: + +- Use captures from the regex trigger using a functionNode: + >lua + s({trig = "b(%d)", regTrig = true}, + f(function(args, snip) return + "Captured Text: " .. snip.captures[1] .. "." end, {}) + ) + < +- `argnodes_text` during function evaluation: + >lua + s("trig", { + i(1, "text_of_first"), + i(2, {"first_line_of_second", "second_line_of_second"}), + f(function(args, snip) + --here + -- order is 2,1, not 1,2!! + end, {2, 1} )}) + < + At `--here`, `args` would look as follows (provided no text was changed after + expansion): + >lua + args = { + {"first_line_of_second", "second_line_of_second"}, + {"text_of_first"} + } + < +- |luasnip-absolute-indexer|: + >lua + s("trig", { + i(1, "text_of_first"), + i(2, {"first_line_of_second", "second_line_of_second"}), + f(function(args, snip) + -- just concat first lines of both. + return args[1][1] .. args[2][1] + end, {ai[2], ai[1]} )}) + < + +If the function only performs simple operations on text, consider using the +`lambda` from `luasnip.extras` (See |luasnip-extras-lambda|) + + +============================================================================== +7. Node Reference *luasnip-node-reference* + +Node references are used to refer to other nodes in various parts of +luasnip’s API. For example, argnodes in functionNode, dynamicNode or lambda +are node references. These references can be either of: + +- `number`: the jump-index of the node. + This will be resolved relative to the parent of the node this is passed to. + (So, only nodes with the same parent can be referenced. This is very easy to + grasp, but also limiting) +- `key_indexer`: the key of the node, if it is present. This will come in + handy if the node that is being referred to is not in the same + snippet/snippetNode as the one the node reference is passed to. + Also, it is the proper way to refer to a non-interactive node (a + functionNode, for example) +- `absolute_indexer`: the absolute position of the node. Just like + `key_indexer`, it allows addressing non-sibling nodes, but is a bit more + awkward to handle since a path from root to node has to be determined, + whereas `key_indexer` just needs the key to match. + Due to this, `key_indexer` should be generally preferred. + (More information in |luasnip-absolute-indexer|). +- `node`: just the node. Usage of this is discouraged since it can lead to + subtle errors (for example, if the node passed here is captured in a closure + and therefore not copied with the remaining tables in the snippet; there’s a + big comment about just this in commit 8bfbd61). + + +============================================================================== +8. ChoiceNode *luasnip-choicenode* + +ChoiceNodes allow choosing between multiple nodes. + +>lua + s("trig", c(1, { + t("Ugh boring, a text node"), + i(nil, "At least I can edit something now..."), + f(function(args) return "Still only counts as text!!" end, {}) + })) +< + +`c(jump_index, choices, node_opts)` + +- `jump_index`: `number`, since choiceNodes can be jumped to, they need a + jump-index (Info in |luasnip-basics-jump-index|). +- `choices`: `node[]|node`, the choices. The first will be initialliy active. + A list of nodes will be turned into a `snippetNode`. +- `node_opts`: `table`. `choiceNode` supports the keys common to all nodes + described in |luasnip-node|, and one additional key: + - `restore_cursor`: `false` by default. If it is set, and the node that was being + edited also appears in the switched to choice (can be the case if a + `restoreNode` is present in both choice) the cursor is restored relative to + that node. The default is `false` as enabling might lead to decreased + performance. It’s possible to override the default by wrapping the + `choiceNode` constructor in another function that sets `opts.restore_cursor` to + `true` and then using that to construct `choiceNode`s: + >lua + local function restore_cursor_choice(pos, choices, opts) + if opts then + opts.restore_cursor = true + else + opts = {restore_cursor = true} + end + return c(pos, choices, opts) + end + < + +Jumpable nodes that normally expect an index as their first parameter don’t +need one inside a choiceNode; their jump-index is the same as the +choiceNodes’. + +As it is only possible (for now) to change choices from within the choiceNode, +make sure that all of the choices have some place for the cursor to stop at! + +This means that in `sn(nil, {...nodes...})` `nodes` has to contain e.g. an +`i(1)`, otherwise luasnip will just "jump through" the nodes, making it +impossible to change the choice. + +>lua + c(1, { + t"some text", -- textNodes are just stopped at. + i(nil, "some text"), -- likewise. + sn(nil, {t"some text"}) -- this will not work! + sn(nil, {i(1), t"some text"}) -- this will. + }) +< + +The active choice for a choiceNode can be changed by either calling one of +`ls.change_choice(1)` (forwards) or `ls.change_choice(-1)` (backwards), or by +calling `ls.set_choice(choice_indx)`. + +One way to easily interact with choiceNodes is binding `change_choice(1/-1)` to +keys: + +>lua + -- set keybinds for both INSERT and VISUAL. + vim.api.nvim_set_keymap("i", "", "luasnip-next-choice", {}) + vim.api.nvim_set_keymap("s", "", "luasnip-next-choice", {}) + vim.api.nvim_set_keymap("i", "", "luasnip-prev-choice", {}) + vim.api.nvim_set_keymap("s", "", "luasnip-prev-choice", {}) +< + +Apart from this, there is also a picker (see |luasnip-select_choice| where no +cycling is necessary and any choice can be selected right away, via +`vim.ui.select`. + + +============================================================================== +9. SnippetNode *luasnip-snippetnode* + +SnippetNodes directly insert their contents into the surrounding snippet. This +is useful for `choiceNode`s, which only accept one child, or `dynamicNode`s, +where nodes are created at runtime and inserted as a `snippetNode`. + +Their syntax is similar to `s`, however, where snippets require a table +specifying when to expand, `snippetNode`s, similar to `insertNode`s, expect a +jump-index. + +>lua + s("trig", sn(1, { + t("basically just text "), + i(1, "And an insertNode.") + })) +< + +`sn(jump_index, nodes, node_opts)` + +- `jump_index`: `number`, the usual |luasnip-jump-index|. +- `nodes`: `node[]|node`, just like for `s`. + Note that `snippetNode`s don’t accept an `i(0)`, so the jump-indices of the nodes + inside them have to be in `1,2,...,n`. +- `node_opts`: `table`: again, the keys common to all nodes (documented in + |luasnip-node|) are supported, but also + - `callbacks`, + - `child_ext_opts` and + - `merge_child_ext_opts`, + which are further explained in |luasnip-snippets|. + + +============================================================================== +10. IndentSnippetNode *luasnip-indentsnippetnode* + +By default, all nodes are indented at least as deep as the trigger. With these +nodes it’s possible to override that behaviour: + +>lua + s("isn", { + isn(1, { + t({"This is indented as deep as the trigger", + "and this is at the beginning of the next line"}) + }, "") + }) +< + +(Note the empty string passed to isn). + +Indent is only applied after linebreaks, so it’s not possible to remove +indent on the line where the snippet was triggered using `ISN` (That is +possible via regex triggers where the entire line before the trigger is +matched). + +Another nice use case for `ISN` is inserting text, e.g. `//` or some other +comment string before the nodes of the snippet: + +>lua + s("isn2", { + isn(1, t({"//This is", "A multiline", "comment"}), "$PARENT_INDENT//") + }) +< + +Here the `//` before `This is` is important, once again, because indent is only +applied after linebreaks. + +To enable such usage, `$PARENT_INDENT` in the indentstring is replaced by the +parent’s indent. + +`isn(jump_index, nodes, indentstring, node_opts)` + +All of these parameters except `indentstring` are exactly the same as in +|luasnip-snippetnode|. + +- `indentstring`: `string`, will be used to indent the nodes inside this + `snippetNode`. + All occurences of `"$PARENT_INDENT"` are replaced with the actual indent of + the parent. + + +============================================================================== +11. DynamicNode *luasnip-dynamicnode* + +Very similar to functionNode, but returns a snippetNode instead of just text, +which makes them very powerful as parts of the snippet can be changed based on +user input. + +`d(jump_index, function, node-references, opts)`: + +- `jump_index`: `number`, just like all jumpable nodes, its’ position in the + jump-list (|luasnip-basics-jump-index|). +- `function`: `fn(args, parent, old_state, user_args) -> snippetNode` This + function is called when the argnodes’ text changes. It should generate and + return (wrapped inside a `snippetNode`) nodes, which will be inserted at the + dynamicNode’s place. `args`, `parent` and `user_args` are also explained in + |luasnip-functionnode| + - `args`: `table of text` (`{{"node1line1", "node1line2"}, {"node2line1"}}`) + from nodes the `dynamicNode` depends on. + - `parent`: the immediate parent of the `dynamicNode`. + - `old_state`: a user-defined table. This table may contain anything; its + intended usage is to preserve information from the previously generated + `snippetNode`. If the `dynamicNode` depends on other nodes, it may be + reconstructed, which means all user input (text inserted in `insertNodes`, + changed choices) to the previous `dynamicNode` is lost. + The `old_state` table must be stored in `snippetNode` returned by + the function (`snippetNode.old_state`). + The second example below illustrates the usage of `old_state`. + - `user_args`: passed through from `dynamicNode`-opts; may have more than one + argument. +- `node_references`: `node_reference[]|node_references|nil`, + |luasnip-node-references| to the nodes the dynamicNode depends on: if any of + these trigger an update (for example, if the text inside them changes), the + `dynamicNode`s’ function will be executed, and the result inserted at the + `dynamicNode`s place. (`dynamicNode` behaves exactly the same as `functionNode` + in this regard). +- `opts`: In addition to the common |luasnip-node|-keys, there is, again, + - `user_args`, which is described in |luasnip-functionnode|. + +**Examples**: + +This `dynamicNode` inserts an `insertNode` which copies the text inside the +first `insertNode`. + +>lua + s("trig", { + t"text: ", i(1), t{"", "copy: "}, + d(2, function(args) + -- the returned snippetNode doesn't need a position; it's inserted + -- "inside" the dynamicNode. + return sn(nil, { + -- jump-indices are local to each snippetNode, so restart at 1. + i(1, args[1]) + }) + end, + {1}) + }) +< + +This snippet makes use of `old_state` to count the number of updates. + +To store/restore values generated by the `dynamicNode` or entered into +`insert/choiceNode`, consider using the shortly-introduced `restoreNode` +instead of `old_state`. + +>lua + local function count(_, _, old_state) + old_state = old_state or { + updates = 0 + } + + old_state.updates = old_state.updates + 1 + + local snip = sn(nil, { + t(tostring(old_state.updates)) + }) + + snip.old_state = old_state + return snip + end + + ls.add_snippets("all", + s("trig", { + i(1, "change to update"), + d(2, count, {1}) + }) + ) +< + +As with `functionNode`, `user_args` can be used to reuse similar `dynamicNode`- +functions. + + +============================================================================== +12. RestoreNode *luasnip-restorenode* + +This node can store and restore a snippetNode as is. This includes changed +choices and changed text. Its’ usage is best demonstrated by an example: + +>lua + s("paren_change", { + c(1, { + sn(nil, { t("("), r(1, "user_text"), t(")") }), + sn(nil, { t("["), r(1, "user_text"), t("]") }), + sn(nil, { t("{"), r(1, "user_text"), t("}") }), + }), + }, { + stored = { + -- key passed to restoreNodes. + ["user_text"] = i(1, "default_text") + } + }) +< + +Here the text entered into `user_text` is preserved upon changing choice. + +`r(jump_index, key, nodes, node_opts)`: + +- `jump_index`, when to jump to this node. +- `key`, `string`: `restoreNode`s with the same key share their content. +- `nodes`, `node[]|node`: the content of the `restoreNode`. + Can either be a single node, or a table of nodes (both of which will be + wrapped inside a `snippetNode`, except if the single node already is a + `snippetNode`). + The content for a given key may be defined multiple times, but if the + contents differ, it’s undefined which will actually be used. + If a key’s content is defined in a `dynamicNode`, it will not be initially + used for `restoreNodes` outside that `dynamicNode`. A way around this + limitation is defining the content in the `restoreNode` outside the + `dynamicNode`. + +The content for a key may also be defined in the `opts`-parameter of the +snippet-constructor, as seen in the example above. The `stored`-table accepts +the same values as the `nodes`-parameter passed to `r`. If no content is +defined for a key, it defaults to the empty `insertNode`. + +An important-to-know limitation of `restoreNode` is that, for a given key, only +one may be visible at a time. See this issue + for details. + +The `restoreNode` is especially useful for storing input across updates of a +`dynamicNode`. Consider this: + +>lua + local function simple_restore(args, _) + return sn(nil, {i(1, args[1]), i(2, "user_text")}) + end + + s("rest", { + i(1, "preset"), t{"",""}, + d(2, simple_restore, 1) + }) +< + +Every time the `i(1)` in the outer snippet is changed, the text inside the +`dynamicNode` is reset to `"user_text"`. This can be prevented by using a +`restoreNode`: + +>lua + local function simple_restore(args, _) + return sn(nil, {i(1, args[1]), r(2, "dyn", i(nil, "user_text"))}) + end + + s("rest", { + i(1, "preset"), t{"",""}, + d(2, simple_restore, 1) + }) +< + +Now the entered text is stored. + +`restoreNode`s indent is not influenced by `indentSnippetNodes` right now. If +that really bothers you feel free to open an issue. + + +============================================================================== +13. Key Indexer *luasnip-key-indexer* + +A very flexible way of referencing nodes (|luasnip-node-reference|). While the +straightforward way of addressing nodes via their |luasnip-jump-index| suffices +in most cases, a `dynamic/functionNode` can only depend on nodes in the same +snippet(Node), its siblings (since the index is interpreted as relative to +their parent). Accessing a node with a different parent is thus not possible. +Secondly, and less relevant, only nodes that actually have a jump-index can be +referred to (a `functionNode`, for example, cannot be depended on). Both of +these restrictions are lifted with `key_indexer`: It allows addressing nodes by +their key, which can be set when the node is constructed, and is wholly +independent of the nodes’ position in the snippet, thus enabling descriptive +labeling. + +The following snippets demonstrate the issue and the solution by using +`key_indexer`: + +First, the addressed problem of referring to nodes outside the `functionNode`s +parent: + +>lua + s("trig", { + i(1), c(2, { + sn(nil, { + t"cannot access the argnode :(", + f(function(args) + return args[1] + end, {???}) -- can't refer to i(1), since it isn't a sibling of `f`. + }), + t"sample_text" + }) + }) +< + +And the solution: first give the node we want to refer to a key, and then pass +the same to the `functionNode`. + +>lua + s("trig", { + i(1, "", {key = "i1-key"}), c(2, { + sn(nil, { i(1), + t"can access the argnode :)", + f(function(args) + return args[1] + end, k("i1-key") ) + }), + t"sample_text" + }) + }) +< + + +============================================================================== +14. Absolute Indexer *luasnip-absolute-indexer* + +`absolute_indexer` allows accessing nodes by their unique jump-index path from +the snippet-root. This makes it almost as powerful as |luasnip-key-indexer|, +but again removes the possibility of referring to non-jumpable nodes and makes +it all a bit more error-prone since the jump-index paths are hard to follow, +and (unfortunately) have to be a bit verbose (see the long example of +`absolute_indexer`-positions below). Consider just using |luasnip-key-indexer| +instead. + +(The solution-snippet from |luasnip-key-indexer|, but using `ai` instead.) + +>lua + s("trig", { + i(1), c(2, { + sn(nil, { i(1), + t"can access the argnode :)", + f(function(args) + return args[1] + end, ai(1) ) + }), + t"sample_text" + }) + }) +< + +There are some quirks in addressing nodes: + +>lua + s("trig", { + i(2), -- ai[2]: indices based on jump-index, not position. + sn(1, { -- ai[1] + i(1), -- ai[1][1] + t"lel", -- not addressable. + i(2) -- ai[1][2] + }), + c(3, { -- ai[3] + i(nil), -- ai[3][1] + t"lel", -- ai[3][2]: choices are always addressable. + }), + d(4, function() -- ai[4] + return sn(nil, { -- ai[4][0] + i(1), -- ai[4][0][1] + }) + end, {}), + r(5, "restore_key", -- ai[5] + i(1) -- ai[5][0][1]: restoreNodes always store snippetNodes. + ), + r(6, "restore_key_2", -- ai[6] + sn(nil, { -- ai[6][0] + i(1) -- ai[6][0][1] + }) + ) + }) +< + +Note specifically that the index of a dynamicNode differs from that of the +generated snippetNode, and that restoreNodes (internally) always store a +snippetNode, so even if the restoreNode only contains one node, that node has +to be accessed as `ai[restoreNodeIndx][0][1]`. + +`absolute_indexer`s’ can be constructed in different ways: + +- `ai[1][2][3]` +- `ai(1, 2, 3)` +- `ai{1, 2, 3}` + +are all the same node. + + +============================================================================== +15. MultiSnippet *luasnip-multisnippet* + +There are situations where it might be comfortable to access a snippet in +different ways. For example, one might want to enable auto-triggering in +regions where the snippets usage is common, while leaving it manual-only in +others. This is where `ms` should be used: A single snippet can be associated +with multiple `context`s (the `context`-table determines the conditions under +which a snippet may be triggered). This has the advantage (compared with just +registering copies) that all `context`s are backed by a single snippet, and not +multiple, and it’s (at least should be :D) more comfortable to use. + +`ms(contexts, nodes, opts) -> addable`: + +- `contexts`: table containing list of `contexts`, and some keywords. + `context` are described in |luasnip-snippets|, here they may also be tables + or strings. + So far, there is only one valid keyword: + - `common`: Accepts yet another context. + The options in `common` are applied to (but don’t override) the other + contexts specified in `contexts`. +- `nodes`: List of nodes, exactly like in |luasnip-snippets|. +- `opts`: Table, options for this function: + - `common_opts`: The snippet-options (see also |luasnip-snippets|) applied + to the snippet generated from `nodes`. + +The returned object is an `addable`, something which can be passed to +`add_snippets`, or returned from the lua-loader. + +**Examples**: + +>lua + ls.add_snippets("all", { + ms({"a", "b"}, {t"a or b"}) + }) +< + +>lua + ls.add_snippets("all", { + ms({ + common = {snippetType = "autosnippet"}, + "a", + "b" + }, { + t"a or b (but autotriggered!!)" + }) + }) +< + +>lua + ls.add_snippets("all", { + ms({ + common = {snippetType = "autosnippet"}, + {trig = "a", snippetType = "snippet"}, + "b", + {trig = "c", condition = function(line_to_cursor) + return line_to_cursor == "" + end} + }, { + t"a or b (but autotriggered!!)" + }) + }) +< + + +============================================================================== +16. Extras *luasnip-extras* + + +LAMBDA *luasnip-extras-lambda* + +A shortcut for `functionNode`s that only do very basic string manipulation. + +`l(lambda, argnodes)`: + +- `lambda`: An object created by applying string-operations to `l._n`, objects + representing the `n`th argnode. + For example: + - `l._1:gsub("a", "e")` replaces all occurences of "a" in the text of the + first argnode with "e", or + - `l._1 .. l._2` concatenates text of the first and second argnode. + If an argnode contains multiple lines of text, they are concatenated with + `"\n"` prior to any operation. +- `argnodes`, a |luasnip-node-reference|, just like in function- and + dynamicNode. + +There are many examples for `lambda` in `Examples/snippets.lua` + + +MATCH *luasnip-extras-match* + +`match` can insert text based on a predicate (again, a shorthand for +`functionNode`). + +`match(argnodes, condition, then, else)`: + +- `argnode`: A single |luasnip-node-reference|. May not be nil, or + a table. +- `condition` may be either of + - `string`: interpreted as a lua pattern. Matched on the `\n`-joined (in case + it’s multiline) text of the first argnode (`args[1]:match(condition)`). + - `function`: `fn(args, snip) -> bool`: takes the same parameters as the + `functionNode`-function, any value other than nil or false is interpreted + as a match. + - `lambda`: `l._n` is the `\n`-joined text of the nth argnode. + Useful if string manipulations have to be performed before the string is matched. + Should end with `match`, but any other truthy result will be interpreted + as matching. +- `then` is inserted if the condition matches, +- `else` if it does not. + +Both `then` and `else` can be either text, lambda or function (with the same +parameters as specified above). `then`’s default-value depends on the +`condition`: + +- `pattern`: Simply the return value from the `match`, e.g. the entire match, + or, if there were capture groups, the first capture group. +- `function`: the return value of the function if it is either a string, or a + table (if there is no `then`, the function cannot return a table containing + something other than strings). +- `lambda`: Simply the first value returned by the lambda. + +Examples: + +- `match(n, "^ABC$", "A")`. +- `match(n, lambda._1:match(lambda._1:reverse()), "PALINDROME")` + >lua + s("trig", { + i(1), t":", + i(2), t"::", + m({1, 2}, l._1:match("^"..l._2.."$"), l._1:gsub("a", "e")) + }) + < +- >lua + s("extras1", { + i(1), t { "", "" }, m(1, "^ABC$", "A") + }) + < + Inserts "A" if the node with jump-index `n` matches "ABC" exactly, nothing + otherwise. +- >lua + s("extras2", { + i(1, "INPUT"), t { "", "" }, m(1, l._1:match(l._1:reverse()), "PALINDROME") + }) + < + Inserts `"PALINDROME"` if i(1) contains a palindrome. +- >lua + s("extras3", { + i(1), t { "", "" }, i(2), t { "", "" }, + m({ 1, 2 }, l._1:match("^" .. l._2 .. "$"), l._1:gsub("a", "e")) + }) + < + This inserts the text of the node with jump-index 1, with all occurences of `a` + replaced with `e`, if the second insertNode matches the first exactly. + + +REPEAT *luasnip-extras-repeat* + +Inserts the text of the passed node. + +`rep(node_reference)` - `node_reference`, a single |luasnip-node-reference|. + +>lua + s("extras4", { i(1), t { "", "" }, extras.rep(1) }) +< + + +PARTIAL *luasnip-extras-partial* + +Evaluates a function on expand and inserts its value. + +`partial(fn, params...)` - `fn`: any function - `params`: varargs, any, will be +passed to `fn`. + +For example `partial(os.date, "%Y")` inserts the current year on expansion. + +>lua + s("extras5", { extras.partial(os.date, "%Y") }) +< + + +NONEMPTY *luasnip-extras-nonempty* + +Inserts text if the referenced node doesn’t contain any text. + +`nonempty(node_reference, not_empty, empty)`: + +- `node_reference`, a single |luasnip-node-reference|. +- `not_empty`, `string`: inserted if the node is not empty. +- `empty`, `string`: inserted if the node is empty. + +>lua + s("extras6", { i(1, ""), t { "", "" }, extras.nonempty(1, "not empty!", "empty!") }) +< + + +DYNAMIC LAMBDA *luasnip-extras-dynamic-lambda* + +Pretty much the same as lambda, but it inserts the resulting text as an +insertNode, and, as such, it can be quickly overridden. + +`dynamic_lambda(jump_indx, lambda, node_references)` - `jump_indx`, as usual, +the jump-indx. + +The remaining arguments carry over from lambda. + +>lua + s("extras7", { i(1), t { "", "" }, extras.dynamic_lambda(2, l._1 .. l._1, 1) }) +< + + +FMT *luasnip-extras-fmt* + +Authoring snippets can be quite clunky, especially since every second node is +probably a `textNode`, inserting a small number of characters between two more +complicated nodes. + +`fmt` can be used to define snippets in a much more readable way. This is +achieved by borrowing (as the name implies) from `format`-functionality (our +syntax is very similar to python’s +). + +`fmt` accepts a string and a table of nodes. Each occurrence of a delimiter +pair in the string is replaced by one node from the table, while text outside +the delimiters is turned into textNodes. + +Simple example: + +>lua + ls.add_snippets("all", { + -- important! fmt does not return a snippet, it returns a table of nodes. + s("example1", fmt("just an {iNode1}", { + iNode1 = i(1, "example") + })), + s("example2", fmt([[ + if {} then + {} + end + ]], { + -- i(1) is at nodes[1], i(2) at nodes[2]. + i(1, "not now"), i(2, "when") + })), + s("example3", fmt([[ + if <> then + <> + end + ]], { + -- i(1) is at nodes[1], i(2) at nodes[2]. + i(1, "not now"), i(2, "when") + }, { + delimiters = "<>" + })), + s("example4", fmt([[ + repeat {a} with the same key {a} + ]], { + a = i(1, "this will be repeat") + }, { + repeat_duplicates = true + })) + }) +< + +One important detail here is that the position of the delimiters does not, in +any way, correspond to the jump-index of the nodes! + +`fmt(format:string, nodes:table of nodes, opts:table|nil) -> table of nodes` + +- `format`: a string. Occurences of `{}` ( `{}` are customizable; more + on that later) are replaced with `content[]` (which should be a + node), while surrounding text becomes `textNode`s. + To escape a delimiter, repeat it (`"{{"`). + If no key is given (`{}`) are numbered automatically: + `"{} ? {} : {}"` becomes `"{1} ? {2} : {3}"`, while + `"{} ? {3} : {}"` becomes `"{1} ? {3} : {4}"` (the count restarts at each + numbered placeholder). + If a key appears more than once in `format`, the node in + `content[]` is inserted for the first, and copies of it for + subsequent occurences. +- `nodes`: just a table of nodes. +- `opts`: optional arguments: + - `delimiters`: string, two characters. Change `{}` to some other pair, e.g. + `"<>"`. + - `strict`: Warn about unused nodes (default true). + - `trim_empty`: remove empty (`"%s*"`) first and last line in `format`. Useful + when passing multiline strings via `[[]]` (default true). + - `dedent`: remove indent common to all lines in `format`. Again, makes + passing multiline-strings a bit nicer (default true). + - `repeat_duplicates`: repeat nodes when a key is reused instead of copying + the node if it has a jump-index, refer to |luasnip-basics-jump-index| to + know which nodes have a jump-index (default false). + +There is also `require("luasnip.extras.fmt").fmta`. This only differs from +`fmt` by using angle brackets (`<>`) as the default delimiter. + + +CONDITIONS *luasnip-extras-conditions* + +This module (`luasnip.extras.condition`) contains functions that can be passed +to a snippet’s `condition` or `show_condition`. These are grouped accordingly +into `luasnip.extras.conditions.expand` and `luasnip.extras.conditions.show`: + +**expand**: + +- `line_begin`: only expand if the cursor is at the beginning of the line. + +**show**: + +- `line_end`: only expand at the end of the line. +- `has_selected_text`: only expand if there’s selected text stored after pressing + `store_selection_keys`. + +Additionally, `expand` contains all conditions provided by `show`. + + +CONDITION OBJECTS ~ + +`luasnip.extras.conditions` also contains condition objects. These can, just +like functions, be passed to `condition` or `show_condition`, but can also be +combined with each other into logical expressions: + +- `-c1 -> not c1` +- `c1 * c2 -> c1 and c2` +- `c1 + c2 -> c1 or c2` +- `c1 - c2 -> c1 and not c2`: This is similar to set differences: + `A \ B = {a in A | a not in B}`. This makes `-(a + b) = -a - b` an identity + representing de Morgan’s law: `not (a or b) = not a and not b`. However, + since boolean algebra lacks an additive inverse, `a + (-b) = a - b` does not + hold. Thus, this is NOT the same as `c1 + (-c2)`. +- `c1 ^ c2 -> c1 xor(!=) c2` +- `c1 % c2 -> c1 xnor(==) c2`: This decision may seem weird, considering how + there is an overload for the `==`-operator. Unfortunately, it’s not possible + to use this for our purposes (some info + here ), + so we decided to make use of a more obscure symbol (which will hopefully avoid + false assumptions about its meaning). + +This makes logical combinations of conditions very readable. Compare + +>lua + condition = conditions.expand.line_end + conditions.expand.line_begin +< + +with the more verbose + +>lua + condition = function(...) return conditions.expand.line_end(...) or conditions.expand.line_begin(...) end +< + +The conditions provided in `show` and `expand` are already condition objects. +To create new ones, use +`require("luasnip.extras.conditions").make_condition(condition_fn)` + + +ON THE FLY-SNIPPETS *luasnip-extras-on-the-fly-snippets* + +Sometimes it’s desirable to create snippets tailored for exactly the current +situation. For example inserting repetitive, but just slightly different +invocations of some function, or supplying data in some schema. + +On-the-fly snippets enable exactly this use case: they can be quickly created +and expanded with as little disruption as possible. + +Since they should mainly fast to write and don’t necessarily need all bells +and whistles, they don’t make use of lsp/textmate-syntax, but a more +simplistic one: + +- `$anytext` denotes a placeholder (`insertNode`) with text "anytext". The text + also serves as a unique key: if there are multiple placeholders with the same + key, only the first will be editable, the others will just mirror it. +- … That’s it. `$` can be escaped by preceding it with a second `$`, all other + symbols will be interpreted literally. + +There is currently only one way to expand on-the-fly snippets: +`require('luasnip.extras.otf').on_the_fly("")` will interpret +whatever text is in the register `` as a snippet, and expand it +immediately. The idea behind this mechanism is that it enables a very immediate +way of supplying and retrieving (expanding) the snippet: write the snippet-body +into the buffer, cut/yank it into some register, and call +`on_the_fly("")` to expand the snippet. + +Here’s one set of example keybindings: + +>vim + " in the first call: passing the register is optional since `on_the_fly` + " defaults to the unnamed register, which will always contain the previously cut + " text. + vnoremap "eclua require('luasnip.extras.otf').on_the_fly("e") + inoremap lua require('luasnip.extras.otf').on_the_fly("e") +< + +Obviously, `` is arbritary and can be changed to any other key combo. +Another interesting application is allowing multiple on-the-fly snippets at the +same time by retrieving snippets from multiple registers: + +>vim + " For register a + vnoremap a "aclua require('luasnip.extras.otf').on_the_fly() + inoremap a lua require('luasnip.extras.otf').on_the_fly("a") + + + " For register b + vnoremap a "bc:lua require('luasnip.extras.otf').on_the_fly() + inoremap b lua require('luasnip.extras.otf').on_the_fly("b") +< + + +SELECT_CHOICE *luasnip-extras-select_choice* + +It’s possible to leverage `vim.ui.select` for selecting a choice directly, +without cycling through the available choices. All that is needed for this is +calling `require("luasnip.extras.select_choice")`, most likely via some +keybind, e.g. + +>vim + inoremap lua require("luasnip.extras.select_choice")() +< + +while inside a choiceNode. The `opts.kind` hint for `vim.ui.select` will be set +to `luasnip`. + + +FILETYPE-FUNCTIONS *luasnip-extras-filetype-functions* + +Contains some utility functions that can be passed to the `ft_func` or +`load_ft_func`-settings. + +- `from_filetype`: the default for `ft_func`. Simply returns the filetype(s) of + the buffer. +- `from_cursor_pos`: uses treesitter to determine the filetype at the cursor. + With that, it’s possible to expand snippets in injected regions, as long as + the treesitter parser supports them. If this is used in conjuction with + `lazy_load`, extra care must be taken that all the filetypes that can be + expanded in a given buffer are also returned by `load_ft_func` (otherwise their + snippets may not be loaded). This can easily be achieved with `extend_load_ft`. +- `extend_load_ft`: `fn(extend_ft:map) -> fn` A simple solution to the problem + described above is loading more filetypes than just that of the target buffer + when `lazy_load`ing. This can be done ergonomically via `extend_load_ft`: + calling it with a table where the keys are filetypes, and the values are the + filetypes that should be loaded additionaly returns a function that can be + passed to `load_ft_func` and takes care of extending the filetypes properly. + >lua + ls.setup({ + load_ft_func = + -- Also load both lua and json when a markdown-file is opened, + -- javascript for html. + -- Other filetypes just load themselves. + require("luasnip.extras.filetype_functions").extend_load_ft({ + markdown = {"lua", "json"}, + html = {"javascript"} + }) + }) + < + + +POSTFIX-SNIPPET *luasnip-extras-postfix-snippet* + +Postfix snippets, famously used in rust analyzer + and various IDEs, are a type of snippet +which alters text before the snippet’s trigger. While these can be +implemented using regTrig snippets, this helper makes the process easier in +most cases. + +The simplest example, which surrounds the text preceeding the `.br` with +brackets `[]`, looks like: + +>lua + postfix(".br", { + f(function(_, parent) + return "[" .. parent.snippet.env.POSTFIX_MATCH .. "]" + end, {}), + }) +< + +and is triggered with `xxx.br` and expands to `[xxx]`. + +Note the `parent.snippet.env.POSTFIX_MATCH` in the function node. This is +additional field generated by the postfix snippet. This field is generated by +extracting the text matched (using a configurable matching string, see below) +from before the trigger. In the case above, the field would equal `"xxx"`. This +is also usable within dynamic nodes. + +This field can also be used within lambdas and dynamic nodes. + +>lua + postfix(".br", { + l("[" .. l.POSTFIX_MATCH .. "]"), + }) +< + +>lua + postfix(".brd", { + d(1, function (_, parent) + return sn(nil, {t("[" .. parent.env.POSTFIX_MATCH .. "]")}) + end) + }) +< + +The arguments to `postfix` are identical to the arguments to `s` but with a few +extra options. + +The first argument can be either a string or a table. If it is a string, that +string will act as the trigger, and if it is a table it has the same valid keys +as the table in the same position for `s` except: + +- `wordTrig`: This key will be ignored if passed in, as it must always be + false for postfix snippets. +- `match_pattern`: The pattern that the line before the trigger is matched + against. The default match pattern is `"[%w%.%_%-]+$"`. Note the `$`. This + matches since only the line _up until_ the beginning of the trigger is + matched against the pattern, which makes the character immediately + preceeding the trigger match as the end of the string. + +Some other match strings, including the default, are available from the postfix +module. `require("luasnip.extras.postfix).matches`: + +- `default`: `[%w%.%_%-%"%']+$` +- `line`: `^.+$` + +The second argument is identical to the second argument for `s`, that is, a +table of nodes. + +The optional third argument is the same as the third (`opts`) argument to the +`s` function, but with one difference: + +The postfix snippet works using a callback on the pre_expand event of the +snippet. If you pass a callback on the pre_expand event (structure example +below) it will get run after the builtin callback. + +>lua + { + callbacks = { + [-1] = { + [events.pre_expand] = function(snippet, event_args) + -- function body to match before the dot + -- goes here + end + } + } + } +< + + +TREESITTER-POSTFIX-SNIPPET *luasnip-extras-treesitter-postfix-snippet* + +Instead of triggering a postfix-snippet when some pattern matches in front of +the trigger, it might be useful to match if some specific treesitter-nodes +surround/are in front of the trigger. While this functionality can also be +implemented by a cusutom `resolveExpandParams`, this helper simplifies the +common cases. + +This matching of treesitter-nodes can be done either + +- by providing a query and the name of the capture that should be in front of + the trigger (in most cases, the complete match, but requiring specific nodes + before/after the matched node may be useful as well), or +- by providing a function that manually walks the node-tree, and returns the + node in front of the trigger on success (for increased flexibility). + +A simple example, which surrounds the previous node’s text preceeding the +`.mv` with `std::move()` in cpp files, looks like: + +>lua + local treesitter_postfix = require("luasnip.extras.treesitter_postfix").treesitter_postfix + + treesitter_postfix({ + trig = ".mv", + matchTSNode = { + query = [[ + [ + (call_expression) + (identifier) + (template_function) + (subscript_expression) + (field_expression) + (user_defined_literal) + ] @prefix + ]] + query_lang = "cpp" + }, + },{ + f(function(_, parent) + local node_content = table.concat(parent.snippet.env.LS_TSMATCH, '\n') + local replaced_content = ("std::move(%s)"):format(node_content) + return vim.split(ret_str, "\n", { trimempty = false }) + end) + }) +< + +`LS_TSMATCH` is the treesitter-postfix equivalent to `POSTFIX_MATCH`, and is +populated with the match (in this case the text of a treesitter-node) in front +of the trigger. + +The arguments to `treesitter_postfix` are identical to the arguments to `s` but +with a few extra options. + +The first argument has to be a table, which defines at least `trig` and +`matchTSNode`. All keys from the regular `s` may be set here (except for +`wordTrig`, which will be ignored), and additionally the following: + +- `reparseBuffer`, `string?`: Sometimes the trigger may interfere with + treesitter recognizing queries correctly. With this option, the trigger may + either be removed from the live-buffer (`"live"`), from a copy of the buffer + (`"copy"`), or not at all (`nil`). +- `matchTSNode`: How to determine whether there is a matching node in front of + the cursor. There are two options: + - `fun(parser: LuaSnip.extra.TSParser, pos: { [1]: number, [2]: number }): LuaSnip.extra.NamedTSMatch?, TSNode? Manually determine whether there is a matching node that ends just before`pos`(the beginning of the trigger). Return`nil,nil`if there is no match, otherwise first return a table mapping names to nodes (the text, position and type of these will be provided via`snip.env`), and second the node that is the matched node. + - `LuaSnip.extra.MatchTSNodeOpts`, which represents a query and provides all + captures of the matched pattern in `NamedTSMatch`. It contains the following + options: + - `query`, `string`: The query, in textual form. + - `query_name`, `string`: The name of the runtime-query to be used (passed + to `query.get()`), defaults to `"luasnip"` (so one could create a + file which only contains queries used by luasnip, like + `$CONFDIR/queries//luasnip.scm`, which might make sense to define + general concepts independent of a single snippet). + `query` and `query_name` are mutually exclusive, only one of both shall be + defined. + - `query_lang`, `string`: The language of the query. This is the only + required parameter to this function, since there’s no sufficiently + straightforward way to determine the language of the query for us. + Consider using `extend_override` to define a `ts_postfix`-function that + automatically fills in the language for the filetype of the snippet-file. + - `match_captures`, `string|string[]`: The capture(s) to use for determining + the actual prefix (so the node that should be immediately in front of the + trigger). This defaults to just `"prefix"`. + - `select`, `string?|fun(): LuaSnip.extra.MatchSelector`: Since there may be + multiple matching captures in front of the cursor, there has to be some + way to select the node that will actually be used. + If this is a string, it has to be one of "any", "shortest", or "longest", + which mean that any, the shortest, or the longest match is used. + If it is a function, it must return a table with two fields, `record` and + `retrieve`. `record` is called with a TSMatch and a potential node for the + ts-match, and may return `true` to abort the selection-procedure. + `retrieve` must return either a TSMatch-TSNode-tuple (which is used as the + match) or `nil`, to signify that there is no match. + `lua/luasnip/extras/_treesitter.lua` contains the table + `builtin_tsnode_selectors`, which contains the implementations for + any/shortest/longest, which can be used as examples for more complicated + custom-selectors. + +The text of the matched node can be accessed as `snip.env.LS_TSMATCH`. The text +of the nodes returned as `NamedTSMatch` can be accessed as +`snip.env.LS_TSCAPTURE_`, and their range and type as +`snip.env.LS_TSDATA..range/type` (where range is a tuple +of row-col-tuples, both 0-indexed). + +For a query like + +>scm + (function_declaration + name: (identifier) @fname + parameters: (parameters) @params + body: (block) @body + ) @prefix +< + +matched against + +>lua + function add(a, b) + return a + b + end +< + +`snip.env` would contain: + +- `LS_TSMATCH`: `{ "function add(a, b)", "\treturn a + b", "end" }` +- `LS_TSDATA`: + >lua + { + body = { + range = { { 1, 1 }, { 1, 13 } }, + type = "block" + }, + fname = { + range = { { 0, 9 }, { 0, 12 } }, + type = "identifier" + }, + params = { + range = { { 0, 12 }, { 0, 18 } }, + type = "parameters" + }, + prefix = { + range = { { 0, 0 }, { 2, 3 } }, + type = "function_declaration" + } + } + < +- `LS_TSCAPTURE_FNAME`: `{ "add" }` +- `LS_TSCAPTURE_PARAMS`: `{ "(a, b)" }` +- `LS_TSCAPTURE_BODY`: `{ "return a + b" }` +- `LS_TSCAPTURE_PREFIX`: `{ "function add(a, b)", "\treturn a + b", "end" }` + +(note that all variables containing text of nodes are string-arrays, one entry +for each line) + +There is one important caveat when accessing `LS_TSDATA` in +function/dynamicNodes: It won’t contain the values as specified here while +generating docstrings (in fact, it won’t even be a table). Since docstrings +have to be generated without any runtime-information, we just have to provide +dummy-data in `env`, which will be some kind of string related to the name of +the env-variable. Since the structure of `LS_TSDATA` obviously does not fit +that model, we can’t really handle it in a nice way (at least yet). So, for +now, best include a check like `local static_evaluation = type(env.LS_TSDATA) +== "string"`, and behave accordingly if `static_evaluation` is true (for +example, return some value tailored for displaying it in a docstring). + +One more example, which actually uses a few captures: + +>lua + ts_post({ + matchTSNode = { + query = [[ + (function_declaration + name: (identifier) @fname + parameters: (parameters) @params + body: (block) @body + ) @prefix + ]], + query_lang = "lua", + }, + trig = ".var" + }, fmt([[ + local {} = function{} + {} + end + ]], { + l(l.LS_TSCAPTURE_FNAME), + l(l.LS_TSCAPTURE_PARAMS), + l(l.LS_TSCAPTURE_BODY), + })) +< + +The module `luasnip.extras.treesitter_postfix` contains a few functions that +may be useful for creating more efficient ts-postfix-snippets. Nested in +`builtin.tsnode_matcher` are: + +- `fun find_topmost_types(types: string[]): MatchTSNodeFunc`: Generates + a `LuaSnip.extra.MatchTSNodeFunc` which returns the last parent whose type + is in `types`. +- `fun find_first_types(types: string[]): MatchTSNodeFunc`: Similar to + `find_topmost_types`, only this one matches the first parent whose type is in + types. +- `find_nth_parent(n: number): MatchTSNodeFunc`: Simply matches the `n`-th + parent of the innermost node infront of the trigger. + +With `find_topmost_types`, the first example can be implemented more +efficiently (without needing a whole query): + +>lua + local postfix_builtin = require("luasnip.extras.treesitter_postfix").builtin + + ls.add_snippets("all", { + ts_post({ + matchTSNode = postfix_builtin.tsnode_matcher.find_topmost_types({ + "call_expression", + "identifier", + "template_function", + "subscript_expression", + "field_expression", + "user_defined_literal" + }), + trig = ".mv" + }, { + l(l_str.format("std::move(%s)", l.LS_TSMATCH)) + }) + }, {key = "asdf"}) +< + + +SNIPPET LIST *luasnip-extras-snippet-list* + +>lua + local sl = require("luasnip.extras.snippet_list") +< + +Makes an `open` function available to use to open currently available snippets +in a different buffer/window/tab. + +`sl.open(opts:table|nil)` + +- `opts`: optional arguments: + - `snip_info`: `snip_info(snippet) -> table representation of snippet` + - `printer`: `printer(snippets:table) -> any` + - `display`: `display(snippets:any)` + +Benefits include: syntax highlighting, searching, and customizability. + +Simple Example: + +>lua + sl.open() +< + +Customization Examples: + +>lua + -- making our own snip_info + local function snip_info(snippet) + return { name = snippet.name } + end + + -- using it + sl.open({snip_info = snip_info}) +< + +>lua + -- making our own printer + local function printer(snippets) + local res = "" + + for ft, snips in pairs(snippets) do + res = res .. ft .. "\n" + for _, snip in pairs(snips) do + res = res .. " " .. "Name: " .. snip.name .. "\n" + res = res .. " " .. "Desc: " .. snip.description[1] .. "\n" + res = res .. " " .. "Trigger: " .. snip.trigger .. "\n" + res = res .. " ----" .. "\n" + end + end + + return res + end + + + -- using it + sl.open({printer = printer}) +< + +>lua + -- making our own display + local function display(printer_result) + -- right vertical split + vim.cmd("botright vnew") + + -- get buf and win handle + local buf = vim.api.nvim_get_current_buf() + local win = vim.api.nvim_get_current_win() + + -- setting window and buffer options + vim.api.nvim_win_set_option(win, "foldmethod", "manual") + vim.api.nvim_buf_set_option(buf, "filetype", "javascript") + + vim.api.nvim_buf_set_option(buf, "buftype", "nofile") + vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe") + vim.api.nvim_buf_set_option(buf, "buflisted", false) + + vim.api.nvim_buf_set_name(buf, "Custom Display buf " .. buf) + + -- dump snippets + local replacement = vim.split(printer_result) + vim.api.nvim_buf_set_lines(buf, 0, 0, false, replacement) + end + + -- using it + sl.open({display = display}) +< + +There is a **caveat** with implementing your own printer and/or display +function. The **default** behavior for the printer function is to return a +string representation of the snippets. The display function uses the results +from the printer function, therefore by **default** the display function is +expecting that result to be a string. + +However, this doesn’t have to be the case. For example, you can implement +your own printer function that returns a table representation of the snippets +**but** you would have to then implement your own display function or some +other function in order to return the result as a string. + +An `options` table, which has some core functionality that can be used to +customize 'common' settings, is provided. + +- `sl.options`: options table: + - `display`: `display(opts:table|nil) -> function(printer_result:string)` + +You can see from the example above that making a custom display is a fairly +involved process. What if you just wanted to change a buffer option like the +name or just the filetype? This is where `sl.options.display` comes in. It +allows you to customize buffer and window options while keeping the default +behavior. + +`sl.options.display(opts:table|nil) -> function(printer_result:string)` + +- `opts`: optional arguments: + - `win_opts`: `table which has a {window_option = value} form` + - `buf_opts`: `table which has a {buffer_option = value} form` + - `get_name`: `get_name(buf) -> string` + +Let’s recreate the custom display example above: + +>lua + -- keeping the default display behavior but modifying window/buffer + local modified_default_display = sl.options.display({ + buf_opts = {filetype = "javascript"}, + win_opts = {foldmethod = "manual"}, + get_name = function(buf) return "Custom Display buf " .. buf end + }) + + -- using it + sl.open({display = modified_default_display}) +< + + +SNIPPET LOCATION *luasnip-extras-snippet-location* + +This module can consume a snippets |luasnip-source|, more specifically, jump to +the location referred by it. This is primarily implemented for snippet which +got their source from one of the loaders, but might also work for snippets +where the source was set manually. + +`require("luasnip.extras.snip_location")`: + +- `snip_location.jump_to_snippet(snip, opts)` + Jump to the definition of `snip`. + - `snip`: a snippet with attached source-data. + - `opts`: `nil|table`, optional arguments, valid keys are: + - `hl_duration_ms`: `number`, duration for which the definition should be highlighted, + in milliseconds. 0 disables the highlight. + - `edit_fn`: `function(file)`, this function will be called with the file + the snippet is located in, and is responsible for jumping to it. + We assume that after it has returned, the current buffer contains `file`. +- `snip_location.jump_to_active_snippet(opts)` + Jump to definition of active snippet. + - `opts`: `nil|table`, accepts the same keys as the `opts`-parameter of + `jump_to_snippet`. + + +============================================================================== +17. Extend Decorator *luasnip-extend-decorator* + +Most of luasnip’s functions have some arguments to control their behaviour. +Examples include `s`, where `wordTrig`, `regTrig`, … can be set in the first +argument to the function, or `fmt`, where the delimiter can be set in the third +argument. This is all good and well, but if these functions are often used with +non-default settings, it can become cumbersome to always explicitly set them. + +This is where the `extend_decorator` comes in: it can be used to create +decorated functions which always extend the arguments passed directly with +other previously defined ones. + +An example: + +>lua + local fmt = require("luasnip.extras.fmt").fmt + + fmt("{}", {i(1)}) -- -> list of nodes, containing just the i(1). + + -- when authoring snippets for some filetype where `{` and `}` are common, they + -- would always have to be escaped in the format-string. It might be preferable + -- to use other delimiters, like `<` and `>`. + + fmt("<>", {i(1)}, {delimiters = "<>"}) -- -> same as above. + + -- but it's quite annoying to always pass the `{delimiters = "<>"}`. + + -- with extend_decorator: + local fmt_angle = ls.extend_decorator.apply(fmt, {delimiters = "<>"}) + fmt_angle("<>", {i(1)}) -- -> same as above. + + -- the same also works with other functions provided by luasnip, for example all + -- node/snippet-constructors and `parse_snippet`. +< + +`extend_decorator.apply(fn, ...)` requires that `fn` is previously registered +via `extend_decorator.register`. (This is not limited to LuaSnip’s functions; +although, for usage outside of LuaSnip, best copy the source file: +`/lua/luasnip/util/extend_decorator.lua`). + +`register(fn, ...)`: + +- `fn`: the function. +- `...`: any number of tables. Each specifies how to extend an argument of `fn`. + The tables accept: + - arg_indx, `number` (required): the position of the parameter to override. + - extend, `fn(arg, extend_value) -> effective_arg` (optional): this function + is used to extend the args passed to the decorated function. + It defaults to a function which just extends the the arg-table with the + extend table (accepts `nil`). + This extend behaviour is adaptable to accomodate `s`, where the first + argument may be string or table. + +`apply(fn, ...) -> decorated_fn`: + +- `fn`: the function to decorate. +- `...`: The values to extend with. These should match the descriptions passed + in `register` (the argument first passed to `register` will be extended with + the first value passed here). + +One more example for registering a new function: + +>lua + local function somefn(arg1, arg2, opts1, opts2) + -- not important + end + + -- note the reversed arg_indx!! + extend_decorator.register(somefn, {arg_indx=4}, {arg_indx=3}) + local extended = extend_decorator.apply(somefn, + {key = "opts2 is extended with this"}, + {key = "and opts1 with this"}) + extended(...) +< + + +============================================================================== +18. LSP-Snippets *luasnip-lsp-snippets* + +LuaSnip is capable of parsing LSP-style snippets using +`ls.parser.parse_snippet(context, snippet_string, opts)`: + +>lua + ls.parser.parse_snippet({trig = "lsp"}, "$1 is ${2|hard,easy,challenging|}") +< + +`context` can be: - `string|table`: treated like the first argument to `ls.s`, +`parse_snippet` returns a snippet. - `number`: `parse_snippet` returns a +snippetNode, with the position `context`. - `nil`: `parse_snippet` returns a +flat table of nodes. This can be used like `fmt`. + +Nested placeholders(`"${1:this is ${2:nested}}"`) will be turned into +choiceNodes with: - the given snippet(`"this is ${1:nested}"`) and - an empty +insertNode + +This behaviour can be modified by changing `parser_nested_assembler` in +`ls.setup()`. + +LuaSnip will also modify some snippets that it is incapable of representing +accurately: + +- if the `$0` is a placeholder with something other than just text inside +- if the `$0` is a choice +- if the `$0` is not an immediate child of the snippet (it could be inside a + placeholder: `"${1: $0 }"`) + +To remedy those incompatibilities, the invalid `$0` will be replaced with a +tabstop/placeholder/choice which will be visited just before the new `$0`. This +new `$0` will be inserted at the (textually) earliest valid position behind the +invalid `$0`. + +`opts` can contain the following keys: - `trim_empty`: boolean, remove empty +lines from the snippet. Default true. - `dedent`: boolean, remove common indent +from the snippet’s lines. Default true. + +Both `trim_empty` and `dedent` will be disabled for snippets parsed via +`ls.lsp_expand`: it might prevent correct expansion of snippets sent by lsp. + + +SNIPMATE PARSER *luasnip-lsp-snippets-snipmate-parser* + +It is furthermore possible to parse SnipMate snippets (this includes support +for vimscript-evaluation!!) + +SnipMate snippets need to be parsed with a different function, +`ls.parser.parse_snipmate`: + +>lua + ls.parser.parse_snipmate("year", "The year is `strftime('%Y')`") +< + +`parse_snipmate` accepts the same arguments as `parse_snippet`, only the +snippet body is parsed differently. + + +TRANSFORMATIONS *luasnip-lsp-snippets-transformations* + +To apply Variable/Placeholder-transformations +, +luasnip needs to apply ECMAScript regexes. This is implemented by relying on +`jsregexp` . + +The easiest (but potentially error-prone) way to install it is by calling `make +install_jsregexp` in the repo root. + +This process can be automated by `packer.nvim`: + +>lua + use { "L3MON4D3/LuaSnip", run = "make install_jsregexp" } +< + +If this fails, first open an issue :P, and then try installing the +`jsregexp`-luarock. This is also possible via `packer.nvim`, although actual +usage may require a small workaround, see here + or here +. + +Alternatively, `jsregexp` can be cloned locally, `make`d, and the resulting +`jsregexp.so` placed in some place where nvim can find it (probably +`~/.config/nvim/lua/`). + +If `jsregexp` is not available, transformations are replaced by a simple copy. + + +============================================================================== +19. Variables *luasnip-variables* + +All `TM_something`-variables are supported with two additions: `LS_SELECT_RAW` +and `LS_SELECT_DEDENT`. These were introduced because `TM_SELECTED_TEXT` is +designed to be compatible with VSCode’s behavior, which can be +counterintuitive when the snippet can be expanded at places other than the +point where selection started (or when doing transformations on selected text). +Besides those we also provide `LS_TRIGGER` which contains the trigger of the +snippet, and `LS_CAPTURE_n` (where n is a positive integer) that contains the +n-th capture when using a regex with capture groups as `trig` in the snippet +definition. + +All variables can be used outside of lsp-parsed snippets as their values are +stored in a snippets’ `snip.env`-table: + +>lua + s("selected_text", f(function(args, snip) + local res, env = {}, snip.env + table.insert(res, "Selected Text (current line is " .. env.TM_LINE_NUMBER .. "):") + for _, ele in ipairs(env.LS_SELECT_RAW) do table.insert(res, ele) end + return res + end, {})) +< + +To use any `*SELECT*` variable, the `store_selection_keys` must be set via +`require("luasnip").config.setup({store_selection_keys=""})`. In this +case, hitting `` while in visual mode will populate the `*SELECT*`-vars +for the next snippet and then clear them. + + +ENVIRONMENT NAMESPACES *luasnip-variables-environment-namespaces* + +You can also add your own variables by using the `ls.env_namespace(name, opts)` +where: + +- `name`: `string` the names the namespace, can’t contain the character “_” +- `opts` is a table containing (in every case `EnvVal` is the same as `string|list[string]`: + - `vars`: `(fn(name:string)->EnvVal) | map[string, EnvVal]` + Is a function that receives a string and returns a value for the var with that name + or a table from var name to a value + (in this case, if the value is a function it will be executed lazily once per snippet expansion). + - `init`: `fn(info: table)->map[string, EnvVal]` Returns + a table of variables that will set to the environment of the snippet on expansion, + use this for vars that have to be calculated in that moment or that depend on each other. + The `info` table argument contains `pos` (0-based position of the cursor on expansion), + the `trigger` of the snippet and the `captures` list. + - `eager`: `list[string]` names of variables that will be taken from `vars` and appended eagerly (like those in init) + - `multiline_vars`: `(fn(name:string)->bool)|map[sting, bool]|bool|string[]` Says if certain vars are a table or just a string, + can be a function that get’s the name of the var and returns true if the var is a key, + a list of vars that are tables or a boolean for the full namespace, it’s false by default. Refer to + issue#510 for more information. + +The four fields of `opts` are optional but you need to provide either `init` or +`vars`, and `eager` can’t be without `vars`. Also, you can’t use namespaces +that override default vars. + +A simple example to make it more clear: + +>lua + local function random_lang() + return ({"LUA", "VIML", "VIML9"})[math.floor(math.random()/2 + 1.5)] + end + + ls.env_namespace("MY", {vars={ NAME="LuaSnip", LANG=random_lang }}) + + -- then you can use $MY_NAME and $MY_LANG in your snippets + + ls.env_namespace("SYS", {vars=os.getenv, eager={"HOME"}}) + + -- then you can use $SYS_HOME which was eagerly initialized but also $SYS_USER (or any other system environment var) in your snippets + + lsp.env_namespace("POS", {init=function(info) return {VAL=vim.inspect(info.pos)} end}) + + -- then you can use $POS_VAL in your snippets + + s("custom_env", d(1, function(args, parent) + local env = parent.snippet.env + return sn(nil, t { + "NAME: " .. env.MY_NAME, + "LANG: " .. env.MY_LANG, + "HOME: " .. env.SYS_HOME, + "USER: " .. env.SYS_USER, + "VAL: " .. env.POS_VAL + }) + end, {})) +< + + +LSP-VARIABLES *luasnip-variables-lsp-variables* + +All variables, even ones added via `env_namespace`, can be accessed in LSP +snippets as `$VAR_NAME`. + +The lsp-spec states: + +------------------------------------------------------------------------------ +With `$name` or `${name:default}` you can insert the value of a variable. When +a variable isn’t set, its default or the empty string is inserted. When a +variable is unknown (that is, its name isn’t defined) the name of the +variable is inserted and it is transformed into a placeholder. + +------------------------------------------------------------------------------ +The above necessiates a differentiation between `unknown` and `unset` +variables: + +For LuaSnip, a variable `VARNAME` is `unknown` when `env.VARNAME` returns `nil` +and `unset` if it returns an empty string. + +Consider this when adding env-variables which might be used in LSP snippets. + + +============================================================================== +20. Loaders *luasnip-loaders* + +Luasnip is capable of loading snippets from different formats, including both +the well-established VSCode and SnipMate format, as well as plain Lua files for +snippets written in Lua. + +All loaders (except the vscode-standalone-loader) share a similar interface: +`require("luasnip.loaders.from_{vscode,snipmate,lua}").{lazy_,}load(opts:table|nil)` + +where `opts` can contain the following keys: + +- `paths`: List of paths to load. Can be a table, or a single + comma-separated string. + The paths may begin with `~/` or `./` to indicate that the path is + relative to your `$HOME` or to the directory where your `$MYVIMRC` resides + (useful to add your snippets). + If not set, `runtimepath` is searched for + directories that contain snippets. This procedure differs slightly for + each loader: + - `lua`: the snippet-library has to be in a directory named + `"luasnippets"`. + - `snipmate`: similar to lua, but the directory has to be `"snippets"`. + - `vscode`: any directory in `runtimepath` that contains a + `package.json` contributing snippets. +- `lazy_paths`: behaves essentially like `paths`, with two exceptions: if it is + `nil`, it does not default to `runtimepath`, and the paths listed here do not + need to exist, and will be loaded on creation. + LuaSnip will do its best to determine the path that this should resolve to, + but since the resolving we do is not very sophisticated it may produce + incorrect paths. Definitely check the log if snippets are not loaded as + expected. +- `exclude`: List of languages to exclude, empty by default. +- `include`: List of languages to include, includes everything by default. +- `{override,default}_priority`: These keys are passed straight to the + `add_snippets`-calls (documented in |luasnip-api|) and can therefore change the + priority of snippets loaded from some colletion (or, in combination with + `{in,ex}clude`, only some of its snippets). +- `fs_event_providers`: `table?`, specifies which mechanisms + should be used to watch files for updates/creation. + If `autocmd` is set to `true`, a `BufWritePost`-hook watches files of this + collection, if `libuv` is set, the file-watcher-api exposed by libuv is used + to watch for updates. + Use `libuv` if you want snippets to update from other neovim-instances, and + `autocmd` if the collection resides on a filesystem where the libuv-watchers + may not work correctly. Or, of course, just enable both :D + By default, only `autocmd` is enabled. + +While `load` will immediately load the snippets, `lazy_load` will defer loading +until the snippets are actually needed (whenever a new buffer is created, or +the filetype is changed luasnip actually loads `lazy_load`ed snippets for the +filetypes associated with this buffer. This association can be changed by +customizing `load_ft_func` in `setup`: the option takes a function that, passed +a `bufnr`, returns the filetypes that should be loaded (`fn(bufnr) -> filetypes +(string[])`)). + +All of the loaders support reloading, so simply editing any file contributing +snippets will reload its snippets (according to `fs_event_providers` in the +instance where the file was edited, or in other instances as well). + +As an alternative (or addition) to automatic reloading, luasnip can also +process manual updates to files: Call +`require("luasnip.loaders").reload_file(path)` to reload the file at `path`. +This may be useful when the collection is controlled by some other plugin, or +when enabling the other reload-mechanisms is for some reason undesirable +(performance? minimalism?). + +For easy editing of these files, LuaSnip provides a `vim.ui.select`-based +dialog (|luasnip-loaders-edit_snippets|) where first the filetype, and then the +file can be selected. + + +SNIPPET-SPECIFIC FILETYPES *luasnip-loaders-snippet-specific-filetypes* + +Some loaders (vscode,lua) support giving snippets generated in some file their +own filetype (vscode via `scope`, lua via the underlying `filetype`-option for +snippets). These snippet-specific filetypes are not considered when determining +which files to `lazy_load` for some filetype, this is exclusively determined by +the `language` associated with a file in vscodes’ `package.json`, and the +file/directory-name in lua. + +- This can be resolved relatively easily in vscode, where the `language` + advertised in `package.json` can just be a superset of the `scope`s in the file. +- Another simplistic solution is to set the language to `all` (in lua, it might + make sense to create a directory `luasnippets/all/*.lua` to group these files + together). +- Another approach is to modify `load_ft_func` to load a custom filetype if the + snippets should be activated, and store the snippets in a file for that + filetype. This can be used to group snippets by e.g. framework, and load them + once a file belonging to such a framework is edited. + +**Example**: `react.lua` + +>lua + return { + s({filetype = "css", trig = ...}, ...), + s({filetype = "html", trig = ...}, ...), + s({filetype = "js", trig = ...}, ...), + } +< + +`luasnip_config.lua` + +>lua + load_ft_func = function(bufnr) + if "" then + -- will load `react.lua` for this buffer + return {"react"} + else + return require("luasnip.extras.filetype_functions").from_filetype_load + end + end +< + +See the |luasnip-troubleshooting-adding-snippets-loaders| section if one is +having issues adding snippets via loaders. + + +VS-CODE *luasnip-loaders-vs-code* + +As a reference on the structure of these snippet libraries, see +`friendly-snippets` . + +We support a small extension: snippets can contain LuaSnip-specific options in +the `luasnip`-table: + +>json + "example1": { + "prefix": "options", + "body": [ + "whoa! :O" + ], + "luasnip": { + "priority": 2000, + "autotrigger": true, + "wordTrig": false + } + } +< + +Files with the extension `jsonc` will be parsed as `jsonc`, json with comments +, while +`*.json` are parsed with a regular `json` parser, where comments are +disallowed. (the json-parser is a bit faster, so don’t default to `jsonc` if +it’s not necessary). + +**Example**: + +`~/.config/nvim/my_snippets/package.json`: + +>json + { + "name": "example-snippets", + "contributes": { + "snippets": [ + { + "language": [ + "all" + ], + "path": "./snippets/all.json" + }, + { + "language": [ + "lua" + ], + "path": "./lua.json" + } + ] + } + } +< + +`~/.config/nvim/my_snippets/snippets/all.json`: + +>json + { + "snip1": { + "prefix": "all1", + "body": [ + "expands? jumps? $1 $2 !" + ] + }, + "snip2": { + "prefix": "all2", + "body": [ + "multi $1", + "line $2", + "snippet$0" + ] + } + } +< + +`~/.config/nvim/my_snippets/lua.json`: + +>json + { + "snip1": { + "prefix": "lua", + "body": [ + "lualualua" + ] + } + } +< + +This collection can be loaded with any of + +>lua + -- don't pass any arguments, luasnip will find the collection because it is + -- (probably) in rtp. + require("luasnip.loaders.from_vscode").lazy_load() + -- specify the full path... + require("luasnip.loaders.from_vscode").lazy_load({paths = "~/.config/nvim/my_snippets"}) + -- or relative to the directory of $MYVIMRC + require("luasnip.loaders.from_vscode").load({paths = "./my_snippets"}) +< + + +STANDALONE ~ + +Beside snippet-libraries provided by packages, vscode also supports another +format which can be used for project-local snippets, or user-defined snippets, +`.code-snippets`. + +The layout of these files is almost identical to that of the package-provided +snippets, but there is one additional field supported in the +snippet-definitions, `scope`, with which the filetype of the snippet can be +set. If `scope` is not set, the snippet will be added to the global filetype +(`all`). + +`require("luasnip.loaders.from_vscode").load_standalone(opts)` + +- `opts`: `table`, can contain the following keys: + - `path`: `string`, Path to the `*.code-snippets`-file that should be loaded. + Just like the paths in `load`, this one can begin with a `"~/"` to be + relative to `$HOME`, and a `"./"` to be relative to the + neovim-config-directory. + - `{override,default}_priority`: These keys are passed straight to the + `add_snippets`-calls (documented in |luasnip-api|) and can be used to change + the priority of the loaded snippets. + - `lazy`: `boolean`, if it is set, the file does not have to exist when + `load_standalone` is called, and it will be loaded on creation. + `false` by default. + +**Example**: `a.code-snippets`: + +>jsonc + { + // a comment, since `.code-snippets` may contain jsonc. + "c/cpp-snippet": { + "prefix": [ + "trigger1", + "trigger2" + ], + "body": [ + "this is $1", + "my snippet $2" + ], + "description": "A description of the snippet.", + "scope": "c,cpp" + }, + "python-snippet": { + "prefix": "trig", + "body": [ + "this is $1", + "a different snippet $2" + ], + "description": "Another snippet-description.", + "scope": "python" + }, + "global snippet": { + "prefix": "trigg", + "body": [ + "this is $1", + "the last snippet $2" + ], + "description": "One last snippet-description.", + } + } +< + +This file can be loaded by calling + +>lua + require("luasnip.loaders.from_vscode").load_standalone({path = "a.code-snippets"}) +< + + +SNIPMATE *luasnip-loaders-snipmate* + +Luasnip does not support the full snipmate format: Only `./{ft}.snippets` and +`./{ft}/*.snippets` will be loaded. See honza/vim-snippets + for lots of examples. + +Like VSCode, the SnipMate format is also extended to make use of some of +LuaSnip’s more advanced capabilities: + +>snippets + priority 2000 + autosnippet options + whoa :O +< + +**Example**: + +`~/.config/nvim/snippets/c.snippets`: + +>snippets + # this is a comment + snippet c c-snippet + c! +< + +`~/.config/nvim/snippets/cpp.snippets`: + +>snippets + extends c + + snippet cpp cpp-snippet + cpp! +< + +This can, again, be loaded with any of + +>lua + require("luasnip.loaders.from_snipmate").load() + -- specify the full path... + require("luasnip.loaders.from_snipmate").lazy_load({paths = "~/.config/nvim/snippets"}) + -- or relative to the directory of $MYVIMRC + require("luasnip.loaders.from_snipmate").lazy_load({paths = "./snippets"}) +< + +Stuff to watch out for: + +- Using both `extends ` in `.snippets` and + `ls.filetype_extend("", {""})` leads to duplicate snippets. +- `${VISUAL}` will be replaced by `$TM_SELECTED_TEXT` to make the snippets + compatible with LuaSnip +- We do not implement eval using ` (backtick). This may be implemented in the + future. + + +LUA *luasnip-loaders-lua* + +Instead of adding all snippets via `add_snippets`, it’s possible to store +them in separate files and load all of those. The file-structure here is +exactly the supported snipmate-structure, e.g. `.lua` or `/*.lua` to +add snippets for the filetype ``. + +There are two ways to add snippets: + +- the files may return two lists of snippets, the snippets in the first are all + added as regular snippets, while the snippets in the second will be added as + autosnippets (both are the defaults, if a snippet defines a different + `snippetType`, that will have preference) +- snippets can also be appended to the global (only for these files - they are + not visible anywhere else) tables `ls_file_snippets` and + `ls_file_autosnippets`. This can be combined with a custom `snip_env` to define + and add snippets with one function call: + >lua + ls.setup({ + snip_env = { + s = function(...) + local snip = ls.s(...) + -- we can't just access the global `ls_file_snippets`, since it will be + -- resolved in the environment of the scope in which it was defined. + table.insert(getfenv(2).ls_file_snippets, snip) + end, + parse = function(...) + local snip = ls.parser.parse_snippet(...) + table.insert(getfenv(2).ls_file_snippets, snip) + end, + -- remaining definitions. + ... + }, + ... + }) + < + This is more flexible than the previous approach since the snippets don’t + have to be collected; they just have to be defined using the above `s` and + `parse`. + +As defining all of the snippet constructors (`s`, `c`, `t`, …) in every file +is rather cumbersome, LuaSnip will bring some globals into scope for executing +these files. By default, the names from `luasnip.config.snip_env` + +will be used, but it’s possible to customize them by setting `snip_env` in +`setup`. + +**Example**: + +`~/snippets/all.lua`: + +>lua + return { + s("trig", t("loaded!!")) + } +< + +`~/snippets/c.lua`: + +>lua + return { + s("ctrig", t("also loaded!!")) + }, { + s("autotrig", t("autotriggered, if enabled")) + } +< + +Load via + +>lua + require("luasnip.loaders.from_lua").load({paths = "~/snippets"}) +< + + +RELOADING WHEN EDITING REQUIRE’D FILES ~ + +While the lua-snippet-files will be reloaded on edit, this does not +automatically happen if a file the snippet-file depends on (eg. via `require`) +is changed. Since this still may still be desirable, there are two functions +exposed when a file is loaded by the lua-loader: `ls_tracked_dofile` and +`ls_tracked_dopackage`. They perform like `dofile` and (almost like) `require`, +but both register the loaded file internally as a dependency of the +snippet-file, so it can be reloaded when the loaded file is edited. As stated, +`ls_tracked_dofile` behaves exactly like `dofile`, but does the dependency-work +as well. `ls_tracked_dopackage` mimics `require` in that it does not take a +path, but a module-name like `"luasnip.loaders.from_lua"`, and then searches +the `runtimepath/lua`-directories, and path and cpath for the module. Unlike +`require`, the file will not be cached, since that would complicate the +reload-on-edit-behaviour. + + +EDIT_SNIPPETS *luasnip-loaders-edit_snippets* + +To easily edit snippets for the current session, the files loaded by any loader +can be quickly edited via +`require("luasnip.loaders").edit_snippet_files(opts:table|nil)` + +When called, it will open a `vim.ui.select`-dialog to select first a filetype, +and then (if there are multiple) the associated file to edit. + +`opts` contains four settings: + +- `ft_filter`: `fn(filetype:string) -> bool` Optionally filter initially listed + filetypes. `true` -> filetype will be listed, `false` -> not listed. Accepts + all filetypes by default. +- `format`: `fn(file:string, source_name:string) -> string|nil` `file` is simply + the path to the file, `source_name` is one of `"lua"`, `"snipmate"` or + `"vscode"`. If a string is returned, it is used as the title of the item, `nil` + on the other hand will filter out this item. The default simply replaces some + long strings (packer-path and config-path) in `file` with shorter, symbolic + names (`"$PLUGINS"`, `"$CONFIG"`), but this can be extended to + - filter files from some specific source/path + - more aggressively shorten paths using symbolic names, e.g. + `"$FRIENDLY_SNIPPETS"`. Example: hide the `*.lua` snippet files, and shorten + the path with `$LuaSnip`: + >lua + require "luasnip.loaders" .edit_snippet_files { + format = function(file, source_name) + if source_name == "lua" then return nil + else return file:gsub("/root/.config/nvim/luasnippets", "$LuaSnip") + end + end + } + < +- `edit`: `fn(file:string)` This function is supposed to open the file for + editing. The default is a simple `vim.cmd("edit " .. file)` (replace the + current buffer), but one could open the file in a split, a tab, or a floating + window, for example. +- `extend`: `fn(ft:string, ft_paths:string[]) -> (string,string)[]` This function + can be used to create additional choices for the file-selection. + - `ft`: The filetype snippet-files are queried for. + - `ft_paths`: list of paths to the known snippet files. + The function should return a list of `(string,string)`-tuples. The first of + each pair is the label that will appear in the selection-prompt, and the second + is the path that will be passed to the `edit()` function if that item was + selected. + This can be used to create a new snippet file for the current filetype: + >lua + require("luasnip.loaders").edit_snippet_files { + extend = function(ft, paths) + if #paths == 0 then + return { + { "$CONFIG/" .. ft .. ".snippets", + string.format("%s/%s.snippets", , ft) } + } + end + + return {} + end + } + < + +One comfortable way to call this function is registering it as a command: + +>vim + command! LuaSnipEdit :lua require("luasnip.loaders").edit_snippet_files() +< + + +============================================================================== +21. SnippetProxy *luasnip-snippetproxy* + +`SnippetProxy` is used internally to alleviate the upfront cost of loading +snippets from e.g. a SnipMate library or a VSCode package. This is achieved by +only parsing the snippet on expansion, not immediately after reading it from +some file. `SnippetProxy` may also be used from Lua directly to get the same +benefits: + +This will parse the snippet on startup: + +>lua + ls.parser.parse_snippet("trig", "a snippet $1!") +< + +while this will parse the snippet upon expansion: + +>lua + local sp = require("luasnip.nodes.snippetProxy") + sp("trig", "a snippet $1") +< + +`sp(context, body, opts) -> snippetProxy` + +- `context`: exactly the same as the first argument passed to `ls.s`. +- `body`: the snippet body. +- `opts`: accepts the same `opts` as `ls.s`, with some additions: + - `parse_fn`: the function for parsing the snippet. Defaults to + `ls.parser.parse_snippet` (the parser for LSP snippets), an alternative is + the parser for SnipMate snippets (`ls.parser.parse_snipmate`). + + +============================================================================== +22. ext_opts *luasnip-ext_opts* + +`ext_opts` can be used to set the `opts` (see `nvim_buf_set_extmark`) of the +extmarks used for marking node positions, either globally, per snippet or per +node. This means that they allow highlighting the text inside of nodes, or +adding virtual text to the line the node begins on. + +This is an example for the `node_ext_opts` used to set `ext_opts` of single +nodes: + +>lua + local ext_opts = { + -- these ext_opts are applied when the node is active (e.g. it has been + -- jumped into, and not out yet). + active = + -- this is the table actually passed to `nvim_buf_set_extmark`. + { + -- highlight the text inside the node red. + hl_group = "GruvboxRed" + }, + -- these ext_opts are applied when the node is not active, but + -- the snippet still is. + passive = { + -- add virtual text on the line of the node, behind all text. + virt_text = {{"virtual text!!", "GruvboxBlue"}} + }, + -- visited or unvisited are applied when a node was/was not jumped into. + visited = { + hl_group = "GruvboxBlue" + }, + unvisited = { + hl_group = "GruvboxGreen" + }, + -- and these are applied when both the node and the snippet are inactive. + snippet_passive = {} + } + + s("trig", { + i(1, "text1", { + node_ext_opts = ext_opts + }), + i(2, "text2", { + node_ext_opts = ext_opts + }) + }) +< + +In the above example, the text inside the insertNodes is higlighted in green if +they were not yet visited, in blue once they were, and red while they are. The +virtual text "virtual text!!" is visible as long as the snippet is active. + +To make defining `ext_opts` less verbose, more specific states inherit from +less specific ones: + +- `passive` inherits from `snippet_passive` +- `visited` and `unvisited` from `passive` +- `active` from `visited` + +To disable a key from a less specific state, it has to be explicitly set to its +default, e.g. to disable highlighting inherited from `passive` when the node +is `active`, `hl_group` should be set to `None`. + +------------------------------------------------------------------------------ +As stated earlier, these `ext_opts` can also be applied globally or for an +entire snippet. For this, it’s necessary to specify which kind of node a +given set of `ext_opts` should be applied to: + +>lua + local types = require("luasnip.util.types") + + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = {...}, + visited = {...}, + passive = {...}, + snippet_passive = {...} + }, + [types.choiceNode] = { + active = {...}, + unvisited = {...} + }, + [types.snippet] = { + passive = {...} + } + } + }) +< + +The above applies the given `ext_opts` to all nodes of these types, in all +snippets. + +>lua + local types = require("luasnip.util.types") + + s("trig", { i(1, "text1"), i(2, "text2") }, { + child_ext_opts = { + [types.insertNode] = { + passive = { + hl_group = "GruvboxAqua" + } + } + } + }) +< + +However, the `ext_opts` here are only applied to the `insertNodes` inside this +snippet. + +------------------------------------------------------------------------------ +By default, the `ext_opts` actually used for a node are created by extending +the `node_ext_opts` with the `effective_child_ext_opts[node.type]` of the +parent, which are in turn the parent’s `child_ext_opts` extended with the +global `ext_opts` (those set `ls.setup`). + +It’s possible to prevent both of these merges by passing +`merge_node/child_ext_opts=false` to the snippet/node-opts: + +>lua + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = {...} + } + } + }) + + s("trig", { + i(1, "text1", { + node_ext_opts = { + active = {...} + }, + merge_node_ext_opts = false + }), + i(2, "text2") + }, { + child_ext_opts = { + [types.insertNode] = { + passive = {...} + } + }, + merge_child_ext_opts = false + }) +< + +------------------------------------------------------------------------------ +The `hl_group` of the global `ext_opts` can also be set via standard highlight +groups: + +>lua + vim.cmd("hi link LuasnipInsertNodePassive GruvboxRed") + vim.cmd("hi link LuasnipSnippetPassive GruvboxBlue") + + -- needs to be called for resolving the effective ext_opts. + ls.setup({}) +< + +The names for the used highlight groups are +`"Luasnip{Passive,Active,SnippetPassive}"`, where `` can be any +kind of node in PascalCase (or "Snippet"). + +------------------------------------------------------------------------------ +One problem that might arise when nested nodes are highlighted is that the +highlight of inner nodes should be visible, e.g. above that of nodes they are +nested inside. + +This can be controlled using the `priority`-key in `ext_opts`. In +`nvim_buf_set_extmark`, that value is an absolute value, but here it is +relative to some base-priority, which is increased for each nesting level of +snippet(Nodes)s. + +Both the initial base-priority and its’ increase and can be controlled using +`ext_base_prio` and `ext_prio_increase`: + +>lua + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = { + hl_group = "GruvboxBlue", + -- the priorities should be \in [0, ext_prio_increase). + priority = 1 + } + }, + [types.choiceNode] = { + active = { + hl_group = "GruvboxRed" + -- priority defaults to 0 + } + } + } + ext_base_prio = 200, + ext_prio_increase = 2 + }) +< + +Here the highlight of an insertNode nested directly inside a choiceNode is +always visible on top of it. + + +============================================================================== +23. Docstrings *luasnip-docstrings* + +Snippet docstrings can be queried using `snippet:get_docstring()`. The function +evaluates the snippet as if it was expanded regularly, which can be problematic +if e.g. a dynamicNode in the snippet relies on inputs other than the argument +nodes. `snip.env` and `snip.captures` are populated with the names of the +queried variable and the index of the capture respectively +(`snip.env.TM_SELECTED_TEXT` -> `'$TM_SELECTED_TEXT'`, `snip.captures[1]` -> +`'$CAPTURES1'`). Although this leads to more expressive docstrings, it can +cause errors in functions that e.g. rely on a capture being a number: + +>lua + s({trig = "(%d)", regTrig = true}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +This snippet works fine because `snippet.captures[1]` is always a number. +During docstring generation, however, `snippet.captures[1]` is `'$CAPTURES1'`, +which will cause an error in the functionNode. Issues with `snippet.captures` +can be prevented by specifying `docTrig` during snippet-definition: + +>lua + s({trig = "(%d)", regTrig = true, docTrig = "3"}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +`snippet.captures` and `snippet.trigger` will be populated as if actually +triggered with `3`. + +Other issues will have to be handled manually by checking the contents of e.g. +`snip.env` or predefining the docstring for the snippet: + +>lua + s({trig = "(%d)", regTrig = true, docstring = "repeatmerepeatmerepeatme"}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +Refer to #515 for a better +example to understand `docTrig` and `docstring`. + + +============================================================================== +24. Docstring-Cache *luasnip-docstring-cache* + +Although generation of docstrings is pretty fast, it’s preferable to not redo +it as long as the snippets haven’t changed. Using +`ls.store_snippet_docstrings(snippets)` and its counterpart +`ls.load_snippet_docstrings(snippets)`, they may be serialized from or +deserialized into the snippets. Both functions accept a table structsured like +this: `{ft1={snippets}, ft2={snippets}}`. Such a table containing all snippets +can be obtained via `ls.get_snippets()`. `load` should be called before any of +the `loader`-functions as snippets loaded from VSCode style packages already +have their `docstring` set (`docstrings` wouldn’t be overwritten, but +there’d be unnecessary calls). + +The cache is located at `stdpath("cache")/luasnip/docstrings.json` (probably +`~/.cache/nvim/luasnip/docstrings.json`). + + +============================================================================== +25. Events *luasnip-events* + +Events can be used to react to some action inside snippets. These callbacks can +be defined per snippet (`callbacks`-key in snippet constructor), per-node by +passing them as `node_callbacks` in `node_opts`, or globally (autocommand). + +`callbacks`: `fn(node[, event_args]) -> event_res` All callbacks receive the +`node` associated with the event and event-specific optional arguments, +`event_args`. `event_res` is only used in one event, `pre_expand`, where some +properties of the snippet can be changed. If multiple callbacks return +`event_res`, we only guarantee that one of them will be effective, not all of +them. + +`autocommand`: Luasnip uses `User`-events. Autocommands for these can be +registered using + +>vim + au User SomeUserEvent echom "SomeUserEvent was triggered" +< + +or + +>lua + vim.api.nvim_create_autocommand("User", { + patter = "SomeUserEvent", + command = "echom SomeUserEvent was triggered" + }) +< + +The node and `event_args` can be accessed through `require("luasnip").session`: + +- `node`: `session.event_node` +- `event_args`: `session.event_args` + +**Events**: + +- `enter/leave`: Called when a node is entered/left (for example when jumping + around in a snippet). + `User-event`: `"Luasnip{Enter,Leave}"`, with `` in + PascalCase, e.g. `InsertNode` or `DynamicNode`. + `event_args`: none +- `change_choice`: When the active choice in a choiceNode is changed. + `User-event`: `"LuasnipChangeChoice"` + `event_args`: none +- `pre_expand`: Called before a snippet is expanded. Modifying text is allowed, + the expand-position will be adjusted so the snippet expands at the same + position relative to existing text. + `User-event`: `"LuasnipPreExpand"` + `event_args`: + - `expand_pos`: `{, }`, position at which the snippet will be + expanded. `` and `` are both 0-indexed. + `event_res`: + - `env_override`: `map string->(string[]|string)`, override or extend the + snippet’s environment (`snip.env`). + +A pretty useless, beyond serving as an example here, application of these would +be printing e.g. the node’s text after entering: + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipInsertNodeEnter", + callback = function() + local node = require("luasnip").session.event_node + print(table.concat(node:get_text(), "\n")) + end + }) +< + +or some information about expansions: + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipPreExpand", + callback = function() + -- get event-parameters from `session`. + local snippet = require("luasnip").session.event_node + local expand_position = + require("luasnip").session.event_args.expand_pos + + print(string.format("expanding snippet %s at %s:%s", + table.concat(snippet:get_docstring(), "\n"), + expand_position[1], + expand_position[2] + )) + end + }) +< + + +============================================================================== +26. Cleanup *luasnip-cleanup* + +The function ls.cleanup() triggers the `LuasnipCleanup` user event, that you +can listen to do some kind of cleaning in your own snippets; by default it will +empty the snippets table and the caches of the lazy_load. + + +============================================================================== +27. Logging *luasnip-logging* + +Luasnip uses logging to report unexpected program states, and information on +what’s going on in general. If something does not work as expected, taking a +look at the log (and potentially increasing the loglevel) might give some good +hints towards what is going wrong. + +The log is stored in `/luasnip.log` +(`/luasnip.log` for Neovim versions where +`stdpath("log")` does not exist), and can be opened by calling `ls.log.open()`. +The loglevel (granularity of reported events) can be adjusted by calling +`ls.log.set_loglevel("error"|"warn"|"info"|"debug")`. `"debug"` has the highest +granularity, `"error"` the lowest, the default is `"warn"`. + +Once this log grows too large (10MiB, currently not adjustable), it will be +renamed to `luasnip.log.old`, and a new, empty log created in its place. If +there already exists a `luasnip.log.old`, it will be deleted. + +`ls.log.ping()` can be used to verify the log is working correctly: it will +print a short message to the log. + + +============================================================================== +28. Source *luasnip-source* + +It is possible to attach, to a snippet, information about its source. This can +be done either by the various loaders (if it is enabled in `ls.setup` +(|luasnip-config-options|, `loaders_store_source`)), or manually. The attached +data can be used by |luasnip-extras-snippet-location| to jump to the definition +of a snippet. + +It is also possible to get/set the source of a snippet via API: + +`ls.snippet_source`: + +- `get(snippet) -> source_data`: + Retrieve the source-data of `snippet`. `source_data` always contains the key + `file`, the file in which the snippet was defined, and may additionally + contain `line` or `line_end`, the first and last line of the definition. +- `set(snippet, source)`: + Set the source of a snippet. + - `snippet`: a snippet which was added via `ls.add_snippets`. + - `source`: a `source`-object, obtained from either `from_debuginfo` or + `from_location`. +- `from_location(file, opts) -> source`: + - `file`: `string`, The path to the file in which the snippet is defined. + - `opts`: `table|nil`, optional parameters for the source. + - `line`: `number`, the first line of the definition. 1-indexed. + - `line_end`: `number`, the final line of the definition. 1-indexed. +- `from_debuginfo(debuginfo) -> source`: + Generates source from the table returned by `debug.getinfo` (from now on + referred to as `debuginfo`). `debuginfo` has to be of a frame of a function + which is backed by a file, and has to contain this information, ie. has to be + generated by `debug.get_info(*, "Sl")` (at least `"Sl"`, it may also contain + more info). + + +============================================================================== +29. Config-Options *luasnip-config-options* + +These are the settings you can provide to `luasnip.setup()`: + +- `keep_roots`: Whether snippet-roots should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `link_roots`: Whether snippet-roots should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `exit_roots`: Whether snippet-roots should exit at reaching at their last node, + `$0`. This setting is only valid for root snippets, not child snippets. This + setting may avoid unexpected behavior by disallowing to jump earlier (finished) + snippets. Check |luasnip-basics-snippet-insertion| for more information on + snippet-roots. +- `link_children`: Whether children should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `history` (deprecated): if not nil, `keep_roots`, `link_roots`, and + `link_children` will be set to the value of `history`, and `exit_roots` will + set to inverse value of `history`. This is just to ensure + backwards-compatibility. +- `update_events`: Choose which events trigger an update of the active nodes’ + dependents. Default is just `'InsertLeave'`, `'TextChanged,TextChangedI'` would + update on every change. These, like all other `*_events` are passed to + `nvim_create_autocmd` as `events`, so they can be wrapped in a table, like + >lua + ls.setup({ + update_events = {"TextChanged", "TextChangedI"} + }) + < +- `region_check_events`: Events on which to leave the current snippet-root if the + cursor is outside its’ 'region'. Disabled by default, `'CursorMoved'`, + `'CursorHold'` or `'InsertEnter'` seem reasonable. +- `delete_check_events`: When to check if the current snippet was deleted, and if + so, remove it from the history. Off by default, `'TextChanged'` (perhaps + `'InsertLeave'`, to react to changes done in Insert mode) should work just fine + (alternatively, this can also be mapped using `luasnip-delete-check`). +- `store_selection_keys`: Mapping for populating `TM_SELECTED_TEXT` and related + variables (not set by default). If you want to set this mapping yourself, map + `ls.select_keys` (not a function, actually a string/key-combination) as a rhs. +- `enable_autosnippets`: Autosnippets are disabled by default to minimize + performance penalty if unused. Set to `true` to enable. +- `ext_opts`: Additional options passed to extmarks. Can be used to add + passive/active highlight on a per-node-basis (more info in DOC.md) +- `parser_nested_assembler`: Override the default behaviour of inserting a + `choiceNode` containing the nested snippet and an empty `insertNode` for nested + placeholders (`"${1: ${2: this is nested}}"`). For an example (behaviour more + similar to vscode), check here + +- `ft_func`: Source of possible filetypes for snippets. Defaults to a function, + which returns `vim.split(vim.bo.filetype, ".", true)`, but check + filetype_functions or the + |luasnip-extras-filetype-functions|-section for more options. +- `load_ft_func`: Function to determine which filetypes belong to a given buffer + (used for `lazy_loading`). `fn(bufnr) -> filetypes (string[])`. Again, there + are some examples in filetype_functions + . +- `snip_env`: The best way to author snippets in lua involves the lua-loader (see + |luasnip-loaders-lua|). Unfortunately, this requires that snippets are defined + in separate files, which means that common definitions like `s`, `i`, `sn`, + `t`, `fmt`, … have to be repeated in each of them, and that adding more + customized functions to ease writing snippets also requires some setup. + `snip_env` can be used to insert variables into exactly the places where + lua-snippets are defined (for now only the file loaded by the lua-loader). + Setting `snip_env` to `{ some_global = "a value" }` will add (amongst the + defaults stated at the beginning of this documentation) the global variable + `some_global` while evaluating these files. There are special keys which, when + set in `snip_env` change the behaviour of this option, and are not passed + through to the lua-files: + - `__snip_env_behaviour`, string: either `"set"` or `"extend"` (default + `"extend"`) + If this is `"extend"`, the variables defined in `snip_env` will complement (and + override) the defaults. If this is not desired, `"set"` will not include the + defaults, but only the variables set here. + One side-effect of this is that analysis-tools (most likely + `lua-language-server`) for lua will generate diagnostics for the usage of + undefined symbols. If you mind the (probably) large number of generated + warnings, consider adding the undefined globals to the globals recognized by + `lua-language-server` or add `---@diagnostic disable: undefined-global` + somewhere in the affected files. +- `loaders_store_source`, boolean, whether loaders should store the source of the + loaded snippets. Enabling this means that the definition of any snippet can be + jumped to via |luasnip-extras-snippet-location|, but also entails slightly + increased memory consumption (and load-time, but it’s not really noticeable). + + +============================================================================== +30. Troubleshooting *luasnip-troubleshooting* + + +ADDING SNIPPETS *luasnip-troubleshooting-adding-snippets* + +### Loaders + +- **Filetypes**. LuaSnip uses `all` as the global filetype. As most snippet + collections don’t explicitly target LuaSnip, they may not provide global + snippets for this filetype, but another, like `_` (`honza/vim-snippets`). In + these cases, it’s necessary to extend LuaSnip’s global filetype with the + collection’s global filetype: + >lua + ls.filetype_extend("all", { "_" }) + < + In general, if some snippets don’t show up when loading a collection, a good + first step is checking the filetype LuaSnip is actually looking into (print + them for the current buffer via `:lua + print(vim.inspect(require("luasnip").get_snippet_filetypes()))`), against the + one the missing snippet is provided for (in the collection). If there is indeed + a mismatch, `filetype_extend` can be used to also search the collection’s + filetype: + >lua + ls.filetype_extend("", { "" }) + < +- **Non-default ft_func loading**. As we only load `lazy_load`ed snippets on some + events, `lazy_load` will probably not play nice when a non-default `ft_func` is + used: if it depends on e.g. the cursor position, only the filetypes for the + cursor position when the `lazy_load` events are triggered will be loaded. Check + |luasnip-extras-filetype-function|’s `extend_load_ft` for a solution. + + +GENERAL ~ + +- **Snippets sharing triggers**. If multiple snippets could be triggered at the + current buffer-position, the snippet that was defined first in one’s + configuration will be expanded first. As a small, real-world LaTeX math + example, given the following two snippets with triggers `.ov` and `ov`: + >lua + postfix( -- Insert over-line command to text via post-fix + { trig = ".ov", snippetType = "autosnippet" }, + { + f(function(_, parent) + return "\\overline{" .. parent.snippet.env.POSTFIX_MATCH .. "}" + end, {}), + } + ), + s( -- Insert over-line command + { trig = "ov", snippetType="autosnippet" }, + fmt( + [[\overline{<>}]], + { i(1) }, + { delimiters = "<>" } + ) + ), + < + If one types `x` followed by `.ov`, the postfix snippet expands producing + `\overline{x}`. However, if the `postfix` snippet above is defined _after_ the + normal snippet `s`, then the same key press sequence produces `x.\overline{}`. + This behaviour can be overridden by explicitly providing a priority to such + snippets. For example, in the above code, if the `postfix` snippet was defined + after the normal snippet `s`, then adding `priority=1001` to the `postfix` + snippet will cause it to expand as if it were defined before the normal snippet + `s`. Snippet `priority` is discussed in the Snippets section + of the + documentation. + + +============================================================================== +31. API *luasnip-api* + +`require("luasnip")`: + +- `add_snippets(ft:string or nil, snippets:list or table, opts:table or nil)`: + Makes `snippets` (list of snippets) available in `ft`. If `ft` is `nil`, + `snippets` should be a table containing lists of snippets, the keys are + corresponding filetypes. `opts` may contain the following keys: + - `type`: type of `snippets`, `"snippets"` or `"autosnippets"` (ATTENTION: + plural form used here). This serves as default value for the `snippetType` + key of each snippet added by this call see |luasnip-snippets|. + - `key`: Key that identifies snippets added via this call. + If `add_snippets` is called with a key that was already used, the snippets + from that previous call will be removed. + This can be used to reload snippets: pass an unique key to each + `add_snippets` and just redo the `add_snippets`-call when the snippets have + changed. + - `override_priority`: set priority for all snippets. + - `default_priority`: set priority only for snippets without snippet priority. +- `clean_invalidated(opts: table or nil) -> bool`: clean invalidated snippets + from internal snippet storage. Invalidated snippets are still stored; it might + be useful to actually remove them as they still have to be iterated during + expansion. + `opts` may contain: + - `inv_limit`: how many invalidated snippets are allowed. If the number of + invalid snippets doesn’t exceed this threshold, they are not yet cleaned up. + A small number of invalidated snippets (<100) probably doesn’t affect runtime + at all, whereas recreating the internal snippet storage might. +- `get_id_snippet(id)`: returns snippet corresponding to id. +- `in_snippet()`: returns true if the cursor is inside the current snippet. +- `jumpable(direction)`: returns true if the current node has a next(`direction` + = 1) or previous(`direction` = -1), e.g. whether it’s possible to jump + forward or backward to another node. +- `jump(direction)`: returns true if the jump was successful. +- `expandable()`: true if a snippet can be expanded at the current cursor + position. +- `expand(opts)`: expands the snippet at(before) the cursor. `opts` may contain: + - `jump_into_func` passed through to `ls.snip_expand`, check its’ doc for a + description. +- `expand_or_jumpable()`: returns `expandable() or jumpable(1)` (exists only + because commonly, one key is used to both jump forward and expand). +- `expand_or_locally_jumpable()`: same as `expand_or_jumpable()` except jumpable + is ignored if the cursor is not inside the current snippet. +- `locally_jumpable(direction)`: same as `jumpable()` except it is ignored if the + cursor is not inside the current snippet. +- `expand_or_jump()`: returns true if jump/expand was succesful. +- `expand_auto()`: expands the autosnippets before the cursor (not necessary to + call manually, will be called via autocmd if `enable_autosnippets` is set in + the config). +- `snip_expand(snip, opts)`: expand `snip` at the current cursor position. `opts` + may contain the following keys: + - `clear_region`: A region of text to clear after expanding (but before jumping + into) snip. It has to be at this point (and therefore passed to this function) + as clearing before expansion will populate `TM_CURRENT_LINE` and + `TM_CURRENT_WORD` with wrong values (they would miss the snippet trigger) and + clearing after expansion may move the text currently under the cursor and have + it end up not at the `i(1)`, but a `#trigger` chars to its right. The actual + values used for clearing are `from` and `to`, both (0,0)-indexed + byte-positions. If the variables don’t have to be populated with the correct + values, it’s safe to remove the text manually. + - `expand_params`: table, override `trigger`, `captures` or environment of the + snippet. This is useful for manually expanding snippets where the trigger + passed via `trig` is not the text triggering the snippet, or those which expect + `captures` (basically, snippets with a non-plaintext `trigEngine`). + One example: ```lua snip_expand(snip, { trigger = "override_trigger", captures + = {"first capture", "second capture"}, env_override = { this_key = "some + value", other_key = {"multiple", "lines"}, TM_FILENAME = + "some_other_filename.lua" } }) + - `pos`: position (`{line, col}`), (0,0)-indexed (in bytes, as returned by + `nvim_win_get_cursor()`), where the snippet should be expanded. The snippet + will be put between `(line,col-1)` and `(line,col)`. The snippet will be + expanded at the current cursor if pos is nil. + - `jump_into_func`: fn(snippet) -> node: Callback responsible for jumping into + the snippet. The returned node is set as the new active node, ie. it is the + origin of the next jump. The default is basically this: `lua function(snip) -- + jump_into set the placeholder of the snippet, 1 -- to jump forwards. return + snip:jump_into(1)` while this can be used to only insert the snippet: `lua + function(snip) return snip.insert_nodes[0] end` + - `indent`: bool?, defaults to `true`. Whether LuaSnip will try to add additional + indents to fit current indent level in snippet expanding. This option is useful + when some LSP server already take indents into consideration. In such cases, + LuaSnip should not try to add additional indents. If you are using `nvim-cmp`, + sample config: + >lua + require("cmp").setup { + snippet = { + expand = function(args) + local indent_nodes = true + if vim.api.nvim_get_option_value("filetype", { buf = 0 }) == "dart" then + indent_nodes = false + end + require("luasnip").lsp_expand(args.body, { + indent = indent_nodes, + }) + end, + }, + } + < + `opts` and any of its parameters may be nil. +- `get_active_snip()`: returns the currently active snippet (not node!). +- `choice_active()`: true if inside a choiceNode. +- `change_choice(direction)`: changes the choice in the innermost currently + active choiceNode forward (`direction` = 1) or backward (`direction` = -1). +- `unlink_current()`: removes the current snippet from the jumplist (useful if + luasnip fails to automatically detect e.g. deletion of a snippet) and sets the + current node behind the snippet, or, if not possible, before it. +- `lsp_expand(snip_string, opts)`: expands the LSP snippet defined via + `snip_string` at the cursor. `opts` can have the same options as `opts` in + `snip_expand`. +- `active_update_dependents()`: update all function/dynamicNodes that have the + current node as an argnode (will actually only update them if the text in any + of the argnodes changed). +- `available(snip_info)`: returns a table of all snippets defined for the current + filetypes(s) (`{ft1={snip1, snip2}, ft2={snip3, snip4}}`). The structure of the + snippet is defined by `snip_info` which is a function (`snip_info(snip)`) that + takes in a snippet (`snip`), finds the desired information on it, and returns + it. `snip_info` is an optional argument as a default has already been defined. + You can use it for more granular control over the table of snippets that is + returned. +- `exit_out_of_region(node)`: checks whether the cursor is still within the range + of the root-snippet `node` belongs to. If yes, no change occurs; if no, the + root-snippet is exited and its `$0` will be the new active node. If a jump + causes an error (happens mostly because the text of a snippet was deleted), the + snippet is removed from the jumplist and the current node set to the + end/beginning of the next/previous snippet. +- `store_snippet_docstrings(snippet_table)`: Stores the docstrings of all + snippets in `snippet_table` to a file + (`stdpath("cache")/luasnip/docstrings.json`). Calling + `store_snippet_docstrings(snippet_table)` after adding/modifying snippets and + `load_snippet_docstrings(snippet_table)` on startup after all snippets have + been added to `snippet_table` is a way to avoide regenerating the (unchanged) + docstrings on each startup. (Depending on when the docstrings are required and + how luasnip is loaded, it may be more sensible to let them load lazily, + e.g. just before they are required). `snippet_table` should be laid out just + like `luasnip.snippets` (it will most likely always _be_ `luasnip.snippets`). +- `load_snippet_docstrings(snippet_table)`: Load docstrings for all snippets in + `snippet_table` from `stdpath("cache")/luasnip/docstrings.json`. The docstrings + are stored and restored via trigger, meaning if two snippets for one filetype + have the same (very unlikely to happen in actual usage), bugs could occur. + `snippet_table` should be laid out as described in `store_snippet_docstrings`. +- `unlink_current_if_deleted()`: Checks if the current snippet was deleted; if + so, it is removed from the jumplist. This is not 100% reliable as LuaSnip only + sees the extmarks and their beginning/end may not be on the same position, even + if all the text between them was deleted. +- `filetype_extend(filetype:string, extend_filetypes:table of string)`: Tells + luasnip that for a buffer with `ft=filetype`, snippets from `extend_filetypes` + should be searched as well. `extend_filetypes` is a lua-array (`{ft1, ft2, + ft3}`). `luasnip.filetype_extend("lua", {"c", "cpp"})` would search and expand + c and cpp snippets for lua files. +- `filetype_set(filetype:string, replace_filetypes:table of string)`: Similar to + `filetype_extend`, but where _append_ appended filetypes, _set_ sets them: + `filetype_set("lua", {"c"})` causes only c snippets to be expanded in lua + files; lua snippets aren’t even searched. +- `cleanup()`: clears all snippets. Not useful for regular usage, only when + authoring and testing snippets. +- `refresh_notify(ft:string)`: Triggers an autocmd that other plugins can hook + into to perform various cleanup for the refreshed filetype. Useful for + signaling that new snippets were added for the filetype `ft`. +- `set_choice(indx:number)`: Changes to the `indx`th choice. If no `choiceNode` + is active, an error is thrown. If the active `choiceNode` doesn’t have an + `indx`th choice, an error is thrown. +- `get_current_choices() -> string[]`: Returns a list of multiline-strings + (themselves lists, even if they have only one line), the `i`th string + corresponding to the `i`th choice of the currently active `choiceNode`. If no + `choiceNode` is active, an error is thrown. +- `setup_snip_env()`: Adds the variables defined (during `setup`) in `snip_env` + to the callers environment. +- `get_snip_env()`: Returns `snip_env`. +- `jump_destination(direction)`: Returns the node the next jump in `direction` + (either -1 or 1, for backwards, forwards respectively) leads to, or `nil` if + the destination could not be determined (most likely because there is no node + that can be jumped to in the given direction, or there is no active node). +- `activate_node(opts)`: Activate a node in any snippet. `opts` contains the + following options: + - `pos`, `{[1]: row, [2]: byte-column}?`: The position at which a node should + be activated. Defaults to the position of the cursor. + - `strict`, `bool?`: If set, throw an error if the node under the cursor can’t + be jumped into. If not set, fall back to any node of the snippet and enter + that instead. + - `select`, `bool?`: Whether the text inside the node should be selected. + Defaults to true. + +Not covered in this section are the various node-constructors exposed by the +module, their usage is shown either previously in this file or in +`Examples/snippets.lua` (in the repo). + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/doc/tags b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/doc/tags new file mode 100644 index 00000000..3c6f662a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/doc/tags @@ -0,0 +1,63 @@ +luasnip-absolute-indexer luasnip.txt /*luasnip-absolute-indexer* +luasnip-api luasnip.txt /*luasnip-api* +luasnip-basics luasnip.txt /*luasnip-basics* +luasnip-basics-adding-snippets luasnip.txt /*luasnip-basics-adding-snippets* +luasnip-basics-jump-index luasnip.txt /*luasnip-basics-jump-index* +luasnip-basics-snippet-insertion luasnip.txt /*luasnip-basics-snippet-insertion* +luasnip-choicenode luasnip.txt /*luasnip-choicenode* +luasnip-cleanup luasnip.txt /*luasnip-cleanup* +luasnip-config-options luasnip.txt /*luasnip-config-options* +luasnip-docstring-cache luasnip.txt /*luasnip-docstring-cache* +luasnip-docstrings luasnip.txt /*luasnip-docstrings* +luasnip-dynamicnode luasnip.txt /*luasnip-dynamicnode* +luasnip-events luasnip.txt /*luasnip-events* +luasnip-ext_opts luasnip.txt /*luasnip-ext_opts* +luasnip-extend-decorator luasnip.txt /*luasnip-extend-decorator* +luasnip-extras luasnip.txt /*luasnip-extras* +luasnip-extras-conditions luasnip.txt /*luasnip-extras-conditions* +luasnip-extras-dynamic-lambda luasnip.txt /*luasnip-extras-dynamic-lambda* +luasnip-extras-filetype-functions luasnip.txt /*luasnip-extras-filetype-functions* +luasnip-extras-fmt luasnip.txt /*luasnip-extras-fmt* +luasnip-extras-lambda luasnip.txt /*luasnip-extras-lambda* +luasnip-extras-match luasnip.txt /*luasnip-extras-match* +luasnip-extras-nonempty luasnip.txt /*luasnip-extras-nonempty* +luasnip-extras-on-the-fly-snippets luasnip.txt /*luasnip-extras-on-the-fly-snippets* +luasnip-extras-partial luasnip.txt /*luasnip-extras-partial* +luasnip-extras-postfix-snippet luasnip.txt /*luasnip-extras-postfix-snippet* +luasnip-extras-repeat luasnip.txt /*luasnip-extras-repeat* +luasnip-extras-select_choice luasnip.txt /*luasnip-extras-select_choice* +luasnip-extras-snippet-list luasnip.txt /*luasnip-extras-snippet-list* +luasnip-extras-snippet-location luasnip.txt /*luasnip-extras-snippet-location* +luasnip-extras-treesitter-postfix-snippet luasnip.txt /*luasnip-extras-treesitter-postfix-snippet* +luasnip-functionnode luasnip.txt /*luasnip-functionnode* +luasnip-indentsnippetnode luasnip.txt /*luasnip-indentsnippetnode* +luasnip-insertnode luasnip.txt /*luasnip-insertnode* +luasnip-key-indexer luasnip.txt /*luasnip-key-indexer* +luasnip-loaders luasnip.txt /*luasnip-loaders* +luasnip-loaders-edit_snippets luasnip.txt /*luasnip-loaders-edit_snippets* +luasnip-loaders-lua luasnip.txt /*luasnip-loaders-lua* +luasnip-loaders-snipmate luasnip.txt /*luasnip-loaders-snipmate* +luasnip-loaders-snippet-specific-filetypes luasnip.txt /*luasnip-loaders-snippet-specific-filetypes* +luasnip-loaders-vs-code luasnip.txt /*luasnip-loaders-vs-code* +luasnip-logging luasnip.txt /*luasnip-logging* +luasnip-lsp-snippets luasnip.txt /*luasnip-lsp-snippets* +luasnip-lsp-snippets-snipmate-parser luasnip.txt /*luasnip-lsp-snippets-snipmate-parser* +luasnip-lsp-snippets-transformations luasnip.txt /*luasnip-lsp-snippets-transformations* +luasnip-multisnippet luasnip.txt /*luasnip-multisnippet* +luasnip-node luasnip.txt /*luasnip-node* +luasnip-node-api luasnip.txt /*luasnip-node-api* +luasnip-node-reference luasnip.txt /*luasnip-node-reference* +luasnip-restorenode luasnip.txt /*luasnip-restorenode* +luasnip-snippetnode luasnip.txt /*luasnip-snippetnode* +luasnip-snippetproxy luasnip.txt /*luasnip-snippetproxy* +luasnip-snippets luasnip.txt /*luasnip-snippets* +luasnip-snippets-data luasnip.txt /*luasnip-snippets-data* +luasnip-source luasnip.txt /*luasnip-source* +luasnip-table-of-contents luasnip.txt /*luasnip-table-of-contents* +luasnip-textnode luasnip.txt /*luasnip-textnode* +luasnip-troubleshooting luasnip.txt /*luasnip-troubleshooting* +luasnip-troubleshooting-adding-snippets luasnip.txt /*luasnip-troubleshooting-adding-snippets* +luasnip-variables luasnip.txt /*luasnip-variables* +luasnip-variables-environment-namespaces luasnip.txt /*luasnip-variables-environment-namespaces* +luasnip-variables-lsp-variables luasnip.txt /*luasnip-variables-lsp-variables* +luasnip.txt luasnip.txt /*luasnip.txt* diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/ftplugin/snippets.vim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/ftplugin/snippets.vim new file mode 100644 index 00000000..b13cc8af --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/ftplugin/snippets.vim @@ -0,0 +1,14 @@ +" Vim filetype plugin for SnipMate snippets (.snippets files) + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl et< sts< cms< fdm< fde<" + +" Use hard tabs +setlocal noexpandtab softtabstop=0 + +setlocal commentstring=#\ %s +setlocal nospell diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/nix-support/propagated-build-inputs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/nix-support/propagated-build-inputs new file mode 100644 index 00000000..d8abd799 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/nix-support/propagated-build-inputs @@ -0,0 +1 @@ +/nix/store/qhhsw7qjik5gh1wkai703p6dq0ydp15r-lua5.1-jsregexp-0.0.7-1 /nix/store/mqbhz05llkddfb5wni0m48kw22ixxps4-lua-5.1.5 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/plugin/luasnip.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/plugin/luasnip.lua new file mode 100644 index 00000000..420e4c0a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/plugin/luasnip.lua @@ -0,0 +1,90 @@ +vim.filetype.add({ + extension = { snippets = "snippets" }, +}) + +local function silent_map(mode, lhs, rhs, desc) + vim.keymap.set(mode, lhs, rhs, { silent = true, desc = desc or "" }) +end + +silent_map("i", "luasnip-expand-or-jump", function() + require("luasnip").expand_or_jump() +end, "LuaSnip: Expand or jump in the current snippet") +silent_map("i", "luasnip-expand-snippet", function() + require("luasnip").expand() +end, "LuaSnip: Expand the current snippet") +silent_map("i", "luasnip-next-choice", function() + require("luasnip").change_choice(1) +end, "LuaSnip: Change to the next choice from the choiceNode") +silent_map("i", "luasnip-prev-choice", function() + require("luasnip").change_choice(-1) +end, "LuaSnip: Change to the previous choice from the choiceNode") +silent_map("i", "luasnip-jump-next", function() + require("luasnip").jump(1) +end, "LuaSnip: Jump to the next node") +silent_map("i", "luasnip-jump-prev", function() + require("luasnip").jump(-1) +end, "LuaSnip: Jump to the previous node") + +silent_map("n", "luasnip-delete-check", function() + require("luasnip").unlink_current_if_deleted() +end, "LuaSnip: Removes current snippet from jumplist") +silent_map("!", "luasnip-delete-check", function() + require("luasnip").unlink_current_if_deleted() +end, "LuaSnip: Removes current snippet from jumplist") + +silent_map("", "luasnip-expand-repeat", function() + require("luasnip").expand_repeat() +end, "LuaSnip: Repeat last node expansion") +silent_map("!", "luasnip-expand-repeat", function() + require("luasnip").expand_repeat() +end, "LuaSnip: Repeat last node expansion") + +silent_map("s", "luasnip-expand-or-jump", function() + require("luasnip").expand_or_jump() +end, "LuaSnip: Expand or jump in the current snippet") +silent_map("s", "luasnip-expand-snippet", function() + require("luasnip").expand() +end, "LuaSnip: Expand the current snippet") +silent_map("s", "luasnip-next-choice", function() + require("luasnip").change_choice(1) +end, "LuaSnip: Change to the next choice from the choiceNode") +silent_map("s", "luasnip-prev-choice", function() + require("luasnip").change_choice(-1) +end, "LuaSnip: Change to the previous choice from the choiceNode") +silent_map("s", "luasnip-jump-next", function() + require("luasnip").jump(1) +end, "LuaSnip: Jump to the next node") +silent_map("s", "luasnip-jump-prev", function() + require("luasnip").jump(-1) +end, "LuaSnip: Jump to the previous node") + +vim.api.nvim_create_user_command("LuaSnipUnlinkCurrent", function() + require("luasnip").unlink_current() +end, { force = true }) + +--stylua: ignore +vim.api.nvim_create_user_command("LuaSnipListAvailable", function() + ( + ( + vim.version + and type(vim.version) == "table" + and ( + ((vim.version().major == 0) and (vim.version().minor >= 9)) + or (vim.version().major > 0) ) + ) and vim.print + or vim.pretty_print + )(require("luasnip").available()) +end, { force = true }) + +require("luasnip.config")._setup() + +-- register these during startup so lazy_load will also load filetypes whose +-- events fired only before lazy_load is actually called. +-- (BufWinEnter -> lazy_load() wouldn't load any files without these). +vim.api.nvim_create_augroup("_luasnip_lazy_load", {}) +vim.api.nvim_create_autocmd({ "BufWinEnter", "FileType" }, { + callback = function(event) + require("luasnip.loaders").load_lazy_loaded(tonumber(event.buf)) + end, + group = "_luasnip_lazy_load", +}) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/plugin/luasnip.vim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/plugin/luasnip.vim new file mode 100644 index 00000000..d8628ea7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/plugin/luasnip.vim @@ -0,0 +1,23 @@ +function! luasnip#expandable() + return luaeval('require("luasnip").expandable()') +endfunction + +function! luasnip#expand_or_jumpable() + return luaeval('require("luasnip").expand_or_jumpable()') +endfunction + +function! luasnip#expand_or_locally_jumpable() + return luaeval('require("luasnip").expand_or_locally_jumpable()') +endfunction + +function! luasnip#locally_jumpable(direction) + return luaeval('require("luasnip").locally_jumpable(_A)', a:direction) +endfunction + +function! luasnip#jumpable(direction) + return luaeval('require("luasnip").jumpable(_A)', a:direction) +endfunction + +function! luasnip#choice_active() + return luaeval('require("luasnip").choice_active()') +endfunction diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/syntax/snippets.vim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/syntax/snippets.vim new file mode 100644 index 00000000..dcb39dd9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/syntax/snippets.vim @@ -0,0 +1,29 @@ +" Syntax highlighting for .snippets files +" Hopefully this should make snippets a bit nicer to write! +syn match snipComment '^#.*' +syn match placeHolder '\${\d\+\(:.\{-}\)\=}' contains=snipCommand +syn match tabStop '\$\d\+' +syn match snipEscape '\\\\\|\\`' +syn match snipCommand '\%(\\\@ cover common +" cases with \t and " ". +syn match snipError "^[^#vsaep\t ].*$" + +hi link snippet Identifier +hi link snipComment Comment +hi link multiSnipText String +hi link snipKeyword Keyword +hi link snipEscape SpecialChar +hi link placeHolder Special +hi link tabStop Special +hi link snipCommand String +hi link snipError Error +hi link priority Number diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/_types.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/_types.lua new file mode 100644 index 00000000..ec839c87 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/_types.lua @@ -0,0 +1,8 @@ +---@alias LuaSnip.Cursor {[1]: number, [2]: number} + +---@class LuaSnip.MatchRegion 0-based region +---@field row integer 0-based row +---@field col_range { [1]: integer, [2]: integer } 0-based column range, from-in, to-exclusive + +---@alias LuaSnip.Addable table +---Anything that can be passed to ls.add_snippets(). diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/config.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/config.lua new file mode 100644 index 00000000..aaaaa9c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/config.lua @@ -0,0 +1,143 @@ +local ext_util = require("luasnip.util.ext_opts") +local session = require("luasnip.session") +local conf_defaults = require("luasnip.default_config") + +local function set_snip_env(target_conf_defaults, user_config) + if not user_config.snip_env then + -- target_conf already contains defaults + return + end + + -- either "set" or "extend", make sure it does not appear in the final snip_env. + local snip_env_behaviour = user_config.snip_env.__snip_env_behaviour ~= nil + and user_config.snip_env.__snip_env_behaviour + or "extend" + assert( + snip_env_behaviour == "set" or snip_env_behaviour == "extend", + "Unknown __snip_env_behaviour, `" .. snip_env_behaviour .. "`" + ) + user_config.snip_env.__snip_env_behaviour = nil + + if snip_env_behaviour == "set" then + target_conf_defaults.snip_env = user_config.snip_env + else + -- cannot use vim.tbl_extend, since we'd need to transfer the metatable. + for k, v in pairs(user_config.snip_env) do + target_conf_defaults.snip_env[k] = v + end + end + + -- set to nil, to mark that it's handled. + user_config.snip_env = nil +end + +-- declare here to use in set_config. +local c + +c = { + set_config = function(user_config) + user_config = user_config or {} + local conf = vim.deepcopy(conf_defaults) + + -- remove unused highlights from default-ext_opts. + ext_util.clear_invalid(conf.ext_opts) + conf.ext_opts = ext_util.child_complete(conf.ext_opts) + user_config.ext_opts = + ext_util.child_complete(user_config.ext_opts or {}) + ext_util.child_extend(user_config.ext_opts, conf.ext_opts) + + -- use value from update_events, then updateevents. + -- also nil updateevents, don't spill it into the main config. + user_config.update_events = user_config.update_events + or user_config.updateevents + user_config.updateevents = nil + + set_snip_env(conf, user_config) + + -- handle legacy-key history. + if user_config.history ~= nil then + conf.keep_roots = user_config.history + conf.link_roots = user_config.history + conf.exit_roots = not user_config.history + conf.link_children = user_config.history + + -- unset key to prevent handling twice. + conf.history = nil + end + + for k, v in pairs(user_config) do + conf[k] = v + end + + session.config = conf + c._setup() + end, + + _setup = function() + local augroup = vim.api.nvim_create_augroup("luasnip", {}) + + -- events: string[], or string. if string[], each string is one + -- event-name, if string, either one event-name, or multiple delimited by `,`. + local function ls_autocmd(events, callback) + if type(events) == "string" then + -- split on ',' for backwards compatibility. + -- remove spaces from string. + events = vim.split(events:gsub(" ", ""), ",") + end + vim.api.nvim_create_autocmd(events, { + callback = callback, + group = augroup, + }) + end + if session.config.delete_check_events ~= nil then + ls_autocmd( + session.config.delete_check_events, + require("luasnip").unlink_current_if_deleted + ) + end + ls_autocmd( + session.config.update_events, + require("luasnip").active_update_dependents + ) + if session.config.region_check_events ~= nil then + ls_autocmd(session.config.region_check_events, function() + require("luasnip").exit_out_of_region( + require("luasnip").session.current_nodes[vim.api.nvim_get_current_buf()] + ) + end) + end + -- Remove buffers' nodes on deletion+wipeout. + ls_autocmd({ "BufDelete", "BufWipeout" }, function(event) + local current_nodes = require("luasnip").session.current_nodes + if current_nodes then + current_nodes[event.buf] = nil + end + end) + if session.config.enable_autosnippets then + ls_autocmd("InsertCharPre", function() + Luasnip_just_inserted = true + end) + ls_autocmd({ "TextChangedI", "TextChangedP" }, function() + if Luasnip_just_inserted then + require("luasnip").expand_auto() + Luasnip_just_inserted = nil + end + end) + end + + if session.config.store_selection_keys then + vim.cmd( + string.format( + [[xnoremap %s %s]], + session.config.store_selection_keys, + require("luasnip.util.select").select_keys + ) + ) + end + end, +} + +-- Keep these two for backward compativility +c.setup = c.set_config + +return c diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/default_config.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/default_config.lua new file mode 100644 index 00000000..68ed80c4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/default_config.lua @@ -0,0 +1,213 @@ +local types = require("luasnip.util.types") +local lazy_table = require("luasnip.util.lazy_table") + +local ft_functions = require("luasnip.extras.filetype_functions") + +-- Inserts a insert(1) before all other nodes, decreases node.pos's as indexing is "wrong". +local function modify_nodes(snip) + for i = #snip.nodes, 1, -1 do + snip.nodes[i + 1] = snip.nodes[i] + local node = snip.nodes[i + 1] + if node.pos then + node.pos = node.pos + 1 + end + end + + local iNode = require("luasnip.nodes.insertNode") + snip.nodes[1] = iNode.I(1) +end + +local lazy_snip_env = { + s = function() + return require("luasnip.nodes.snippet").S + end, + sn = function() + return require("luasnip.nodes.snippet").SN + end, + isn = function() + return require("luasnip.nodes.snippet").ISN + end, + t = function() + return require("luasnip.nodes.textNode").T + end, + i = function() + return require("luasnip.nodes.insertNode").I + end, + f = function() + return require("luasnip.nodes.functionNode").F + end, + c = function() + return require("luasnip.nodes.choiceNode").C + end, + d = function() + return require("luasnip.nodes.dynamicNode").D + end, + r = function() + return require("luasnip.nodes.restoreNode").R + end, + events = function() + return require("luasnip.util.events") + end, + k = function() + return require("luasnip.nodes.key_indexer").new_key + end, + ai = function() + return require("luasnip.nodes.absolute_indexer") + end, + extras = function() + return require("luasnip.extras") + end, + l = function() + return require("luasnip.extras").lambda + end, + rep = function() + return require("luasnip.extras").rep + end, + p = function() + return require("luasnip.extras").partial + end, + m = function() + return require("luasnip.extras").match + end, + n = function() + return require("luasnip.extras").nonempty + end, + dl = function() + return require("luasnip.extras").dynamic_lambda + end, + fmt = function() + return require("luasnip.extras.fmt").fmt + end, + fmta = function() + return require("luasnip.extras.fmt").fmta + end, + conds = function() + return require("luasnip.extras.expand_conditions") + end, + postfix = function() + return require("luasnip.extras.postfix").postfix + end, + types = function() + return require("luasnip.util.types") + end, + parse = function() + return require("luasnip.util.parser").parse_snippet + end, + ms = function() + return require("luasnip.nodes.multiSnippet").new_multisnippet + end, +} + +-- stylua: ignore +return { + -- corresponds to legacy "history=false". + keep_roots = false, + link_roots = false, + exit_roots = true, + link_children = false, + + update_events = "InsertLeave", + -- see :h User, event should never be triggered(except if it is `doautocmd`'d) + region_check_events = nil, + delete_check_events = nil, + store_selection_keys = nil, -- Supossed to be the same as the expand shortcut + ext_opts = { + [types.textNode] = { + active = { hl_group = "LuasnipTextNodeActive" }, + passive = { hl_group = "LuasnipTextNodePassive" }, + visited = { hl_group = "LuasnipTextNodeVisited" }, + unvisited = { hl_group = "LuasnipTextNodeUnvisited" }, + snippet_passive = { hl_group = "LuasnipTextNodeSnippetPassive" }, + }, + [types.insertNode] = { + active = { hl_group = "LuasnipInsertNodeActive" }, + passive = { hl_group = "LuasnipInsertNodePassive" }, + visited = { hl_group = "LuasnipInsertNodeVisited" }, + unvisited = { hl_group = "LuasnipInsertNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipInsertNodeSnippetPassive", + }, + }, + [types.exitNode] = { + active = { hl_group = "LuasnipExitNodeActive" }, + passive = { hl_group = "LuasnipExitNodePassive" }, + visited = { hl_group = "LuasnipExitNodeVisited" }, + unvisited = { hl_group = "LuasnipExitNodeUnvisited" }, + snippet_passive = { hl_group = "LuasnipExitNodeSnippetPassive" }, + }, + [types.functionNode] = { + active = { hl_group = "LuasnipFunctionNodeActive" }, + passive = { hl_group = "LuasnipFunctionNodePassive" }, + visited = { hl_group = "LuasnipFunctionNodeVisited" }, + unvisited = { hl_group = "LuasnipFunctionNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipFunctionNodeSnippetPassive", + }, + }, + [types.snippetNode] = { + active = { hl_group = "LuasnipSnippetNodeActive" }, + passive = { hl_group = "LuasnipSnippetNodePassive" }, + visited = { hl_group = "LuasnipSnippetNodeVisited" }, + unvisited = { hl_group = "LuasnipSnippetNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipSnippetNodeSnippetPassive", + }, + }, + [types.choiceNode] = { + active = { hl_group = "LuasnipChoiceNodeActive" }, + passive = { hl_group = "LuasnipChoiceNodePassive" }, + visited = { hl_group = "LuasnipChoiceNodeVisited" }, + unvisited = { hl_group = "LuasnipChoiceNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipChoiceNodeSnippetPassive", + }, + }, + [types.dynamicNode] = { + active = { hl_group = "LuasnipDynamicNodeActive" }, + passive = { hl_group = "LuasnipDynamicNodePassive" }, + visited = { hl_group = "LuasnipDynamicNodeVisited" }, + unvisited = { hl_group = "LuasnipDynamicNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipDynamicNodeSnippetPassive", + }, + }, + [types.snippet] = { + active = { hl_group = "LuasnipSnippetActive" }, + passive = { hl_group = "LuasnipSnippetPassive" }, + -- not used! + visited = { hl_group = "LuasnipSnippetVisited" }, + unvisited = { hl_group = "LuasnipSnippetUnvisited" }, + snippet_passive = { hl_group = "LuasnipSnippetSnippetPassive" }, + }, + [types.restoreNode] = { + active = { hl_group = "LuasnipRestoreNodeActive" }, + passive = { hl_group = "LuasnipRestoreNodePassive" }, + visited = { hl_group = "LuasnipRestoreNodeVisited" }, + unvisited = { hl_group = "LuasnipRestoreNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipRestoreNodeSnippetPassive", + }, + }, + }, + ext_base_prio = 200, + ext_prio_increase = 9, + enable_autosnippets = false, + parser_nested_assembler = function(pos, snip) + -- only require here, to prevent some upfront load-cost. + local iNode = require("luasnip.nodes.insertNode") + local cNode = require("luasnip.nodes.choiceNode") + + modify_nodes(snip) + snip:init_nodes() + snip.pos = nil + + return cNode.C(pos, { snip, iNode.I(nil, { "" }) }) + end, + -- Function expected to return a list of filetypes (or empty list) + ft_func = ft_functions.from_filetype, + -- fn(bufnr) -> string[] (filetypes). + load_ft_func = ft_functions.from_filetype_load, + -- globals injected into luasnippet-files. + snip_env = lazy_table({}, lazy_snip_env), + loaders_store_source = false, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_extra_types.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_extra_types.lua new file mode 100644 index 00000000..d8d7305c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_extra_types.lua @@ -0,0 +1,26 @@ +---@class LuaSnip.extra.MatchTSNodeOpts Passed in by the user, describes how to +---select a node from the tree via a query and captures. +---@field query? string A query, as text +---@field query_name? string The name of a query (passed to `vim.treesitter.query.get`). +---@field query_lang string The language of the query. +---@field select? LuaSnip.extra.BuiltinMatchSelector|LuaSnip.extra.MatchSelector +---@field match_captures? string|string[] + +---@class LuaSnip.extra.MatchedTSNodeInfo +---@field capture_name string +---@field node TSNode + +---@alias LuaSnip.extra.BuiltinMatchSelector +---| '"any"' # The default selector, selects the first match but not return all captures +---| '"shortest"' # Selects the shortest match, return all captures too +---| '"longest"' # Selects the longest match, return all captures too + +---Call record repeatedly to record all matches/nodes, retrieve once there are no more matches +---@class LuaSnip.extra.MatchSelector +---@field record fun(ts_match: TSMatch?, node: TSNode): boolean return true if recording can be aborted +--- (because the best match has been found) +---@field retrieve fun(): TSMatch?,TSNode? return the best match, as determined by this selector. + +---@alias LuaSnip.extra.MatchTSNodeFunc fun(parser: LuaSnip.extra.TSParser, cursor: LuaSnip.Cursor): LuaSnip.extra.NamedTSMatch?,TSNode? + +---@alias LuaSnip.extra.NamedTSMatch table diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_lambda.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_lambda.lua new file mode 100644 index 00000000..cea4490d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_lambda.lua @@ -0,0 +1,377 @@ +-- Mostly borrowed from https://github.com/lunarmodules/Penlight/ with just some changes to use +-- neovim internal functions and reformat +-- Copyright (C) 2009-2016 Steve Donovan, David Manura. + +local concat, append = table.concat, table.insert +local map = vim.tbl_map + +local _DEBUG = rawget(_G, "_DEBUG") + +local function assert_arg(n, val, tp, verify, msg, lev) + if type(val) ~= tp then + error( + ("argument %d expected a '%s', got a '%s'"):format(n, tp, type(val)), + lev or 2 + ) + end + if verify and not verify(val) then + error(("argument %d: '%s' %s"):format(n, val, msg), lev or 2) + end +end + +local lambda = {} + +-- metatable for Placeholder Expressions (PE) +local _PEMT = {} + +local function P(t) + setmetatable(t, _PEMT) + return t +end + +lambda.PE = P + +local function isPE(obj) + return getmetatable(obj) == _PEMT +end + +lambda.isPE = isPE + +-- construct a placeholder variable (e.g _1 and _2) +local function PH(idx) + return P({ op = "X", repr = "args[" .. idx .. "]", index = idx }) +end + +-- construct a constant placeholder variable (e.g _C1 and _C2) +local function CPH(idx) + return P({ op = "X", repr = "_C" .. idx, index = idx }) +end + +lambda._1, lambda._2, lambda._3, lambda._4, lambda._5 = + PH(1), PH(2), PH(3), PH(4), PH(5) +lambda._0 = P({ op = "X", repr = "...", index = 0 }) + +function lambda.Var(name) + local ls = vim.split(name, "[%s,]+") + local res = {} + for i = 1, #ls do + append(res, P({ op = "X", repr = ls[i], index = 0 })) + end + return unpack(res) +end + +function lambda._(value) + return P({ op = "X", repr = value, index = "wrap" }) +end + +-- unknown keys are some named variable. +setmetatable(lambda, { + __index = function(_, key) + -- \\n to be correctly interpreted in `load()`. + return P({ + op = "X", + repr = "args." .. key, + index = 0, + }) + end, +}) + +local repr + +lambda.Nil = lambda.Var("nil") + +function _PEMT.__index(obj, key) + return P({ op = "[]", obj, key }) +end + +function _PEMT.__call(fun, ...) + return P({ op = "()", fun, ... }) +end + +function _PEMT.__tostring(e) + return repr(e) +end + +function _PEMT.__unm(arg) + return P({ op = "unm", arg }) +end + +function lambda.Not(arg) + return P({ op = "not", arg }) +end + +function lambda.Len(arg) + return P({ op = "#", arg }) +end + +local function binreg(context, t) + for name, op in pairs(t) do + rawset(context, name, function(x, y) + return P({ op = op, x, y }) + end) + end +end + +local function import_name(name, fun, context) + rawset(context, name, function(...) + return P({ op = "()", fun, ... }) + end) +end + +local imported_functions = {} + +local function is_global_table(n) + return type(_G[n]) == "table" +end + +--- wrap a table of functions. This makes them available for use in +-- placeholder expressions. +-- @string tname a table name +-- @tab context context to put results, defaults to environment of caller +function lambda.import(tname, context) + assert_arg( + 1, + tname, + "string", + is_global_table, + "arg# 1: not a name of a global table" + ) + local t = _G[tname] + context = context or _G + for name, fun in pairs(t) do + import_name(name, fun, context) + imported_functions[fun] = name + end +end + +--- register a function for use in placeholder expressions. +-- @lambda fun a function +-- @string[opt] name an optional name +-- @return a placeholder functiond +function lambda.register(fun, name) + assert_arg(1, fun, "function") + if name then + assert_arg(2, name, "string") + imported_functions[fun] = name + end + return function(...) + return P({ op = "()", fun, ... }) + end +end + +function lambda.lookup_imported_name(fun) + return imported_functions[fun] +end + +local function _arg(...) + return ... +end + +function lambda.Args(...) + return P({ op = "()", _arg, ... }) +end + +-- binary operators with their precedences (see Lua manual) +-- precedences might be incremented by one before use depending on +-- left- or right-associativity, space them out +local binary_operators = { + ["or"] = 0, + ["and"] = 2, + ["=="] = 4, + ["~="] = 4, + ["<"] = 4, + [">"] = 4, + ["<="] = 4, + [">="] = 4, + [".."] = 6, + ["+"] = 8, + ["-"] = 8, + ["*"] = 10, + ["/"] = 10, + ["%"] = 10, + ["^"] = 14, +} + +-- unary operators with their precedences +local unary_operators = { + ["not"] = 12, + ["#"] = 12, + ["unm"] = 12, +} + +-- comparisons (as prefix functions) +binreg(lambda, { + And = "and", + Or = "or", + Eq = "==", + Lt = "<", + Gt = ">", + Le = "<=", + Ge = ">=", +}) + +-- standard binary operators (as metamethods) +binreg(_PEMT, { + __add = "+", + __sub = "-", + __mul = "*", + __div = "/", + __mod = "%", + __pow = "^", + __concat = "..", +}) + +binreg(_PEMT, { __eq = "==" }) + +--- all elements of a table except the first. +-- @tab ls a list-like table. +function lambda.tail(ls) + assert_arg(1, ls, "table") + local res = {} + for i = 2, #ls do + append(res, ls[i]) + end + return res +end + +--- create a string representation of a placeholder expression. +-- @param e a placeholder expression +-- @param lastpred not used +function repr(e, lastpred) + local tail = lambda.tail + if isPE(e) then + local pred = binary_operators[e.op] or unary_operators[e.op] + if pred then + -- binary or unary operator + local s + if binary_operators[e.op] then + local left_pred = pred + local right_pred = pred + if e.op == ".." or e.op == "^" then + left_pred = left_pred + 1 + else + right_pred = right_pred + 1 + end + local left_arg = repr(e[1], left_pred) + local right_arg = repr(e[2], right_pred) + s = left_arg .. " " .. e.op .. " " .. right_arg + else + local op = e.op == "unm" and "-" or e.op + s = op .. " " .. repr(e[1], pred) + end + if lastpred and lastpred > pred then + s = "(" .. s .. ")" + end + return s + else -- either postfix, or a placeholder + local ls = map(repr, e) + if e.op == "[]" then + return ls[1] .. "[" .. ls[2] .. "]" + elseif e.op == "()" then + local fn + if ls[1] ~= nil then -- was _args, undeclared! + fn = ls[1] + else + fn = "" + end + return fn .. "(" .. concat(tail(ls), ",") .. ")" + else + return e.repr + end + end + elseif type(e) == "string" then + return '"' .. e .. '"' + elseif type(e) == "function" then + local name = lambda.lookup_imported_name(e) + if name then + return name + else + return tostring(e) + end + else + return tostring(e) --should not really get here! + end +end +lambda.repr = repr + +-- collect all the non-PE values in this PE into vlist, and replace each occurence +-- with a constant PH (_C1, etc). Return the maximum placeholder index found. +local collect_values +function collect_values(e, vlist) + if isPE(e) then + if e.op ~= "X" then + local m = 0 + for i = 1, #e do + local subx = e[i] + local pe = isPE(subx) + if pe then + if subx.op == "X" and subx.index == "wrap" then + subx = subx.repr + pe = false + else + m = math.max(m, collect_values(subx, vlist)) + end + end + if not pe then + append(vlist, subx) + e[i] = CPH(#vlist) + end + end + return m + else -- was a placeholder, it has an index... + return e.index + end + else -- plain value has no placeholder dependence + return 0 + end +end +lambda.collect_values = collect_values + +--- instantiate a PE into an actual function. First we find the largest placeholder used, +-- e.g. _2; from this a list of the formal parameters can be build. Then we collect and replace +-- any non-PE values from the PE, and build up a constant binding list. +-- Finally, the expression can be compiled, and e.__PE_function is set. +-- @param e a placeholder expression +-- @return a function +function lambda.instantiate(e) + local consts, values = {}, {} + local rep, err, fun + local n = lambda.collect_values(e, values) + for i = 1, #values do + append(consts, "_C" .. i) + if _DEBUG then + print(i, values[i]) + end + end + + consts = concat(consts, ",") + rep = repr(e) + local fstr = ("return function(%s) return function(args) return %s end end"):format( + consts, + rep + ) + if _DEBUG then + print(fstr) + end + fun, err = load(fstr, "fun") + if not fun then + return nil, err + end + fun = fun() -- get wrapper + fun = fun(unpack(values)) -- call wrapper (values could be empty) + e.__PE_function = fun + return fun +end + +--- instantiate a PE unless it has already been done. +-- @param e a placeholder expression +-- @return the function +function lambda.I(e) + if rawget(e, "__PE_function") then + return e.__PE_function + else + return lambda.instantiate(e) + end +end + +return lambda diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_parser_combinator.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_parser_combinator.lua new file mode 100644 index 00000000..b761b3c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_parser_combinator.lua @@ -0,0 +1,94 @@ +-- Minimal parser combinator, +-- only for internal use so not exposed elsewhere nor documented in the oficial doc +-- +local M = {} + +-- Consumes strings matching a pattern, generates the matched string +function M.pattern(pat) + return function(text, pos) + local s, e = text:find(pat, pos) + + if s then + local v = text:sub(s, e) + return true, v, pos + #v + else + return false, nil, pos + end + end +end + +-- Matches whatever `p matches and generates whatever p generates after +-- transforming it with `f +function M.map(p, f) + return function(text, pos) + local succ, val, new_pos = p(text, pos) + if succ then + return true, f(val), new_pos + end + return false, nil, pos + end +end + +-- Matches and generates the same as the first of it's children that matches something +function M.any(...) + local parsers = { ... } + return function(text, pos) + for _, p in ipairs(parsers) do + local succ, val, new_pos = p(text, pos) + if succ then + return true, val, new_pos + end + end + return false, nil, pos + end +end + +-- Matches all what its children do in sequence, generates a table of its children generations +function M.seq(...) + local parsers = { ... } + return function(text, pos) + local original_pos = pos + local values = {} + for _, p in ipairs(parsers) do + local succ, val, new_pos = p(text, pos) + pos = new_pos + if not succ then + return false, nil, original_pos + end + table.insert(values, val) + end + return true, values, pos + end +end + +-- Matches cero or more times what it child do in sequence, generates a table with those generations +function M.star(p) + return function(text, pos) + local len = #text + local values = {} + + while pos <= len do + local succ, val, new_pos = p(text, pos) + if succ then + pos = new_pos + table.insert(values, val) + else + break + end + end + return #values > 0, values, pos + end +end + +-- Consumes a literal string, does not generates +function M.literal(t) + return function(text, pos) + if text:sub(pos, pos + #t - 1) == t then + return true, nil, pos + #t + else + return false, text:sub(pos, pos + #t), pos + #t + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_treesitter.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_treesitter.lua new file mode 100644 index 00000000..69aef027 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/_treesitter.lua @@ -0,0 +1,433 @@ +local util = require("luasnip.util.util") +local tbl = require("luasnip.util.table") + +local function get_lang(bufnr) + local ft = vim.api.nvim_buf_get_option(bufnr, "ft") + local lang = vim.treesitter.language.get_lang(ft) or ft + return lang +end + +-- Inspect node +---@param node TSNode? +---@return string +local function inspect_node(node) + if node == nil then + return "nil" + end + + local start_row, start_col, end_row, end_col = + vim.treesitter.get_node_range(node) + + return ("%s [%d, %d] [%d, %d]"):format( + node:type(), + start_row, + start_col, + end_row, + end_col + ) +end + +---@param bufnr number +---@param region LuaSnip.MatchRegion +---@return LanguageTree, string +local function reparse_buffer_after_removing_match(bufnr, region) + local lang = get_lang(bufnr) + + -- have to get entire buffer, a pattern-match may include lines behind the trigger. + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + -- region is 0-indexed, lines and strings 1-indexed. + local region_line = lines[region.row + 1] + -- sub includes end, want to exclude it. + local left_part = region_line:sub(1, region.col_range[1] + 1 - 1) + local right_part = region_line:sub(region.col_range[2] + 1) + + lines[region.row + 1] = left_part .. right_part + + local source = table.concat(lines, "\n") + + ---@type LanguageTree + local parser = vim.treesitter.get_string_parser(source, lang, nil) + parser:parse() + return parser, source +end + +---@class LuaSnip.extra.FixBufferContext +---@field ori_bufnr number +---@field ori_text string +---@field region LuaSnip.MatchRegion +local FixBufferContext = {} + +---@param ori_bufnr number +---@param region LuaSnip.MatchRegion +---@return LuaSnip.extra.FixBufferContext +function FixBufferContext.new(ori_bufnr, region, region_content) + local o = { + ori_bufnr = ori_bufnr, + ori_text = region_content, + region = region, + } + setmetatable(o, { + __index = FixBufferContext, + }) + + return o +end + +function FixBufferContext:enter() + vim.api.nvim_buf_set_text( + self.ori_bufnr, + self.region.row, + self.region.col_range[1], + self.region.row, + self.region.col_range[2], + { "" } + ) + + local parser, source = + vim.treesitter.get_parser(self.ori_bufnr), self.ori_bufnr + parser:parse() + + return parser, source +end + +function FixBufferContext:leave() + vim.api.nvim_buf_set_text( + self.ori_bufnr, + self.region.row, + self.region.col_range[1], + self.region.row, + self.region.col_range[1], + { self.ori_text } + ) + + -- The cursor does not necessarily move with the insertion, and has to be + -- restored manually. + -- when making this work for expansion away from cursor, store cursor-pos + -- in self. + vim.api.nvim_win_set_cursor( + 0, + { self.region.row + 1, self.region.col_range[2] } + ) + + local parser, source = + vim.treesitter.get_parser(self.ori_bufnr), self.ori_bufnr + parser:parse() + return parser, source +end + +-- iterate over all +local function captures_iter(captures) + -- turn string/string[] into map: string -> bool, for querying whether some + -- string is present in captures. + local capture_map = tbl.list_to_set(captures) + + -- receives the query and the iterator over all its matches. + return function(query, match_iter) + local current_match + local current_capture_id + local iter + local pattern + + iter = function() + -- if there is no current match to continue, + if not current_match then + pattern, current_match, _ = match_iter() + + -- occurs once there are no more matches. + if not pattern then + return nil + end + end + while true do + local node + current_capture_id, node = + next(current_match, current_capture_id) + if not current_capture_id then + break + end + + local capture_name = query.captures[current_capture_id] + + if capture_map[capture_name] then + return current_match, node + end + end + + -- iterated over all captures of the current match, reset it to + -- retrieve the next match in the recursion. + current_match = nil + + -- tail-call-optimization! :fingers_crossed: + return iter() + end + + return iter + end +end + +local builtin_tsnode_selectors = { + any = function() + local best_node + local best_node_match + return { + record = function(match, node) + best_node = node + best_node_match = match + -- abort immediately, we just want any match. + return true + end, + retrieve = function() + return best_node_match, best_node + end, + } + end, + shortest = function() + local best_node + local best_node_match + + -- end is already equal, only have to compare start. + local best_node_start + return { + record = function(match, node) + local start_row, start_col, _, _ = + vim.treesitter.get_node_range(node) + if + (best_node == nil) + or (start_row > best_node_start[1]) + or ( + start_row == best_node_start[1] + and start_col > best_node_start[2] + ) + then + best_node = node + best_node_match = match + best_node_start = { start_row, start_col } + end + -- don't abort, have to see all potential nodes to find shortest match. + return false + end, + retrieve = function() + return best_node_match, best_node + end, + } + end, + longest = function() + local best_node + local best_node_match + + -- end is already equal, only have to compare start. + local best_node_start + return { + record = function(match, node) + local start_row, start_col, _, _ = + vim.treesitter.get_node_range(node) + if + (best_node == nil) + or (start_row < best_node_start[1]) + or ( + start_row == best_node_start[1] + and start_col < best_node_start[2] + ) + then + best_node = node + best_node_match = match + best_node_start = { start_row, start_col } + end + -- don't abort, have to see all potential nodes to find longest match. + return false + end, + retrieve = function() + return best_node_match, best_node + end, + } + end, +} + +---@class LuaSnip.extra.TSParser +---@field parser LanguageTree +---@field source string|number +local TSParser = {} + +---@param bufnr number? +---@param parser LanguageTree +---@param source string|number +---@return LuaSnip.extra.TSParser? +function TSParser.new(bufnr, parser, source) + bufnr = bufnr or vim.api.nvim_get_current_buf() + + local o = { + parser = parser, + source = source, + } + + setmetatable(o, { + __index = TSParser, + ---@param self LuaSnip.extra.TSParser + ---@return string + __tostring = function(self) + return ("trees: %d, source: %s"):format( + #self.parser:trees(), + type(self.source) == "number" and tostring(self.source) + or "[COPIED]" + ) + end, + }) + return o +end + +---@param pos { [1]: number, [2]: number }? +---@return TSNode? +function TSParser:get_node_at_pos(pos) + pos = vim.F.if_nil(pos, util.get_cursor_0ind()) + local row, col = pos[1], pos[2] + assert( + row >= 0 and col >= 0, + "Invalid position: row and col must be non-negative" + ) + local range = { row, col, row, col } + return self.parser:named_node_for_range( + range, + { ignore_injections = false } + ) +end + +---Get the root for the smallest tree containing `pos`. +---@param pos { [1]: number, [2]: number } +---@return TSNode? +function TSParser:root_at(pos) + local tree = self.parser:tree_for_range( + { pos[1], pos[2], pos[1], pos[2] }, + { ignore_injections = false } + ) + if not tree then + return nil + end + + return tree:root() +end + +---@param match_opts LuaSnip.extra.EffectiveMatchTSNodeOpts +---@param pos { [1]: number, [2]: number } +---@return LuaSnip.extra.NamedTSMatch?, TSNode? +function TSParser:match_at(match_opts, pos) + -- Since we want to find a match to the left of pos, and if we accept there + -- has to be at least one character (I assume), we should probably not look + -- for the tree containing `pos`, since that might be the wrong one (if + -- injected languages are in play). + local root = self:root_at({ pos[1], pos[2] - 1 }) + if root == nil then + return nil, nil + end + local root_from_line, _, root_to_line, _ = root:range() + + local query = match_opts.query + local selector = match_opts.selector() + local next_ts_match = + -- end-line is excluded by iter_matches, if the column of root_to + -- greater than 0, we would erroneously ignore a line that could + -- contain our match. + query:iter_matches(root, self.source, root_from_line, root_to_line + 1) + + for match, node in match_opts.generator(query, next_ts_match) do + -- false: don't include bytes. + local _, _, end_row, end_col = node:range(false) + if end_row == pos[1] and end_col == pos[2] then + if selector.record(match, node) then + -- should abort iteration + break + end + end + end + + local best_match, node = selector.retrieve() + if not best_match then + return nil, nil + end + + -- map captures via capture-name, not id. + local named_captures_match = {} + for id, capture_node in pairs(best_match) do + named_captures_match[query.captures[id]] = capture_node + end + return named_captures_match, node +end + +---@param node TSNode +---@return string +function TSParser:get_node_text(node) + -- not sure what happens if this is multiline. + return vim.treesitter.get_node_text(node, self.source) +end + +---@param root TSNode +---@param n number +---@param matcher fun(node:TSNode):boolean|nil +---@return TSNode? +local function find_nth_parent(root, n, matcher) + local parent = root + matcher = matcher or function() + return true + end + local i = 0 + while i < n do + if not parent or not matcher(parent) then + return nil + end + parent = parent:parent() + i = i + 1 + end + if not parent or not matcher(parent) then + return nil + end + return parent +end + +---@param root TSNode +---@param matcher fun(node:TSNode):boolean|nil +local function find_topmost_parent(root, matcher) + ---@param node TSNode? + ---@return TSNode? + local function _impl(node) + if node == nil then + return nil + end + local current = nil + if matcher == nil or matcher(node) then + current = node + end + return vim.F.if_nil(_impl(node:parent()), current) + end + + return _impl(root) +end + +---@param root TSNode +---@param matcher fun(node:TSNode):boolean|nil +local function find_first_parent(root, matcher) + ---@param node TSNode? + ---@return TSNode? + local function _impl(node) + if node == nil then + return nil + end + if matcher == nil or matcher(node) then + return node + end + return _impl(node:parent()) + end + + return _impl(root) +end + +return { + get_lang = get_lang, + reparse_buffer_after_removing_match = reparse_buffer_after_removing_match, + TSParser = TSParser, + FixBufferContext = FixBufferContext, + find_topmost_parent = find_topmost_parent, + find_first_parent = find_first_parent, + find_nth_parent = find_nth_parent, + inspect_node = inspect_node, + captures_iter = captures_iter, + builtin_tsnode_selectors = builtin_tsnode_selectors, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/expand.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/expand.lua new file mode 100644 index 00000000..e97d85fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/expand.lua @@ -0,0 +1,14 @@ +local cond_obj = require("luasnip.extras.conditions") + +-- use the functions from show as basis and extend/overwrite functions specific for expand here +local M = vim.deepcopy(require("luasnip.extras.conditions.show")) +----------------------- +-- PRESET CONDITIONS -- +----------------------- +local function line_begin(line_to_cursor, matched_trigger) + -- +1 because `string.sub("abcd", 1, -2)` -> abc + return line_to_cursor:sub(1, -(#matched_trigger + 1)):match("^%s*$") +end +M.line_begin = cond_obj.make_condition(line_begin) + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/init.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/init.lua new file mode 100644 index 00000000..db9ff39f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/init.lua @@ -0,0 +1,55 @@ +local M = {} + +----------------------- +-- CONDITION OBJECTS -- +----------------------- +local condition_mt = { + -- logic operators + -- not '-' + __unm = function(o1) + return M.make_condition(function(...) + return not o1(...) + end) + end, + -- or '+' + __add = function(o1, o2) + return M.make_condition(function(...) + return o1(...) or o2(...) + end) + end, + __sub = function(o1, o2) + return M.make_condition(function(...) + return o1(...) and not o2(...) + end) + end, + -- and '*' + __mul = function(o1, o2) + return M.make_condition(function(...) + return o1(...) and o2(...) + end) + end, + -- xor '^' + __pow = function(o1, o2) + return M.make_condition(function(...) + return o1(...) ~= o2(...) + end) + end, + -- xnor '%' + -- might be counter intuitive, but as we can't use '==' (must return bool) + -- it's best to use something weird (doesn't have to be used) + __mod = function(o1, o2) + return function(...) + return o1(...) == o2(...) + end + end, + -- use table like a function by overloading __call + __call = function(tab, line_to_cursor, matched_trigger, captures) + return tab.func(line_to_cursor, matched_trigger, captures) + end, +} + +function M.make_condition(func) + return setmetatable({ func = func }, condition_mt) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/show.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/show.lua new file mode 100644 index 00000000..eb11cf4e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/conditions/show.lua @@ -0,0 +1,21 @@ +local cond_obj = require("luasnip.extras.conditions") + +local M = {} +----------------------- +-- PRESET CONDITIONS -- +----------------------- +local function line_end(line_to_cursor) + local line = vim.api.nvim_get_current_line() + -- looks pretty inefficient, but as lue interns strings, this is just a + -- comparision of pointers (which probably is faster than calculate the + -- length and then checking) + return line_to_cursor == line +end +M.line_end = cond_obj.make_condition(line_end) + +local function has_selected_text() + return vim.b.LUASNIP_TM_SELECT ~= nil +end +M.has_selected_text = cond_obj.make_condition(has_selected_text) + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/expand_conditions.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/expand_conditions.lua new file mode 100644 index 00000000..69c662fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/expand_conditions.lua @@ -0,0 +1 @@ +return require("luasnip.extras.conditions.expand") diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/filetype_functions.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/filetype_functions.lua new file mode 100644 index 00000000..e7011f0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/filetype_functions.lua @@ -0,0 +1,89 @@ +local function fts_from_ts_lang(lang) + local fts = {} + -- In case of someone using nvim <= 0.9 + if vim.treesitter.language and vim.treesitter.language.get_filetypes then + fts = vim.treesitter.language.get_filetypes(lang) + end + -- Keep lang as part of the result, for backward compatibility. + -- If lang is already part of fts, one entry will be removed by deduplicate + -- in get_snippet_filetypes(). + table.insert(fts, lang) + return fts +end + +local function from_cursor_pos() + -- get_parser errors if parser not present (no grammar for language). + local has_parser, parser = pcall(vim.treesitter.get_parser) + + if has_parser then + local cursor = require("luasnip.util.util").get_cursor_0ind() + -- assumption: languagetree uses 0-indexed byte-ranges. + local lang = parser + :language_for_range({ + cursor[1], + cursor[2], + cursor[1], + cursor[2], + }) + :lang() + return fts_from_ts_lang(lang) + else + return {} + end +end + +local function from_filetype() + return vim.split(vim.bo.filetype, ".", { plain = true, trimemtpy = false }) +end + +-- NOTE: Beware that the resulting filetypes may differ from the ones in `vim.bo.filetype`. (for +-- example the filetype for LaTeX is 'latex' and not 'tex' as in `vim.bo.filetype`) -- +local function from_pos_or_filetype() + local from_cursor = from_cursor_pos() + if not vim.tbl_isempty(from_cursor) then + return from_cursor + else + return from_filetype() + end +end + +local function from_filetype_load(bufnr) + return vim.split(vim.api.nvim_buf_get_option(bufnr, "filetype"), ".", true) +end + +local function extend_load_ft(extend_fts) + setmetatable(extend_fts, { + -- if the filetype is not extended, only it itself should be loaded. + -- preventing ifs via __index. + __index = function(t, ft) + local val = { ft } + rawset(t, ft, val) + return val + end, + }) + + for ft, _ in pairs(extend_fts) do + -- append the regular filetype to the extend-filetypes. + table.insert(extend_fts[ft], ft) + end + + return function(bufnr) + local fts = + vim.split(vim.api.nvim_buf_get_option(bufnr, "filetype"), ".", true) + local res = {} + + for _, ft in ipairs(fts) do + vim.list_extend(res, extend_fts[ft]) + end + + return res + end +end + +return { + from_filetype = from_filetype, + from_cursor_pos = from_cursor_pos, + from_pos_or_filetype = from_pos_or_filetype, + from_filetype_load = from_filetype_load, + extend_load_ft = extend_load_ft, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/fmt.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/fmt.lua new file mode 100644 index 00000000..4d8e3152 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/fmt.lua @@ -0,0 +1,231 @@ +local text_node = require("luasnip.nodes.textNode").T +local wrap_nodes = require("luasnip.util.util").wrap_nodes +local extend_decorator = require("luasnip.util.extend_decorator") +local Str = require("luasnip.util.str") +local rp = require("luasnip.extras").rep + +-- https://gist.github.com/tylerneylon/81333721109155b2d244 +local function copy3(obj, seen) + -- Handle non-tables and previously-seen tables. + if type(obj) ~= "table" then + return obj + end + if seen and seen[obj] then + return seen[obj] + end + + -- New table; mark it as seen an copy recursively. + local s = seen or {} + local res = {} + s[obj] = res + for k, v in next, obj do + res[copy3(k, s)] = copy3(v, s) + end + return setmetatable(res, getmetatable(obj)) +end + +-- Interpolate elements from `args` into format string with placeholders. +-- +-- The placeholder syntax for selecting from `args` is similar to fmtlib and +-- Python's .format(), with some notable differences: +-- * no format options (like `{:.2f}`) +-- * 1-based indexing +-- * numbered/auto-numbered placeholders can be mixed; numbered ones set the +-- current index to new value, so following auto-numbered placeholders start +-- counting from the new value (e.g. `{} {3} {}` is `{1} {3} {4}`) +-- +-- Arguments: +-- fmt: string with placeholders +-- args: table with list-like and/or map-like keys +-- opts: +-- delimiters: string, 2 distinct characters (left, right), default "{}" +-- strict: boolean, set to false to allow for unused `args`, default true +-- repeat_duplicates: boolean, repeat nodes which have jump_index instead of copying them, default false +-- Returns: a list of strings and elements of `args` inserted into placeholders +local function interpolate(fmt, args, opts) + local defaults = { + delimiters = "{}", + strict = true, + repeat_duplicates = false, + } + opts = vim.tbl_extend("force", defaults, opts or {}) + + -- sanitize delimiters + assert( + #opts.delimiters == 2, + 'Currently only single-char delimiters are supported, e.g. delimiters="{}" (left, right)' + ) + assert( + opts.delimiters:sub(1, 1) ~= opts.delimiters:sub(2, 2), + "Delimiters must be two _different_ characters" + ) + local delimiters = { + left = opts.delimiters:sub(1, 1), + right = opts.delimiters:sub(2, 2), + esc_left = vim.pesc(opts.delimiters:sub(1, 1)), + esc_right = vim.pesc(opts.delimiters:sub(2, 2)), + } + + -- manage insertion of text/args + local elements = {} + local last_index = 0 + local used_keys = {} + + local add_text = function(text) + if #text > 0 then + table.insert(elements, text) + end + end + local add_arg = function(placeholder) + local num = tonumber(placeholder) + local key + if num then -- numbered placeholder + last_index = num + key = last_index + elseif placeholder == "" then -- auto-numbered placeholder + key = last_index + 1 + last_index = key + else -- named placeholder + key = placeholder + end + assert( + args[key], + string.format( + "Missing key `%s` in format arguments: `%s`", + key, + fmt + ) + ) + -- if the node was already used, insert a copy of it. + -- The nodes are modified in-place as part of constructing the snippet, + -- modifying one node twice will lead to UB. + if used_keys[key] then + local jump_index = args[key]:get_jump_index() -- For nodes that don't have a jump index, copy it instead + if not opts.repeat_duplicates or jump_index == nil then + table.insert(elements, copy3(args[key])) + else + table.insert(elements, rp(jump_index)) + end + else + table.insert(elements, args[key]) + used_keys[key] = true + end + end + + -- iterate keeping a range from previous match, e.g. (not in_placeholder vs in_placeholder) + -- "Sample {2} string {3}." OR "Sample {2} string {3}." + -- left^--------^right OR left^-^right + local pattern = + string.format("[%s%s]", delimiters.esc_left, delimiters.esc_right) + local in_placeholder = false + local left = 0 + + while true do + local right = fmt:find(pattern, left + 1) + -- if not found, add the remaining part of string and finish + if right == nil then + assert( + not in_placeholder, + string.format('Missing closing delimiter: "%s"', fmt:sub(left)) + ) + add_text(fmt:sub(left + 1)) + break + end + -- check if the delimiters are escaped + local delim = fmt:sub(right, right) + local next_char = fmt:sub(right + 1, right + 1) + if not in_placeholder and delim == next_char then + -- add the previous part of the string with a single delimiter + add_text(fmt:sub(left + 1, right)) + -- and jump over the second one + left = right + 1 + -- "continue" + else -- non-escaped delimiter + assert( + delim + == (in_placeholder and delimiters.right or delimiters.left), + string.format( + 'Found unescaped %s %s placeholder; format[%d:%d]="%s"', + delim, + in_placeholder and "inside" or "outside", + left, + right, + fmt:sub(left, right) + ) + ) + -- add arg/text depending on current state + local add = in_placeholder and add_arg or add_text + add(fmt:sub(left + 1, right - 1)) + -- update state + left = right + in_placeholder = delim == delimiters.left + end + end + + -- sanity check: all arguments were used + if opts.strict then + for key, _ in pairs(args) do + assert( + used_keys[key], + string.format("Unused argument: args[%s]", key) + ) + end + end + + return elements +end + +-- Use a format string with placeholders to interpolate nodes. +-- +-- See `interpolate` documentation for details on the format. +-- +-- Arguments: +-- str: format string +-- nodes: snippet node or list of nodes +-- opts: optional table +-- trim_empty: boolean, remove whitespace-only first/last lines, default true +-- dedent: boolean, remove all common indent in `str`, default true +-- ... the rest is passed to `interpolate` +-- Returns: list of snippet nodes +local function format_nodes(str, nodes, opts) + local defaults = { + trim_empty = true, + dedent = true, + } + opts = vim.tbl_extend("force", defaults, opts or {}) + + -- allow to pass a single node + nodes = wrap_nodes(nodes) + + -- optimization: avoid splitting multiple times + local lines = nil + + lines = vim.split(str, "\n", true) + Str.process_multiline(lines, opts) + str = table.concat(lines, "\n") + + -- pop format_nodes's opts + for key, _ in ipairs(defaults) do + opts[key] = nil + end + + local parts = interpolate(str, nodes, opts) + return vim.tbl_map(function(part) + -- wrap strings in text nodes + if type(part) == "string" then + return text_node(vim.split(part, "\n", true)) + else + return part + end + end, parts) +end + +extend_decorator.register(format_nodes, { arg_indx = 3 }) + +return { + interpolate = interpolate, + format_nodes = format_nodes, + -- alias + fmt = format_nodes, + fmta = extend_decorator.apply(format_nodes, { delimiters = "<>" }), +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/init.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/init.lua new file mode 100644 index 00000000..c4be557d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/init.lua @@ -0,0 +1,164 @@ +local F = require("luasnip.nodes.functionNode").F +local SN = require("luasnip.nodes.snippet").SN +local D = require("luasnip.nodes.dynamicNode").D +local I = require("luasnip.nodes.insertNode").I + +local lambda = {} + +local function _concat(lines) + return table.concat(lines, "\n") +end + +local function make_lambda_args(node_args, imm_parent) + local snip = imm_parent.snippet + -- turn args' table-multilines into \n-multilines (needs to be possible + -- to process args with luas' string-functions). + local args = vim.tbl_map(_concat, node_args) + + setmetatable(args, { + __index = function(table, key) + local val + -- key may be capture or env-variable. + local num = key:match("CAPTURE(%d+)") + if num then + val = snip.captures[tonumber(num)] + else + -- env may be string or table. + if type(snip.env[key]) == "table" then + -- table- to \n-multiline. + val = _concat(snip.env[key]) + else + val = snip.env[key] + end + end + rawset(table, key, val) + return val + end, + }) + return args +end + +local function expr_to_fn(expr) + local _lambda = require("luasnip.extras._lambda") + + local fn_code = _lambda.instantiate(expr) + local function fn(args, snip) + -- to be sure, lambda may end with a `match` returning nil. + local out = fn_code(make_lambda_args(args, snip)) or "" + return vim.split(out, "\n") + end + return fn +end + +local LM = {} +function LM:__index(key) + return require("luasnip.extras._lambda")[key] +end +function LM:__call(expr, input_ids) + return F(expr_to_fn(expr), input_ids) +end + +setmetatable(lambda, LM) + +local function to_function(val, use_re) + if type(val) == "function" then + return val + end + if type(val) == "string" and not use_re then + return function() + return val + end + end + if type(val) == "string" and use_re then + return function(args) + return _concat(args[1]):match(val) + end + end + if lambda.isPE(val) then + local lmb = lambda.instantiate(val) + return function(args, snip) + return lmb(make_lambda_args(args, snip)) + end + end + assert(false, "Can't convert argument to function") +end + +local function match(index, _match, _then, _else) + assert(_match, "You have to pass at least 2 arguments") + + _match = to_function(_match, true) + _then = to_function(_then or function(args, snip) + local match_return = _match(args, snip) + return ( + ( + type(match_return) == "string" + -- _assume_ table of string. + or type(match_return) == "table" + ) and match_return + ) or "" + end) + _else = to_function(_else or "") + + local function func(args, snip) + local out = nil + if _match(args, snip) then + out = _then(args, snip) + else + out = _else(args, snip) + end + -- \n is used as a line-separator for simple strings. + return type(out) == "string" and vim.split(out, "\n") or out + end + + return F(func, index) +end + +return { + lambda = lambda, + match = match, + -- repeat a node. + rep = function(node_indx) + return F(function(args) + return args[1] + end, node_indx) + end, + -- Insert the output of a function. + partial = function(func, ...) + return F(function(_, _, ...) + return func(...) + end, {}, { user_args = { ... } }) + end, + nonempty = function(indx, text_if, text_if_not) + assert( + type(indx) == "number", + "this only checks one node for emptiness!" + ) + assert( + text_if, + "At least the text for nonemptiness has to be supplied." + ) + + return F(function(args) + return (args[1][1] ~= "" or #args[1] > 1) and text_if + or (text_if_not or "") + end, { + indx, + }) + end, + dynamic_lambda = function(pos, lambd, args_indcs) + local insert_preset_text_func = lambda.instantiate(lambd) + return D(pos, function(args, imm_parent) + -- to be sure, lambda may end with a `match` returning nil. + local out = insert_preset_text_func( + make_lambda_args(args, imm_parent) + ) or "" + return SN(pos, { + I(1, vim.split(out, "\n")), + }) + end, args_indcs) + end, + + --alias + l = lambda, + m = match, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/otf.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/otf.lua new file mode 100644 index 00000000..514ddb05 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/otf.lua @@ -0,0 +1,89 @@ +local ls = require("luasnip") +local cp = require("luasnip.util.functions").copy +local p = require("luasnip.extras._parser_combinator") +local dedent = require("luasnip.util.str").dedent +local M = {} + +local T = { EOL = "EOL", TXT = "TXT", INP = "INP" } + +local chunk = p.any( + p.map(p.literal("$$"), function() + return { T.TXT, "$" } + end), + p.map(p.literal("\n"), function() + return { T.EOL } + end), + p.map(p.seq(p.literal("$"), p.pattern("%w*")), function(c) + return { T.INP, c[1] } + end), + p.map(p.pattern("[^\n$]*"), function(c) + return { T.TXT, c } + end) +) + +M._snippet_chunks = p.star(chunk) + +function M._txt_to_snip(txt) + local t = ls.t + local s = ls.s + local i = ls.i + local f = ls.f + txt = dedent(txt) + + -- The parser does not handle empty strings + if txt == "" then + return s("", t({ "" })) + end + + local _, chunks, _ = M._snippet_chunks(txt, 1) + + local current_text_arg = { "" } + local nodes = {} + local know_inputs = {} + local last_input_pos = 0 + + for _, part in ipairs(chunks) do + if part[1] == T.TXT then + current_text_arg[#current_text_arg] = current_text_arg[#current_text_arg] + .. part[2] + elseif #current_text_arg > 1 or current_text_arg[1] ~= "" then + table.insert(nodes, t(current_text_arg)) + current_text_arg = { "" } + end + + if part[1] == T.EOL then + table.insert(current_text_arg, "") + elseif part[1] == T.INP then + local inp_pos = know_inputs[part[2]] + if inp_pos then + table.insert(nodes, f(cp, { inp_pos })) + else + last_input_pos = last_input_pos + 1 + know_inputs[part[2]] = last_input_pos + table.insert(nodes, i(last_input_pos, part[2])) + end + end + end + if #current_text_arg > 1 or current_text_arg[1] ~= "" then + table.insert(nodes, t(current_text_arg)) + end + return s("", nodes) +end + +local last_snip = nil +local last_reg = nil + +-- Create snippets On The Fly +-- It's advaisable not to use the default register as luasnip will probably +-- override it +function M.on_the_fly(regname) + regname = regname or "" + local reg = table.concat(vim.fn.getreg(regname, 1, true), "\n") -- Avoid eol in the last line + if last_reg ~= reg then + last_reg = reg + last_snip = M._txt_to_snip(reg) + end + ls.snip_expand(last_snip) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/postfix.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/postfix.lua new file mode 100644 index 00000000..c951b5a8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/postfix.lua @@ -0,0 +1,75 @@ +local snip = require("luasnip.nodes.snippet").S +local events = require("luasnip.util.events") +local extend_decorator = require("luasnip.util.extend_decorator") +local node_util = require("luasnip.nodes.util") +local util = require("luasnip.util.util") + +local matches = { + default = [[[%w%.%_%-%"%']+$]], + line = "^.+$", +} + +local function wrap_resolve_expand_params(match_pattern, user_resolve) + return function(snippet, line_to_cursor, match, captures) + if line_to_cursor:sub(1, -1 - #match):match(match_pattern) == nil then + return nil + end + + local pos = util.get_cursor_0ind() + local line_to_cursor_except_match = + line_to_cursor:sub(1, #line_to_cursor - #match) + local postfix_match = line_to_cursor_except_match:match(match_pattern) + or "" + local res = { + clear_region = { + from = { pos[1], pos[2] - #postfix_match - #match }, + to = pos, + }, + env_override = { + POSTFIX_MATCH = postfix_match, + }, + } + + if user_resolve then + local user_res = + user_resolve(snippet, line_to_cursor, match, captures) + if user_res then + res = vim.tbl_deep_extend("force", res, user_res, { + env_override = {}, + }) + else + return nil + end + end + return res + end +end + +local function postfix(context, nodes, opts) + opts = opts or {} + local user_callback = vim.tbl_get(opts, "callbacks", -1, events.pre_expand) + vim.validate({ + context = { context, { "string", "table" } }, + nodes = { nodes, "table" }, + opts = { opts, "table" }, + user_callback = { user_callback, { "nil", "function" } }, + }) + + context = node_util.wrap_context(context) + context.wordTrig = false + local match_pattern = context.match_pattern or matches.default + context.resolveExpandParams = + wrap_resolve_expand_params(match_pattern, context.resolveExpandParams) + + return snip(context, nodes, opts) +end +extend_decorator.register( + postfix, + { arg_indx = 1, extend = node_util.snippet_extend_context }, + { arg_indx = 3 } +) + +return { + postfix = postfix, + matches = matches, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/select_choice.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/select_choice.lua new file mode 100644 index 00000000..adca3a2a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/select_choice.lua @@ -0,0 +1,25 @@ +local session = require("luasnip.session") +local ls = require("luasnip") + +local function set_choice_callback(_, indx) + if not indx then + return + end + -- feed+immediately execute i to enter INSERT after vim.ui.input closes. + vim.api.nvim_feedkeys("i", "x", false) + ls.set_choice(indx) +end + +local function select_choice() + assert( + session.active_choice_nodes[vim.api.nvim_get_current_buf()], + "No active choiceNode" + ) + vim.ui.select( + ls.get_current_choices(), + { kind = "luasnip" }, + set_choice_callback + ) +end + +return select_choice diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/snip_location.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/snip_location.lua new file mode 100644 index 00000000..cb4618ad --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/snip_location.lua @@ -0,0 +1,190 @@ +local Source = require("luasnip.session.snippet_collection.source") +local util = require("luasnip.util.util") + +-- stylua: ignore +local tsquery_parse = + (vim.treesitter.query and vim.treesitter.query.parse) + and vim.treesitter.query.parse + or vim.treesitter.parse_query + +local M = {} + +-- return: 4-tuple, {start_line, start_col, end_line, end_col}, range of +-- function-call. +local function lua_find_function_call_node_at(bufnr, line) + local has_parser, parser = pcall(vim.treesitter.get_parser, bufnr, "lua") + if not has_parser then + error("Error while getting parser: " .. parser) + end + + local root = parser:parse()[1]:root() + local query = tsquery_parse("lua", [[(function_call) @f_call]]) + for _, node, _ in query:iter_captures(root, bufnr, line, line + 300) do + if node:range() == line then + return { node:range() } + end + end + error( + "Query for `(function_call)` starting at line %s did not yield a result." + ) +end + +local function range_highlight(line_start, line_end, hl_duration_ms) + -- make sure line_end is also visible. + vim.api.nvim_win_set_cursor(0, { line_end, 0 }) + vim.api.nvim_win_set_cursor(0, { line_start, 0 }) + + if hl_duration_ms > 0 then + local hl_buf = vim.api.nvim_get_current_buf() + + -- highlight snippet for 1000ms + local id = vim.api.nvim_buf_set_extmark( + hl_buf, + ls.session.ns_id, + line_start - 1, + 0, + { + -- one line below, at col 0 => entire last line is highlighted. + end_row = line_end - 1 + 1, + hl_group = "Visual", + } + ) + vim.defer_fn(function() + vim.api.nvim_buf_del_extmark(hl_buf, ls.session.ns_id, id) + end, hl_duration_ms) + end +end + +local function json_find_snippet_definition(bufnr, filetype, snippet_name) + local parser_ok, parser = pcall(vim.treesitter.get_parser, bufnr, filetype) + if not parser_ok then + error("Error while getting parser: " .. parser) + end + + local root = parser:parse()[1]:root() + -- don't want to pass through whether this file is json or jsonc, just use + -- parser-language. + local query = tsquery_parse( + parser:lang(), + ([[ + (pair + key: (string (string_content) @key (#eq? @key "%s")) + ) @snippet + ]]):format(snippet_name) + ) + for id, node, _ in query:iter_captures(root, bufnr) do + if + query.captures[id] == "snippet" + and node:parent():parent() == root + then + -- return first match. + return { node:range() } + end + end + + error( + ("Treesitter did not find the definition for snippet `%s`"):format( + snippet_name + ) + ) +end + +local function win_edit(file) + vim.api.nvim_command(":e " .. file) +end + +function M.jump_to_snippet(snip, opts) + opts = opts or {} + local hl_duration_ms = opts.hl_duration_ms or 1500 + local edit_fn = opts.edit_fn or win_edit + + local source = Source.get(snip) + if not source then + print("Snippet does not have a source.") + return + end + + edit_fn(source.file) + -- assumption: after this, file is the current buffer. + + if source.line and source.line_end then + -- happy path: we know both begin and end of snippet-definition. + range_highlight(source.line, source.line_end, hl_duration_ms) + return + end + + local fcall_range + local ft = util.ternary( + vim.bo[0].filetype ~= "", + vim.bo[0].filetype, + vim.api.nvim_buf_get_name(0):match("%.([^%.]+)$") + ) + if ft == "lua" then + if source.line then + -- in lua-file, can get region of definition via treesitter. + -- 0: current buffer. + local ok + ok, fcall_range = + pcall(lua_find_function_call_node_at, 0, source.line - 1) + if not ok then + print( + "Could not determine range for snippet-definition: " + .. fcall_range + ) + vim.api.nvim_win_set_cursor(0, { source.line, 0 }) + return + end + else + print("Can't jump to snippet: source does not provide line.") + return + end + -- matches *.json or *.jsonc. + elseif ft == "json" or ft == "jsonc" then + local ok + ok, fcall_range = pcall(json_find_snippet_definition, 0, ft, snip.name) + if not ok then + print( + "Could not determine range of snippet-definition: " + .. fcall_range + ) + return + end + else + print( + ("Don't know how to highlight snippet-definitions in current buffer `%s`.%s"):format( + vim.api.nvim_buf_get_name(0), + source.line ~= nil and " Jumping to `source.line`" or "" + ) + ) + + if source.line ~= nil then + vim.api.nvim_win_set_cursor(0, { source.line, 0 }) + end + return + end + assert(fcall_range ~= nil, "fcall_range is not nil") + + -- 1 is line_from, 3 is line_end. + -- +1 since range is row-0-indexed. + range_highlight(fcall_range[1] + 1, fcall_range[3] + 1, hl_duration_ms) + + local new_source = Source.from_location( + source.file, + { line = fcall_range[1] + 1, line_end = fcall_range[3] + 1 } + ) + Source.set(snip, new_source) +end + +function M.jump_to_active_snippet(opts) + local active_node = + require("luasnip.session").current_nodes[vim.api.nvim_get_current_buf()] + if not active_node then + print("No active snippet.") + return + end + + local snip = active_node.parent.snippet + M.jump_to_snippet(snip, opts) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/snippet_list.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/snippet_list.lua new file mode 100644 index 00000000..818714bc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/snippet_list.lua @@ -0,0 +1,96 @@ +local available = require("luasnip").available + +local function snip_info(snippet) + return { + name = snippet.name, + trigger = snippet.trigger, + description = snippet.description, + wordTrig = snippet.wordTrig and true or false, + regTrig = snippet.regTrig and true or false, + docstring = snippet:get_docstring(), + } +end + +local function get_name(buf) + return "LuaSnip://Snippets" +end + +local win_opts = { foldmethod = "indent" } +local buf_opts = { filetype = "lua" } + +local function set_win_opts(win, opts) + for opt, val in pairs(opts) do + vim.api.nvim_win_set_option(win, opt, val) + end +end + +local function set_buf_opts(buf, opts) + for opt, val in pairs(opts) do + vim.api.nvim_buf_set_option(buf, opt, val) + end +end + +local function make_scratch_buf(buf) + local opts = { + buftype = "nofile", + bufhidden = "wipe", + buflisted = false, + swapfile = false, + modified = false, + modeline = false, + } + + set_buf_opts(buf, opts) +end + +local function display_split(opts) + opts = opts or {} + opts.win_opts = opts.win_opts or win_opts + opts.buf_opts = opts.buf_opts or buf_opts + opts.get_name = opts.get_name or get_name + + return function(printer_result) + -- create and open buffer on right vertical split + vim.cmd("botright vnew") + + -- get buf and win handle + local buf = vim.api.nvim_get_current_buf() + local win = vim.api.nvim_get_current_win() + + -- make scratch buffer + vim.api.nvim_buf_set_name(buf, opts.get_name(buf)) + make_scratch_buf(buf) + + -- disable diagnostics + vim.diagnostic.disable(buf) + + -- set any extra win and buf opts + set_win_opts(win, opts.win_opts) + set_buf_opts(buf, opts.buf_opts) + + -- dump snippets + local replacement = vim.split(printer_result, "\n") + vim.api.nvim_buf_set_lines(buf, 0, 0, false, replacement) + + -- make it unmodifiable at this point + vim.api.nvim_buf_set_option(buf, "modifiable", false) + end +end + +local function open(opts) + opts = opts or {} + opts.snip_info = opts.snip_info or snip_info + opts.printer = opts.printer or vim.inspect + opts.display = opts.display or display_split() + + -- load snippets before changing windows/buffers + local snippets = available(opts.snip_info) + + -- open snippets + opts.display(opts.printer(snippets)) +end + +return { + open = open, + options = { display = display_split }, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/treesitter_postfix.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/treesitter_postfix.lua new file mode 100644 index 00000000..b313d92d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/extras/treesitter_postfix.lua @@ -0,0 +1,324 @@ +if vim.version().major == 0 and vim.version().minor < 9 then + -- need LanguageTree:tree_for_range and don't want to go through the hassle + -- of differentiating multiple version of query.get/parse. + error("treesitter_postfix does not support neovim < 0.9") +end + +local snip = require("luasnip.nodes.snippet").S +local ts = require("luasnip.extras._treesitter") +local node_util = require("luasnip.nodes.util") +local extend_decorator = require("luasnip.util.extend_decorator") +local tbl = require("luasnip.util.table") +local util = require("luasnip.util.util") + +--- Normalize the arguments passed to treesitter_postfix into a function that +--- returns treesitter-matches to the specified query+captures. +---@param opts LuaSnip.extra.MatchTSNodeOpts +---@return LuaSnip.extra.MatchTSNodeFunc +local function generate_match_tsnode_func(opts) + local match_opts = {} + + if opts.query then + match_opts.query = + vim.treesitter.query.parse(opts.query_lang, opts.query) + else + match_opts.query = vim.treesitter.query.get( + opts.query_lang, + opts.query_name or "luasnip" + ) + end + + match_opts.generator = ts.captures_iter(opts.match_captures or "prefix") + + if type(opts.select) == "function" then + match_opts.selector = opts.select + elseif type(opts.select) == "string" then + match_opts.selector = ts.builtin_tsnode_selectors[opts.select] + assert( + match_opts.selector, + "Selector " .. opts.select .. "is not known" + ) + else + match_opts.selector = ts.builtin_tsnode_selectors.any + end + + ---@param parser LuaSnip.extra.TSParser + ---@param pos { [1]: number, [2]: number } + return function(parser, pos) + return parser:match_at( + match_opts, --[[@as LuaSnip.extra.MatchTSNodeOpts]] + pos + ) + end +end + +local function make_reparse_enter_and_leave_func( + reparse, + bufnr, + trigger_region, + trigger +) + if reparse == "live" then + local context = ts.FixBufferContext.new(bufnr, trigger_region, trigger) + return function() + return context:enter() + end, function(_) + context:leave() + end + elseif reparse == "copy" then + local parser, source = + ts.reparse_buffer_after_removing_match(bufnr, trigger_region) + return function() + return parser, source + end, function() + parser:destroy() + end + else + return function() + return vim.treesitter.get_parser(bufnr), bufnr + end, function(_) end + end +end + +---Optionally parse the buffer +---@param reparse boolean|string|nil +---@param real_resolver function +---@return fun(snippet, line_to_cursor, matched_trigger, captures):table? +local function wrap_with_reparse_context(reparse, real_resolver) + return function(snippet, line_to_cursor, matched_trigger, captures) + local bufnr = vim.api.nvim_win_get_buf(0) + local cursor = util.get_cursor_0ind() + local trigger_region = { + row = cursor[1], + col_range = { + -- includes from, excludes to. + cursor[2] - #matched_trigger, + cursor[2], + }, + } + + local enter, leave = make_reparse_enter_and_leave_func( + reparse, + bufnr, + trigger_region, + matched_trigger + ) + local parser, source = enter() + if parser == nil or source == nil then + return nil + end + + local ret = real_resolver( + snippet, + line_to_cursor, + matched_trigger, + captures, + parser, + source, + bufnr, + { cursor[1], cursor[2] - #matched_trigger } + ) + + leave() + + return ret + end +end + +---@param match_tsnode LuaSnip.extra.MatchTSNodeFunc Determines the constraints on the matched node. +local function generate_resolve_expand_param(match_tsnode, user_resolver) + ---@param snippet any + ---@param line_to_cursor string + ---@param matched_trigger string + ---@param captures any + ---@param parser LanguageTree + ---@param source number|string + ---@param bufnr number + ---@param pos { [1]: number, [2]: number } + return function( + snippet, + line_to_cursor, + matched_trigger, + captures, + parser, + source, + bufnr, + pos + ) + local ts_parser = ts.TSParser.new(bufnr, parser, source) + if ts_parser == nil then + return + end + + local row, col = unpack(pos) + + local best_match, prefix_node = match_tsnode(ts_parser, { row, col }) + + if best_match == nil or prefix_node == nil then + return nil + end + + local start_row, start_col, _, _ = prefix_node:range() + + local env = { + LS_TSMATCH = vim.split(ts_parser:get_node_text(prefix_node), "\n"), + -- filled subsequently. + LS_TSDATA = {}, + } + for capture_name, node in pairs(best_match) do + env["LS_TSCAPTURE_" .. capture_name:upper()] = + vim.split(ts_parser:get_node_text(node), "\n") + + local from_r, from_c, to_r, to_c = node:range() + env.LS_TSDATA[capture_name] = { + type = node:type(), + range = { { from_r, from_c }, { to_r, to_c } }, + } + end + + local ret = { + trigger = matched_trigger, + captures = captures, + clear_region = { + from = { + start_row, + start_col, + }, + to = { + pos[1], + pos[2] + #matched_trigger, + }, + }, + env_override = env, + } + + if user_resolver then + local user_res = user_resolver( + snippet, + line_to_cursor, + matched_trigger, + captures + ) + if user_res then + ret = vim.tbl_deep_extend( + "force", + ret, + user_res, + { env_override = {} } + ) + else + return nil + end + end + + return ret + end +end + +local function generate_simple_parent_lookup_function(lookup_fun) + ---@param types string|string[] + ---@return LuaSnip.extra.MatchTSNodeFunc + return function(types) + local type_checker = tbl.list_to_set(types) + ---@param parser LuaSnip.extra.TSParser + ---@param pos { [1]: number, [2]: number } + return function(parser, pos) + -- check node just before the position. + local root = parser:get_node_at_pos({ pos[1], pos[2] - 1 }) + + if root == nil then + return + end + + ---@param node TSNode + local check_node_exclude_pos = function(node) + local _, _, end_row, end_col = node:range(false) + return end_row == pos[1] and end_col == pos[2] + end + ---@param node TSNode + local check_node_type = function(node) + return type_checker[node:type()] + end + + local prefix_node = lookup_fun(root, function(node) + return check_node_type(node) and check_node_exclude_pos(node) + end) + if prefix_node == nil then + return nil, nil + end + return {}, prefix_node + end + end +end + +---@param n number +local function find_nth_parent(n) + ---@param parser LuaSnip.extra.TSParser + ---@param pos { [1]: number, [2]: number } + return function(parser, pos) + local inner_node = parser:get_node_at_pos({ pos[1], pos[2] - 1 }) + + if inner_node == nil then + return + end + + ---@param node TSNode + local check_node_exclude_pos = function(node) + local _, _, end_row, end_col = node:range(false) + return end_row == pos[1] and end_col == pos[2] + end + + return {}, ts.find_nth_parent(inner_node, n, check_node_exclude_pos) + end +end + +local function treesitter_postfix(context, nodes, opts) + opts = opts or {} + vim.validate({ + context = { context, { "string", "table" } }, + nodes = { nodes, "table" }, + opts = { opts, "table" }, + }) + + context = node_util.wrap_context(context) + context.wordTrig = false + + ---@type LuaSnip.extra.MatchTSNodeFunc + local match_tsnode_func + if type(context.matchTSNode) == "function" then + match_tsnode_func = context.matchTSNode + else + match_tsnode_func = generate_match_tsnode_func(context.matchTSNode) + end + + local expand_params_resolver = generate_resolve_expand_param( + match_tsnode_func, + context.resolveExpandParams + ) + + context.resolveExpandParams = + wrap_with_reparse_context(context.reparseBuffer, expand_params_resolver) + + return snip(context, nodes, opts) +end + +extend_decorator.register( + treesitter_postfix, + { arg_indx = 1, extend = node_util.snippet_extend_context }, + { arg_indx = 3 } +) + +return { + treesitter_postfix = treesitter_postfix, + builtin = { + tsnode_matcher = { + find_topmost_types = generate_simple_parent_lookup_function( + ts.find_topmost_parent + ), + find_first_types = generate_simple_parent_lookup_function( + ts.find_first_parent + ), + find_nth_parent = find_nth_parent, + }, + }, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/health.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/health.lua new file mode 100644 index 00000000..a06fea12 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/health.lua @@ -0,0 +1,14 @@ +return { + check = function() + vim.health.start("luasnip") + local jsregexp = require("luasnip.util.jsregexp") + if jsregexp then + vim.health.ok("jsregexp is installed") + else + vim.health.warn([[ + For Variable/Placeholder-transformations, luasnip requires + the jsregexp library. See `:h luasnip-lsp-snippets-transformations` for advice + ]]) + end + end, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/init.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/init.lua new file mode 100644 index 00000000..14d07443 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/init.lua @@ -0,0 +1,896 @@ +local util = require("luasnip.util.util") +local lazy_table = require("luasnip.util.lazy_table") +local types = require("luasnip.util.types") +local node_util = require("luasnip.nodes.util") + +local session = require("luasnip.session") +local snippet_collection = require("luasnip.session.snippet_collection") +local Environ = require("luasnip.util.environ") +local extend_decorator = require("luasnip.util.extend_decorator") + +local loader = require("luasnip.loaders") + +local next_expand = nil +local next_expand_params = nil +local ls +local luasnip_data_dir = vim.fn.stdpath("cache") .. "/luasnip" + +local log = require("luasnip.util.log").new("main") + +local function get_active_snip() + local node = session.current_nodes[vim.api.nvim_get_current_buf()] + if not node then + return nil + end + while node.parent do + node = node.parent + end + return node +end + +-- returns matching snippet (needs to be copied before usage!) and its expand- +-- parameters(trigger and captures). params are returned here because there's +-- no need to recalculate them. +local function match_snippet(line, type) + return snippet_collection.match_snippet( + line, + util.get_snippet_filetypes(), + type + ) +end + +-- ft: +-- * string: interpreted as filetype, return corresponding snippets. +-- * nil: return snippets for all filetypes: +-- { +-- lua = {...}, +-- cpp = {...}, +-- ... +-- } +-- opts: optional args, can contain `type`, either "snippets" or "autosnippets". +-- +-- return table, may be empty. +local function get_snippets(ft, opts) + opts = opts or {} + + return snippet_collection.get_snippets(ft, opts.type or "snippets") or {} +end + +local function default_snip_info(snip) + return { + name = snip.name, + trigger = snip.trigger, + description = snip.description, + wordTrig = snip.wordTrig and true or false, + regTrig = snip.regTrig and true or false, + } +end + +local function available(snip_info) + snip_info = snip_info or default_snip_info + + local fts = util.get_snippet_filetypes() + local res = {} + for _, ft in ipairs(fts) do + res[ft] = {} + for _, snip in ipairs(get_snippets(ft)) do + if not snip.invalidated then + table.insert(res[ft], snip_info(snip)) + end + end + for _, snip in ipairs(get_snippets(ft, { type = "autosnippets" })) do + if not snip.invalidated then + table.insert(res[ft], snip_info(snip)) + end + end + end + return res +end + +local unlink_set_adjacent_as_current +local function unlink_set_adjacent_as_current_no_log(snippet) + -- prefer setting previous/outer insertNode as current node. + local next_current = + -- either pick i0 of snippet before, or i(-1) of next snippet. + snippet.prev.prev or snippet:next_node() + snippet:remove_from_jumplist() + + if next_current then + -- if snippet was active before, we need to now set its parent to be no + -- longer inner_active. + if + snippet.parent_node == next_current and next_current.inner_active + then + snippet.parent_node:input_leave_children() + else + -- set no_move. + local ok, err = pcall(next_current.input_enter, next_current, true) + if not ok then + -- this won't try to set the previously broken snippet as + -- current, since that link is removed in + -- `remove_from_jumplist`. + unlink_set_adjacent_as_current( + next_current.parent.snippet, + "Error while setting adjacent snippet as current node: %s", + err + ) + end + end + end + + session.current_nodes[vim.api.nvim_get_current_buf()] = next_current +end +function unlink_set_adjacent_as_current(snippet, reason, ...) + log.warn("Removing snippet %s: %s", snippet.trigger, reason:format(...)) + unlink_set_adjacent_as_current_no_log(snippet) +end + +local function unlink_current() + local current = session.current_nodes[vim.api.nvim_get_current_buf()] + if not current then + print("No active Snippet") + return + end + unlink_set_adjacent_as_current_no_log(current.parent.snippet) +end + +-- return next active node. +local function safe_jump_current(dir, no_move, dry_run) + local node = session.current_nodes[vim.api.nvim_get_current_buf()] + if not node then + return nil + end + + local ok, res = pcall(node.jump_from, node, dir, no_move, dry_run) + if ok then + return res + else + local snip = node.parent.snippet + + unlink_set_adjacent_as_current( + snip, + "Removing snippet `%s` due to error %s", + snip.trigger, + res + ) + return session.current_nodes[vim.api.nvim_get_current_buf()] + end +end +local function jump(dir) + local current = session.current_nodes[vim.api.nvim_get_current_buf()] + if current then + local next_node = util.no_region_check_wrap(safe_jump_current, dir) + if next_node == nil then + session.current_nodes[vim.api.nvim_get_current_buf()] = nil + return true + end + if session.config.exit_roots then + if next_node.pos == 0 and next_node.parent.parent_node == nil then + session.current_nodes[vim.api.nvim_get_current_buf()] = nil + return true + end + end + session.current_nodes[vim.api.nvim_get_current_buf()] = next_node + return true + else + return false + end +end +local function jump_destination(dir) + -- dry run of jump (+no_move ofc.), only retrieves destination-node. + return safe_jump_current(dir, true, { active = {} }) +end + +local function jumpable(dir) + -- node is jumpable if there is a destination. + return jump_destination(dir) + ~= session.current_nodes[vim.api.nvim_get_current_buf()] +end + +local function expandable() + next_expand, next_expand_params = + match_snippet(util.get_current_line_to_cursor(), "snippets") + return next_expand ~= nil +end + +local function expand_or_jumpable() + return expandable() or jumpable(1) +end + +local function in_snippet() + -- check if the cursor on a row inside a snippet. + local node = session.current_nodes[vim.api.nvim_get_current_buf()] + if not node then + return false + end + local snippet = node.parent.snippet + local ok, snip_begin_pos, snip_end_pos = + pcall(snippet.mark.pos_begin_end, snippet.mark) + if not ok then + -- if there was an error getting the position, the snippets text was + -- most likely removed, resulting in messed up extmarks -> error. + -- remove the snippet. + unlink_set_adjacent_as_current( + snippet, + "Error while getting extmark-position: %s", + snip_begin_pos + ) + return + end + local pos = vim.api.nvim_win_get_cursor(0) + if pos[1] - 1 >= snip_begin_pos[1] and pos[1] - 1 <= snip_end_pos[1] then + return true -- cursor not on row inside snippet + end +end + +local function expand_or_locally_jumpable() + return expandable() or (in_snippet() and jumpable(1)) +end + +local function locally_jumpable(dir) + return in_snippet() and jumpable(dir) +end + +local function _jump_into_default(snippet) + return util.no_region_check_wrap(snippet.jump_into, snippet, 1) +end + +-- opts.clear_region: table, keys `from` and `to`, both (0,0)-indexed. +local function snip_expand(snippet, opts) + local snip = snippet:copy() + + opts = opts or {} + opts.expand_params = opts.expand_params or {} + -- override with current position if none given. + opts.pos = opts.pos or util.get_cursor_0ind() + opts.jump_into_func = opts.jump_into_func or _jump_into_default + opts.indent = vim.F.if_nil(opts.indent, true) + + snip.trigger = opts.expand_params.trigger or snip.trigger + snip.captures = opts.expand_params.captures or {} + + local info = + { trigger = snip.trigger, captures = snip.captures, pos = opts.pos } + local env = Environ:new(info) + Environ:override(env, opts.expand_params.env_override or {}) + + local pos_id = vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + opts.pos[1], + opts.pos[2], + -- track position between pos[2]-1 and pos[2]. + { right_gravity = false } + ) + + -- optionally clear text. Text has to be cleared befor jumping into the new + -- snippet, as the cursor-position can end up in the wrong position (to be + -- precise the text will be moved, the cursor will stay at the same + -- position, which is just as bad) if text before the cursor, on the same + -- line is cleared. + if opts.clear_region then + vim.api.nvim_buf_set_text( + 0, + opts.clear_region.from[1], + opts.clear_region.from[2], + opts.clear_region.to[1], + opts.clear_region.to[2], + { "" } + ) + end + + local snip_parent_node = snip:trigger_expand( + session.current_nodes[vim.api.nvim_get_current_buf()], + pos_id, + env, + opts.indent + ) + + -- jump_into-callback returns new active node. + session.current_nodes[vim.api.nvim_get_current_buf()] = + opts.jump_into_func(snip) + + local buf_snippet_roots = + session.snippet_roots[vim.api.nvim_get_current_buf()] + if not session.config.keep_roots and #buf_snippet_roots > 1 then + -- if history is not set, and there is more than one snippet-root, + -- remove the other one. + -- The nice thing is: since we maintain that #buf_snippet_roots == 1 + -- whenever outside of this function, we know that if we're here, it's + -- because this snippet was just inserted into buf_snippet_roots. + -- Armed with this knowledge, we can just check which of the roots is + -- this snippet, and remove the other one. + buf_snippet_roots[buf_snippet_roots[1] == snip and 2 or 1]:remove_from_jumplist() + end + + -- stores original snippet, it doesn't contain any data from expansion. + session.last_expand_snip = snippet + session.last_expand_opts = opts + + -- set last action for vim-repeat. + -- will silently fail if vim-repeat isn't available. + -- -1 to disable count. + vim.cmd([[silent! call repeat#set("\luasnip-expand-repeat", -1)]]) + + return snip +end + +---Find a snippet matching the current cursor-position. +---@param opts table: may contain: +--- - `jump_into_func`: passed through to `snip_expand`. +---@return boolean: whether a snippet was expanded. +local function expand(opts) + local expand_params + local snip + -- find snip via next_expand (set from previous expandable()) or manual matching. + if next_expand ~= nil then + snip = next_expand + expand_params = next_expand_params + + next_expand = nil + next_expand_params = nil + else + snip, expand_params = + match_snippet(util.get_current_line_to_cursor(), "snippets") + end + if snip then + local jump_into_func = opts and opts.jump_into_func + + local cursor = util.get_cursor_0ind() + + local clear_region = expand_params.clear_region + or { + from = { + cursor[1], + cursor[2] - #expand_params.trigger, + }, + to = cursor, + } + + -- override snip with expanded copy. + snip = snip_expand(snip, { + expand_params = expand_params, + -- clear trigger-text. + clear_region = clear_region, + jump_into_func = jump_into_func, + }) + + return true + end + return false +end + +local function expand_auto() + local snip, expand_params = + match_snippet(util.get_current_line_to_cursor(), "autosnippets") + if snip then + local cursor = util.get_cursor_0ind() + local clear_region = expand_params.clear_region + or { + from = { + cursor[1], + cursor[2] - #expand_params.trigger, + }, + to = cursor, + } + snip = snip_expand(snip, { + expand_params = expand_params, + -- clear trigger-text. + clear_region = clear_region, + }) + end +end + +local function expand_repeat() + -- prevent clearing text with repeated expand. + session.last_expand_opts.clear_region = nil + session.last_expand_opts.pos = nil + + snip_expand(session.last_expand_snip, session.last_expand_opts) +end + +-- return true and expand snippet if expandable, return false if not. +local function expand_or_jump() + if expand() then + return true + end + if jump(1) then + return true + end + return false +end + +local function lsp_expand(body, opts) + -- expand snippet as-is. + snip_expand( + ls.parser.parse_snippet( + "", + body, + { trim_empty = false, dedent = false } + ), + opts + ) +end + +local function choice_active() + return session.active_choice_nodes[vim.api.nvim_get_current_buf()] ~= nil +end + +-- attempts to do some action on the snippet (like change_choice, set_choice), +-- if it fails the snippet is removed and the next snippet becomes the current node. +-- ... is passed to pcall as-is. +local function safe_choice_action(snip, ...) + local ok, res = pcall(...) + if ok then + return res + else + -- not very elegant, but this way we don't have a near + -- re-implementation of unlink_current. + unlink_set_adjacent_as_current( + snip, + "Removing snippet `%s` due to error %s", + snip.trigger, + res + ) + return session.current_nodes[vim.api.nvim_get_current_buf()] + end +end +local function change_choice(val) + local active_choice = + session.active_choice_nodes[vim.api.nvim_get_current_buf()] + assert(active_choice, "No active choiceNode") + local new_active = util.no_region_check_wrap( + safe_choice_action, + active_choice.parent.snippet, + active_choice.change_choice, + active_choice, + val, + session.current_nodes[vim.api.nvim_get_current_buf()] + ) + session.current_nodes[vim.api.nvim_get_current_buf()] = new_active +end + +local function set_choice(choice_indx) + local active_choice = + session.active_choice_nodes[vim.api.nvim_get_current_buf()] + assert(active_choice, "No active choiceNode") + local choice = active_choice.choices[choice_indx] + assert(choice, "Invalid Choice") + local new_active = util.no_region_check_wrap( + safe_choice_action, + active_choice.parent.snippet, + active_choice.set_choice, + active_choice, + choice, + session.current_nodes[vim.api.nvim_get_current_buf()] + ) + session.current_nodes[vim.api.nvim_get_current_buf()] = new_active +end + +local function get_current_choices() + local active_choice = + session.active_choice_nodes[vim.api.nvim_get_current_buf()] + assert(active_choice, "No active choiceNode") + + local choice_lines = {} + + active_choice:update_static_all() + for i, choice in ipairs(active_choice.choices) do + choice_lines[i] = table.concat(choice:get_docstring(), "\n") + end + + return choice_lines +end + +local function active_update_dependents() + local active = session.current_nodes[vim.api.nvim_get_current_buf()] + -- special case for startNode, cannot focus on those (and they can't + -- have dependents) + -- don't update if a jump/change_choice is in progress. + if not session.jump_active and active and active.pos > 0 then + -- Save cursor-pos to restore later. + local cur = util.get_cursor_0ind() + local cur_mark = vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + cur[1], + cur[2], + { right_gravity = false } + ) + + local ok, err = pcall(active.update_dependents, active) + if not ok then + unlink_set_adjacent_as_current( + active.parent.snippet, + "Error while updating dependents for snippet %s due to error %s", + active.parent.snippet.trigger, + err + ) + return + end + + -- 'restore' orientation of extmarks, may have been changed by some set_text or similar. + ok, err = pcall(active.focus, active) + if not ok then + unlink_set_adjacent_as_current( + active.parent.snippet, + "Error while entering node in snippet %s: %s", + active.parent.snippet.trigger, + err + ) + + return + end + + -- Don't account for utf, nvim_win_set_cursor doesn't either. + cur = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + cur_mark, + { details = false } + ) + util.set_cursor_0ind(cur) + end +end + +local function store_snippet_docstrings(snippet_table) + -- ensure the directory exists. + -- 493 = 0755 + vim.loop.fs_mkdir(luasnip_data_dir, 493) + + -- fs_open() with w+ creates the file if nonexistent. + local docstring_cache_fd = vim.loop.fs_open( + luasnip_data_dir .. "/docstrings.json", + "w+", + -- 420 = 0644 + 420 + ) + + -- get size for fs_read() + local cache_size = vim.loop.fs_fstat(docstring_cache_fd).size + local file_could_be_read, docstrings = pcall( + util.json_decode, + -- offset 0. + vim.loop.fs_read(docstring_cache_fd, cache_size, 0) + ) + docstrings = file_could_be_read and docstrings or {} + + for ft, snippets in pairs(snippet_table) do + if not docstrings[ft] then + docstrings[ft] = {} + end + for _, snippet in ipairs(snippets) do + docstrings[ft][snippet.trigger] = snippet:get_docstring() + end + end + + vim.loop.fs_write(docstring_cache_fd, util.json_encode(docstrings)) +end + +local function load_snippet_docstrings(snippet_table) + -- ensure the directory exists. + -- 493 = 0755 + vim.loop.fs_mkdir(luasnip_data_dir, 493) + + -- fs_open() with "r" returns nil if the file doesn't exist. + local docstring_cache_fd = vim.loop.fs_open( + luasnip_data_dir .. "/docstrings.json", + "r", + -- 420 = 0644 + 420 + ) + + if not docstring_cache_fd then + error("Cached docstrings could not be read!") + return + end + -- get size for fs_read() + local cache_size = vim.loop.fs_fstat(docstring_cache_fd).size + local docstrings = util.json_decode( + -- offset 0. + vim.loop.fs_read(docstring_cache_fd, cache_size, 0) + ) + + for ft, snippets in pairs(snippet_table) do + -- skip if fieltype not in cache. + if docstrings[ft] then + for _, snippet in ipairs(snippets) do + -- only set if it hasn't been set already. + if not snippet.docstring then + snippet.docstring = docstrings[ft][snippet.trigger] + end + end + end + end +end + +local function unlink_current_if_deleted() + local node = session.current_nodes[vim.api.nvim_get_current_buf()] + if not node then + return + end + local snippet = node.parent.snippet + + -- extmarks_valid checks that + -- * textnodes that should contain text still do so, and + -- * that extmarks still fulfill all expectations (should be successive, no gaps, etc.) + if not snippet:extmarks_valid() then + unlink_set_adjacent_as_current( + snippet, + "Detected deletion of snippet `%s`, removing it", + snippet.trigger + ) + end +end + +local function exit_out_of_region(node) + -- if currently jumping via luasnip or no active node: + if session.jump_active or not node then + return + end + + local pos = util.get_cursor_0ind() + local snippet + if node.type == types.snippet then + snippet = node + else + snippet = node.parent.snippet + end + + -- find root-snippet. + while snippet.parent_node do + snippet = snippet.parent_node.parent.snippet + end + + local ok, snip_begin_pos, snip_end_pos = + pcall(snippet.mark.pos_begin_end, snippet.mark) + + if not ok then + unlink_set_adjacent_as_current( + snippet, + "Error while getting extmark-position: %s", + snip_begin_pos + ) + return + end + + -- stylua: ignore + -- leave if curser before or behind snippet + if pos[1] < snip_begin_pos[1] or + pos[1] > snip_end_pos[1] then + + -- make sure the snippet can safely be entered, since it may have to + -- be, in `refocus`. + if not snippet:extmarks_valid() then + unlink_set_adjacent_as_current(snippet, "Leaving snippet-root due to invalid extmarks.") + return + end + + local next_active = snippet.insert_nodes[0] + -- if there is a snippet nested into the $0, enter its $0 instead, + -- recursively. + -- This is to ensure that a jump forward after leaving the region of a + -- root will jump to the next root, or not result in a jump at all. + while next_active.inner_first do + -- make sure next_active is nested into completely intact + -- snippets, since that is a precondition on the to-node of + if not next_active.inner_first:extmarks_valid() then + next_active.inner_first:remove_from_jumplist() + else + -- inner_first is always the snippet, not the -1-node. + next_active = next_active.inner_first.insert_nodes[0] + end + end + + node_util.refocus(node, next_active) + session.current_nodes[vim.api.nvim_get_current_buf()] = next_active + end +end + +-- ft string, extend_ft table of strings. +local function filetype_extend(ft, extend_ft) + vim.list_extend(session.ft_redirect[ft], extend_ft) + session.ft_redirect[ft] = util.deduplicate(session.ft_redirect[ft]) +end + +-- ft string, fts table of strings. +local function filetype_set(ft, fts) + session.ft_redirect[ft] = util.deduplicate(fts) +end + +local function cleanup() + -- Use this to reload luasnip + vim.api.nvim_exec_autocmds( + "User", + { pattern = "LuasnipCleanup", modeline = false } + ) + -- clear all snippets. + snippet_collection.clear_snippets() + loader.cleanup() +end + +local function refresh_notify(ft) + snippet_collection.refresh_notify(ft) +end + +local function setup_snip_env() + local combined_table = vim.tbl_extend("force", _G, session.config.snip_env) + -- TODO: if desired, take into account _G's __index before looking into + -- snip_env's __index. + setmetatable(combined_table, getmetatable(session.config.snip_env)) + + setfenv(2, combined_table) +end +local function get_snip_env() + return session.get_snip_env() +end + +local function get_id_snippet(id) + return snippet_collection.get_id_snippet(id) +end + +local function add_snippets(ft, snippets, opts) + -- don't use yet, not available in some neovim-versions. + -- + -- vim.validate({ + -- filetype = { ft, { "string", "nil" } }, + -- snippets = { snippets, "table" }, + -- opts = { opts, { "table", "nil" } }, + -- }) + + opts = opts or {} + opts.refresh_notify = opts.refresh_notify or true + -- alternatively, "autosnippets" + opts.type = opts.type or "snippets" + + -- if ft is nil, snippets already has this format. + if ft then + snippets = { + [ft] = snippets, + } + end + + snippet_collection.add_snippets(snippets, opts) + + if opts.refresh_notify then + for ft_, _ in pairs(snippets) do + refresh_notify(ft_) + end + end +end + +local function clean_invalidated(opts) + opts = opts or {} + snippet_collection.clean_invalidated(opts) +end + +local function activate_node(opts) + opts = opts or {} + local pos = opts.pos or util.get_cursor_0ind() + local strict = vim.F.if_nil(opts.strict, false) + local select = vim.F.if_nil(opts.select, true) + + -- find tree-node the snippet should be inserted at (could be before another node). + local _, _, _, node = node_util.snippettree_find_undamaged_node(pos, { + tree_respect_rgravs = false, + tree_preference = node_util.binarysearch_preference.inside, + snippet_mode = "interactive", + }) + + if not node then + error("No Snippet at that position") + return + end + + -- only activate interactive nodes, or nodes that are immediately nested + -- inside a choiceNode. + if not node:interactive() then + if strict then + error("Refusing to activate a non-interactive node.") + return + else + -- fall back to known insertNode. + -- snippet.insert_nodes[1] may be preferable, but that is not + -- certainly an insertNode (and does not even certainly contain an + -- insertNode, think snippetNode with only textNode). + -- We could *almost* find the first activateable node by + -- dry_run-jumping into the snippet, but then we'd also need some + -- mechanism for setting the active-state of all nodes to false, + -- which we don't yet have. + -- + -- Instead, just choose -1-node, and allow jumps from there, which + -- is much simpler. + node = node.parent.snippet.prev + end + end + + node_util.refocus( + session.current_nodes[vim.api.nvim_get_current_buf()], + node + ) + if select then + -- input_enter node again, to get highlight and the like. + -- One side-effect of this is that an event will be execute twice, but I + -- feel like that is a trade-off worth doing, since it otherwise refocus + -- would have to be more complicated (or at least, restructured). + node:input_enter() + end + session.current_nodes[vim.api.nvim_get_current_buf()] = node +end + +-- make these lazy, such that we don't have to load them before it's really +-- necessary (drives up cost of initial load, otherwise). +-- stylua: ignore +local ls_lazy = { + s = function() return require("luasnip.nodes.snippet").S end, + sn = function() return require("luasnip.nodes.snippet").SN end, + t = function() return require("luasnip.nodes.textNode").T end, + f = function() return require("luasnip.nodes.functionNode").F end, + i = function() return require("luasnip.nodes.insertNode").I end, + c = function() return require("luasnip.nodes.choiceNode").C end, + d = function() return require("luasnip.nodes.dynamicNode").D end, + r = function() return require("luasnip.nodes.restoreNode").R end, + snippet = function() return require("luasnip.nodes.snippet").S end, + snippet_node = function() return require("luasnip.nodes.snippet").SN end, + parent_indexer = function() return require("luasnip.nodes.snippet").P end, + indent_snippet_node = function() return require("luasnip.nodes.snippet").ISN end, + text_node = function() return require("luasnip.nodes.textNode").T end, + function_node = function() return require("luasnip.nodes.functionNode").F end, + insert_node = function() return require("luasnip.nodes.insertNode").I end, + choice_node = function() return require("luasnip.nodes.choiceNode").C end, + dynamic_node = function() return require("luasnip.nodes.dynamicNode").D end, + restore_node = function() return require("luasnip.nodes.restoreNode").R end, + parser = function() return require("luasnip.util.parser") end, + config = function() return require("luasnip.config") end, + multi_snippet = function() return require("luasnip.nodes.multiSnippet").new_multisnippet end, + snippet_source = function() return require("luasnip.session.snippet_collection.source") end, + select_keys = function() return require("luasnip.util.select").select_keys end +} + +ls = lazy_table({ + expand_or_jumpable = expand_or_jumpable, + expand_or_locally_jumpable = expand_or_locally_jumpable, + locally_jumpable = locally_jumpable, + jumpable = jumpable, + expandable = expandable, + in_snippet = in_snippet, + expand = expand, + snip_expand = snip_expand, + expand_repeat = expand_repeat, + expand_auto = expand_auto, + expand_or_jump = expand_or_jump, + jump = jump, + get_active_snip = get_active_snip, + choice_active = choice_active, + change_choice = change_choice, + set_choice = set_choice, + get_current_choices = get_current_choices, + unlink_current = unlink_current, + lsp_expand = lsp_expand, + active_update_dependents = active_update_dependents, + available = available, + exit_out_of_region = exit_out_of_region, + load_snippet_docstrings = load_snippet_docstrings, + store_snippet_docstrings = store_snippet_docstrings, + unlink_current_if_deleted = unlink_current_if_deleted, + filetype_extend = filetype_extend, + filetype_set = filetype_set, + add_snippets = add_snippets, + get_snippets = get_snippets, + get_id_snippet = get_id_snippet, + setup_snip_env = setup_snip_env, + get_snip_env = get_snip_env, + clean_invalidated = clean_invalidated, + get_snippet_filetypes = util.get_snippet_filetypes, + jump_destination = jump_destination, + session = session, + cleanup = cleanup, + refresh_notify = refresh_notify, + env_namespace = Environ.env_namespace, + setup = require("luasnip.config").setup, + extend_decorator = extend_decorator, + log = require("luasnip.util.log"), + activate_node = activate_node, +}, ls_lazy) + +return ls diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/data.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/data.lua new file mode 100644 index 00000000..6e21d6c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/data.lua @@ -0,0 +1,27 @@ +--- This module stores all files loaded by any of the loaders, ordered by their +--- filetype, and other data. +--- This is to facilitate luasnip.loaders.edit_snippets, and to handle +--- persistency of data, which is not given if it is stored in the module-file, +--- since the module-name we use (luasnip.loaders.*) is not necessarily the one +--- used by the user (luasnip/loader/*, for example), and the returned modules +--- are different tables. + +local autotable = require("luasnip.util.auto_table").autotable + +local M = { + lua_collections = {}, + lua_ft_paths = autotable(2), + + snipmate_collections = {}, + snipmate_ft_paths = autotable(2), + -- set by loader. + snipmate_cache = nil, + + vscode_package_collections = {}, + vscode_standalone_watchers = {}, + vscode_ft_paths = autotable(2), + -- set by loader. + vscode_cache = nil, +} + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_lua.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_lua.lua new file mode 100644 index 00000000..629d5c64 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_lua.lua @@ -0,0 +1,466 @@ +-- loads snippets from directory structured almost like snipmate-collection: +-- - files all named .lua +-- - each returns table containing keys (optional) "snippets" and +-- "autosnippets", value for each a list of snippets. +-- +-- cache: +-- - lazy_load_paths: { +-- { +-- add_opts = {...}, +-- ft1 = {filename1, filename2}, +-- ft2 = {filename1}, +-- ... +-- }, { +-- add_opts = {...}, +-- ft1 = {filename1}, +-- ... +-- } +-- } +-- +-- each call to load generates a new entry in that list. We cannot just merge +-- all files for some ft since add_opts might be different (they might be from +-- different lazy_load-calls). + +local loader_util = require("luasnip.loaders.util") +local log = require("luasnip.util.log").new("lua-loader") +local session = require("luasnip.session") +local util = require("luasnip.util.util") +local autotable = require("luasnip.util.auto_table").autotable +local tree_watcher = require("luasnip.loaders.fs_watchers").tree +local path_watcher = require("luasnip.loaders.fs_watchers").path +local digraph = require("luasnip.util.directed_graph") +local refresh_notify = + require("luasnip.session.enqueueable_operations").refresh_notify +local clean_invalidated = + require("luasnip.session.enqueueable_operations").clean_invalidated + +local Data = require("luasnip.loaders.data") + +local M = {} + +-- ASSUMPTION: this function will only be called inside the snippet-constructor, +-- to find the location of the lua-loaded file calling it. +-- It is not exported, because it will (in its current state) only ever be used +-- in one place, and it feels a bit wrong to expose put a function into `M`. +-- Instead, it is inserted into the global environment before a luasnippet-file +-- is loaded, and removed from it immediately when this is done +local function get_loaded_file_debuginfo() + -- we can skip looking at the first four stackframes, since + -- 1 is this function + -- 2 is the snippet-constructor + -- ... (here anything is going on, could be 0 stackframes, could be many) + -- n-2 (at least 3) is the loaded file + -- n-1 (at least 4) is pcall + -- n (at least 5) is _luasnip_load_file + local current_call_depth = 4 + local debuginfo + + repeat + current_call_depth = current_call_depth + 1 + debuginfo = debug.getinfo(current_call_depth, "n") + until debuginfo.name == "_luasnip_load_file" + + -- ret is stored into a local, and not returned immediately to prevent tail + -- call optimization, which seems to invalidate the stackframe-numbers + -- determined earlier. + -- + -- current_call_depth-0 is _luasnip_load_file, + -- current_call_depth-1 is pcall, and + -- current_call_depth-2 is the lua-loaded file. + -- "Sl": get only source-file and current line. + local ret = debug.getinfo(current_call_depth - 2, "Sl") + return ret +end + +local function search_lua_rtp(modulename) + -- essentially stolen from vim.loader. + local rtp_lua_path = package.path + for _, path in ipairs(vim.api.nvim_get_runtime_file("", true)) do + rtp_lua_path = rtp_lua_path + .. (";%s/lua/?.lua;%s/lua/?/init.lua"):format(path, path) + end + + return package.searchpath(modulename, rtp_lua_path) +end + +local function _luasnip_load_file(file) + -- vim.loader.enabled does not seem to be official api, so always reset + -- if the loader is available. + -- To be sure, even pcall it, in case there are conditions under which + -- it might error. + if vim.loader then + -- pcall, not sure if this can fail in some way.. + -- Does not seem like it though + local ok, res = pcall(vim.loader.reset, file) + if not ok then + log.warn("Could not reset cache for file %s\n: %s", file, res) + end + end + + local func, error_msg = loadfile(file) + if error_msg then + log.error("Failed to load %s\n: %s", file, error_msg) + error(string.format("Failed to load %s\n: %s", file, error_msg)) + end + + -- the loaded file may add snippets to these tables, they'll be + -- combined with the snippets returned regularly. + local file_added_snippets = {} + local file_added_autosnippets = {} + + local dependent_files = {} + + -- setup snip_env in func + local func_env + local function ls_tracked_dofile(filename) + local package_func, err_msg = loadfile(filename) + if package_func then + setfenv(package_func, func_env) + table.insert(dependent_files, filename) + else + error(("File %s could not be loaded: %s"):format(filename, err_msg)) + end + + return package_func() + end + func_env = vim.tbl_extend( + "force", + -- extend the current(expected!) globals with the snip_env, and the + -- two tables. + _G, + session.get_snip_env(), + { + ls_file_snippets = file_added_snippets, + ls_file_autosnippets = file_added_autosnippets, + ls_tracked_dofile = ls_tracked_dofile, + ls_tracked_dopackage = function(package_name) + local package_file = search_lua_rtp(package_name) + if not package_file then + error( + ("Could not find package %s in rtp and package.path"):format( + package_name + ) + ) + end + return ls_tracked_dofile(package_file) + end, + } + ) + -- defaults snip-env requires metatable for resolving + -- lazily-initialized keys. If we have to combine this with an eventual + -- metatable of _G, look into unifying ls.setup_snip_env and this. + setmetatable(func_env, getmetatable(session.get_snip_env())) + setfenv(func, func_env) + + -- Since this function has to reach the snippet-constructor, and fenvs + -- aren't inherited by called functions, we have to set it in the global + -- environment. + _G.__luasnip_get_loaded_file_frame_debuginfo = util.ternary( + session.config.loaders_store_source, + get_loaded_file_debuginfo, + nil + ) + local run_ok, file_snippets, file_autosnippets = pcall(func) + -- immediately nil it. + _G.__luasnip_get_loaded_file_frame_debuginfo = nil + + if not run_ok then + log.error("Failed to execute\n: %s", file, file_snippets) + error("Failed to execute " .. file .. "\n: " .. file_snippets) + end + + -- make sure these aren't nil. + file_snippets = file_snippets or {} + file_autosnippets = file_autosnippets or {} + + vim.list_extend(file_snippets, file_added_snippets) + vim.list_extend(file_autosnippets, file_added_autosnippets) + + return file_snippets, file_autosnippets, dependent_files +end + +local function lua_package_file_filter(fname) + return fname:match("%.lua$") +end + +--- Collection watches all files that belong to a collection of snippets below +--- some root, and registers new files. +local Collection = {} +local Collection_mt = { + __index = Collection, +} + +function Collection.new( + root, + lazy, + include_ft, + exclude_ft, + add_opts, + lazy_watcher, + fs_event_providers +) + local ft_filter = loader_util.ft_filter(include_ft, exclude_ft) + local o = setmetatable({ + root = root, + file_filter = function(path, ft) + if not path:sub(1, #root) == root then + log.warn( + "Tried to filter file `%s`, which is not inside the root `%s`.", + path, + root + ) + return false + end + return lua_package_file_filter(path) and ft_filter(ft) + end, + add_opts = add_opts, + lazy = lazy, + -- store ft -> set of files that should be lazy-loaded. + lazy_files = autotable(2, { warn = false }), + -- store, for all files in this collection, their filetype. + -- No need to always recompute it, and we can use this to store which + -- files belong to the collection. + loaded_path_ft = {}, + file_dependencies = digraph.new_labeled(), + -- store fs_watchers for files the snippets-files depend on. + dependency_watchers = {}, + fs_event_providers = fs_event_providers, + }, Collection_mt) + + -- only register files up to a depth of 2. + local ok, err_or_watcher = pcall(tree_watcher, root, 2, { + -- don't handle removals for now. + new_file = function(path) + local path_ft = loader_util.collection_file_ft(o.root, path) + -- detected new file, make sure it is allowed by our filters. + if o.file_filter(path, path_ft) then + o:add_file(path, path_ft) + end + end, + change_file = function(path) + o:reload(path) + end, + }, { lazy = lazy_watcher, fs_event_providers = fs_event_providers }) + + if not ok then + error(("Could not create watcher: %s"):format(err_or_watcher)) + end + + o.watcher = err_or_watcher + + log.info("Initialized snippet-collection at `%s`", root) + + return o +end + +-- Add file with some filetype to collection. +function Collection:add_file(path, ft) + Data.lua_ft_paths[ft][path] = true + + if self.lazy then + if not session.loaded_fts[ft] then + log.info( + "Registering lazy-load-snippets for ft `%s` from file `%s`", + ft, + path + ) + + -- only register to load later. + self.lazy_files[ft][path] = true + return + else + log.info( + "Filetype `%s` is already active, loading immediately.", + ft + ) + end + end + + self:load_file(path, ft) +end +function Collection:load_file(path, ft) + log.info("Adding snippets for filetype `%s` from file `%s`", ft, path) + self.loaded_path_ft[path] = ft + + local snippets, autosnippets, dependent_files = _luasnip_load_file(path) + + -- ignored if it already exists. + self.file_dependencies:set_vertex(path) + -- make sure we don't retain any old dependencies. + self.file_dependencies:clear_edges(path) + + for _, file_dependency in ipairs(dependent_files) do + -- ignored if it already exists. + self.file_dependencies:set_vertex(file_dependency) + -- path depends on dependent_file => if dependent_file is changed, path + -- should be updated. + self.file_dependencies:set_edge(file_dependency, path, path) + + if not self.dependency_watchers[file_dependency] then + self.dependency_watchers[file_dependency] = path_watcher( + file_dependency, + { + change = function(_) + local depending_files = + self.file_dependencies:connected_component( + file_dependency, + "Forward" + ) + for _, file in ipairs(depending_files) do + -- Prevent loading one of the utility-files as a snippet-file. + -- This will not reject any snippet-file in + -- depending_files. This is because since they are in + -- depending_files, we have their dependency-information, + -- which can only be obtained by loading them, and so there + -- can't be any unloaded files in there. + if self.loaded_path_ft[file] then + self:load_file(file, self.loaded_path_ft[file]) + end + end + end, + }, + { lazy = false, fs_event_providers = self.fs_event_providers } + ) + end + end + + loader_util.add_file_snippets( + ft, + path, + snippets, + autosnippets, + self.add_opts + ) + + refresh_notify(ft) +end +function Collection:do_lazy_load(ft) + for file, _ in pairs(self.lazy_files[ft]) do + if not self.loaded_path_ft[file] then + self:load_file(file, ft) + end + end +end + +-- will only do something, if the file at `path` was loaded previously. +function Collection:reload(path) + local path_ft = self.loaded_path_ft[path] + if not path_ft then + -- file not yet loaded. + return + end + + -- will override previously-loaded snippets from this path. + self:load_file(path, path_ft) + + -- clean snippets if enough were removed. + clean_invalidated() +end + +function Collection:stop() + self.watcher:stop() + for _, watcher in pairs(self.dependency_watchers) do + watcher:stop() + end +end + +function M._load_lazy_loaded_ft(ft) + log.info("Loading lazy-load-snippets for filetype `%s`", ft) + + for _, collection in ipairs(Data.lua_collections) do + collection:do_lazy_load(ft) + end +end + +local function _load(lazy, opts) + local o = loader_util.normalize_opts(opts) + + local collection_roots = + loader_util.resolve_root_paths(o.paths, "luasnippets") + local lazy_roots = loader_util.resolve_lazy_root_paths(o.lazy_paths) + + log.info( + "Found roots `%s` for paths `%s`.", + vim.inspect(collection_roots), + vim.inspect(o.paths) + ) + if o.paths and #o.paths ~= #collection_roots then + log.warn( + "Could not resolve all collection-roots for paths `%s`: only found `%s`", + vim.inspect(o.paths), + vim.inspect(collection_roots) + ) + end + + log.info( + "Determined roots `%s` for lazy_paths `%s`.", + vim.inspect(lazy_roots), + vim.inspect(o.lazy_paths) + ) + if o.lazy_paths and #o.lazy_paths ~= #lazy_roots then + log.warn( + "Could not resolve all collection-roots for lazy_paths `%s`: only found `%s`", + vim.inspect(o.lazy_paths), + vim.inspect(lazy_roots) + ) + end + + for paths_lazy, roots in pairs({ + [true] = lazy_roots, + [false] = collection_roots, + }) do + for _, collection_root in ipairs(roots) do + local ok, coll_or_err = pcall( + Collection.new, + collection_root, + lazy, + o.include, + o.exclude, + o.add_opts, + paths_lazy, + o.fs_event_providers + ) + + if not ok then + log.error( + "Could not create collection at %s: %s", + collection_root, + coll_or_err + ) + else + table.insert(Data.lua_collections, coll_or_err) + end + end + end +end + +--- Load lua-snippet-collections immediately. +--- @param opts LuaSnip.Loaders.LoadOpts? +function M.load(opts) + _load(false, opts) +end + +--- Load lua-snippet-collections on demand. +--- @param opts LuaSnip.Loaders.LoadOpts? +function M.lazy_load(opts) + _load(true, opts) + + -- load for current buffer on startup. + for _, ft in + ipairs(loader_util.get_load_fts(vim.api.nvim_get_current_buf())) + do + M._load_lazy_loaded_ft(ft) + end +end + +function M.clean() + for _, collection in ipairs(Data.lua_collections) do + collection:stop() + end + -- bit ugly, keep in sync with defaults in data.lua. + -- Don't anticipate those changing, so fine I guess. + Data.lua_collections = {} + Data.lua_ft_paths = autotable(2) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_snipmate.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_snipmate.lua new file mode 100644 index 00000000..f70288de --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_snipmate.lua @@ -0,0 +1,512 @@ +local loader_util = require("luasnip.loaders.util") +local util = require("luasnip.util.util") +local tbl_util = require("luasnip.util.table") +local Path = require("luasnip.util.path") +local autotable = require("luasnip.util.auto_table").autotable +local digraph = require("luasnip.util.directed_graph") +local tree_watcher = require("luasnip.loaders.fs_watchers").tree +local Data = require("luasnip.loaders.data") +local session = require("luasnip.session") +local snippetcache = require("luasnip.loaders.snippet_cache") +local refresh_notify = + require("luasnip.session.enqueueable_operations").refresh_notify +local clean_invalidated = + require("luasnip.session.enqueueable_operations").clean_invalidated + +local log = require("luasnip.util.log").new("snipmate-loader") + +--- Load data from any snippet-file. +--- @param filename string +--- @return LuaSnip.Loaders.SnippetFileData +local function load_snipmate(filename) + local buffer_ok, buffer = pcall(Path.read_file, filename) + if not buffer_ok then + log.error(("Could not read file %s: %s"):format(filename, buffer)) + -- return dummy-data. + return { + snippets = {}, + autosnippets = {}, + misc = {}, + } + end + + local sp = require("luasnip.nodes.snippetProxy") + local snipmate_parse_fn = require("luasnip.util.parser").parse_snipmate + local source = require("luasnip.session.snippet_collection.source") + + -- could also be separate variables, but easier to access this way. + local snippets = { + snippet = {}, + autosnippet = {}, + } + local extends = {} + + ---@type string[] + local lines = loader_util.split_lines(buffer) + local i = 1 + + local function _parse(snippet_type, snipmate_opts) + local line = lines[i] + -- "snippet" or "autosnippet" + local prefix, description = + line:match("^" .. snippet_type .. [[%s+(%S+)%s*(.*)]]) + local body = {} + + local snip_begin_line = i + i = i + 1 + ---@type number + local indent + + while i <= #lines do + line = lines[i] + if line:find("^%s+") then + if not indent then + indent = #line:match("^%s+") + end + line = line:sub(indent + 1) + line = line:gsub("${VISUAL}", "${TM_SELECTED_TEXT}") + elseif line ~= "" then + break + end + table.insert(body, line) + i = i + 1 + end + + body = table.concat(body, "\n") + local snip = sp( + { + trig = prefix, + desc = description, + wordTrig = true, + priority = snipmate_opts.priority, + }, + body, + { + parse_fn = snipmate_parse_fn, + } + ) + if session.config.loaders_store_source then + snip._source = source.from_location( + filename, + { line = snip_begin_line, line_end = i - 1 } + ) + end + table.insert(snippets[snippet_type], snip) + end + + -- options for some snippet can be specified in the lines before the + -- {auto}snippet-keyword ("priority 2000\nsnippet...."). + -- They are stored in snipmate_opts, which is cleaned whenever a snippet is + -- actually created. + local snipmate_opts = {} + while i <= #lines do + local line = lines[i] + if vim.startswith(line, "snippet") then + _parse("snippet", snipmate_opts) + snipmate_opts = {} + elseif vim.startswith(line, "autosnippet") then + _parse("autosnippet", snipmate_opts) + snipmate_opts = {} + elseif vim.startswith(line, "extends") then + vim.list_extend(extends, vim.split(vim.trim(line:sub(8)), "[,%s]+")) + i = i + 1 + elseif vim.startswith(line, "#") or line:find("^%s*$") then + -- comment and blank line + i = i + 1 + elseif vim.startswith(line, "priority") then + snipmate_opts.priority = tonumber(line:match("priority%s+(%d+)")) + i = i + 1 + else + log.error("Invalid line in %s: %s", filename, i) + error(("Invalid line in %s: %s"):format(filename, i)) + end + end + + return { + snippets = snippets.snippet, + autosnippets = snippets.autosnippet, + misc = extends, + } +end + +-- cache snippets without filetype-association for reuse. +Data.snipmate_cache = snippetcache.new(load_snipmate) + +--- Collection watches all files that belong to a collection of snippets below +--- some root, and registers new files. +local Collection = {} +local Collection_mt = { + __index = Collection, +} + +local function snipmate_package_file_filter(fname) + return fname:match("%.snippets$") +end + +function Collection.new( + root, + lazy, + include_ft, + exclude_ft, + add_opts, + lazy_watcher, + fs_event_providers +) + local ft_filter = loader_util.ft_filter(include_ft, exclude_ft) + local o = setmetatable({ + root = root, + + --- @alias LuaSnip.Loaders.Snipmate.FileCategory + --- | '"collection"' File only belongs to the collection + --- | '"load"' File should be loaded + + --- Determine whether a file should be loaded, belongs to the + --- collection, or doesn't. + --- This distinction is important because we need to know about all + --- files to correctly resolve `extend `, but only want to load + --- the filetypes allowed by in/exclude. + --- @param path string + ---@return LuaSnip.Loaders.Snipmate.FileCategory? + categorize_file = function(path) + if not path:sub(1, #root) == root then + log.warn( + "Tried to filter file `%s`, which is not inside the root `%s`.", + path, + root + ) + return nil + end + if snipmate_package_file_filter(path) then + local path_ft = loader_util.collection_file_ft(root, path) + if ft_filter(path_ft) then + return "load" + end + return "collection" + end + return nil + end, + -- sometimes we don't want the full categorize-file-data, only which + -- filetypes should be loaded. + ft_filter = ft_filter, + + add_opts = add_opts, + lazy = lazy, + -- store ft -> set of files that should be lazy-loaded. + lazy_files = autotable(2, { warn = false }), + -- store for each path the set of filetypes it has been loaded with. + loaded_path_fts = autotable(2, { warn = false }), + -- model filetype-extensions (`extends ` in `ft.snippets`). + -- Better than a flat table with t[ft] = {someft=true, somotherft=true} + -- since transitive dependencies are easier to understand/query. + -- There is an edge with source src to destination dst, if snippets for + -- filetype src also contribute to filetype dst. + -- Since we respect transitive `extends`, we can get all filetypes a + -- snippet-file for some filetype A contributes to by querying the + -- connected component of A (all filetype-vertices reachable from A). + ft_extensions = digraph.new_labeled(), + -- store all files in the collection, by their filetype. + -- This information is necessary to handle `extends` even for files + -- that are not actually loaded (due to in/exclude). + collection_files_by_ft = autotable(2, { warn = false }), + -- set if creation successful. + watcher = nil, + }, Collection_mt) + + -- only register files up to a depth of 2. + local ok, err_or_watcher = pcall(tree_watcher, root, 2, { + -- don't handle removals for now. + new_file = function(path) + ---@as LuaSnip.Loaders.Snipmate.FileCategory + local file_category = o.categorize_file(path) + + if file_category then + -- know it's at least in the collection -> can register it. + local file_ft = loader_util.collection_file_ft(o.root, path) + o:register_file(path, file_ft) + if file_category == "load" then + -- actually load if allowed by in/exclude. + o:add_file(path, file_ft) + end + end + end, + change_file = function(path) + vim.schedule_wrap(function() + o:reload(path) + end)() + end, + }, { lazy = lazy_watcher, fs_event_providers = fs_event_providers }) + + if not ok then + error(("Could not create watcher: %s"):format(err_or_watcher)) + end + + o.watcher = err_or_watcher + + log.info("Initialized snippet-collection at `%s`", root) + + return o +end + +--- Makes the file known to the collection, but does not load its snippets. +--- This is important because `extends` may require loading a file excluded by +--- `file_filter`, ie `include` and `exclude`. +--- @param path string +--- @param ft string +function Collection:register_file(path, ft) + self.collection_files_by_ft[ft][path] = true +end + +--- Register a file-filetype-association with the collection. +--- @param path string Path to a file that belongs to this collection. +--- @param add_ft string The original filetype this file should be added as. +--- Since we have to support filetype-extensions, this may +--- add the snippets of the file to several other +--- filetypes. +function Collection:add_file(path, add_ft) + -- register known file. + Data.snipmate_ft_paths[add_ft][path] = true + + if self.lazy then + if not session.loaded_fts[add_ft] then + log.info( + "Registering lazy-load-snippets for ft `%s` from file `%s`", + add_ft, + path + ) + + -- only register to load later. + self.lazy_files[add_ft][path] = true + return + else + log.info( + "Filetype `%s` is already active, loading immediately.", + add_ft + ) + end + end + + -- extended filetypes will be loaded in load_file. + self:load_file(path, add_ft, "SkipIfLoaded") +end + +--- @alias LuaSnip.Loaders.Snipmate.SkipLoad +--- | '"ForceLoad"' Always load, even if it was already loaded. +--- | '"SkipIfLoaded"' Skip the load if the file has been loaded already. + +-- loads the fts that extend load_ft as well. +-- skip_load_mode allows this code to both prevent unnecessary loads (which +-- could be caused if some file is added to the same filetype more than once), +-- while still handling reload (where the files has to be loaded again for +-- every filetype, even if it already is loaded (since it may have different +-- snippets)) +function Collection:load_file(path, ft, skip_load_mode) + if skip_load_mode == "SkipIfLoaded" and self.loaded_path_fts[path][ft] then + return + end + -- ignore load_file if ft is excluded/not included. + if not self.ft_filter(ft) then + return + end + + log.info("Adding snippets for filetype `%s` from file `%s`", ft, path) + + -- Set here to skip loads triggered for the same path-file-combination in + -- subsequent code, which would trigger and endless loop. + self.loaded_path_fts[path][ft] = true + + -- this may already be set, but setting again here ensures that a file is + -- certainly associated with each filetype it's loaded for. (for example, + -- file-ft-combinations loaded as a dependency from another file may not be + -- set already). + Data.snipmate_ft_paths[ft][path] = true + + -- snippets may already be loaded -> get them from cache. + local data = Data.snipmate_cache:fetch(path) + local snippets = data.snippets + local autosnippets = data.autosnippets + -- data.misc is user-input, clean it here. + local extended_fts = util.deduplicate(data.misc) + + -- ignored if it already exists. + self.ft_extensions:set_vertex(ft) + -- make sure we don't retain any old dependencies. + self.ft_extensions:clear_edges(path) + + for _, extended_ft in pairs(extended_fts) do + -- ignored if it already exists. + self.ft_extensions:set_vertex(extended_ft) + -- snippets for extended_ft should also be loaded if ft is loaded + -- label edge with path, so all edges from this file can be updated on + -- reload. + self.ft_extensions:set_edge(extended_ft, ft, path) + end + + loader_util.add_file_snippets( + ft, + path, + snippets, + autosnippets, + self.add_opts + ) + + -- get all filetypes this one extends (directly or transitively), and load + -- their files for ft. + local load_fts = self.ft_extensions:connected_component(ft, "Backward") + for _, extended_ft in ipairs(load_fts) do + for file, _ in pairs(self.collection_files_by_ft[extended_ft]) do + -- check which filetypes a file with filetype extended_ft has to be + -- loaded for currently!! (ie. which filetypes directly or + -- transitively extend extended_ft. + -- The reason we can't just check that statically once, or just load + -- a file for the filetypes its filetype is initially extended by is + -- that the extends-graph may change on each `load_file`, and we + -- want to respect these changes. + for _, file_ft in + ipairs( + self.ft_extensions:connected_component( + extended_ft, + "Forward" + ) + ) + do + -- skips load if the file is already loaded for the given filetype. + -- One bad side-effect of this current implementation is that + -- the edges in the graph will be reset/set multiple times, + -- until they are retained in the last load_file-call to the + -- last filetype. + self:load_file(file, file_ft, "SkipIfLoaded") + end + end + end + + refresh_notify(ft) +end + +function Collection:do_lazy_load(lazy_ft) + for file, _ in pairs(self.lazy_files[lazy_ft]) do + for _, ft in + ipairs(self.ft_extensions:connected_component(lazy_ft, "Forward")) + do + -- skips load if the file is already loaded for the given filetype. + self:load_file(file, ft, "SkipIfLoaded") + end + end +end +-- will only do something, if the file at `path` is actually in the collection. +function Collection:reload(path) + local loaded_fts = tbl_util.set_to_list(self.loaded_path_fts[path]) + for _, loaded_ft in ipairs(loaded_fts) do + -- will override previously-loaded snippets from this path. + self:load_file(path, loaded_ft, "ForceLoad") + end + + -- clean snippets if enough were removed. + clean_invalidated() +end + +function Collection:stop() + self.watcher:stop() +end + +local M = {} + +function M._load_lazy_loaded_ft(ft) + log.info("Loading lazy-load-snippets for filetype `%s`", ft) + + for _, collection in ipairs(Data.snipmate_collections) do + collection:do_lazy_load(ft) + end +end + +--- Generalized loading of collections. +--- @param lazy boolean Whether the collection should be loaded lazily. +--- @param opts LuaSnip.Loaders.LoadOpts? +local function _load(lazy, opts) + local o = loader_util.normalize_opts(opts) + + local collection_roots = loader_util.resolve_root_paths(o.paths, "snippets") + local lazy_roots = loader_util.resolve_lazy_root_paths(o.lazy_paths) + + log.info( + "Found roots `%s` for paths `%s`.", + vim.inspect(collection_roots), + vim.inspect(o.paths) + ) + if o.paths and #o.paths ~= #collection_roots then + log.warn( + "Could not resolve all collection-roots for paths `%s`: only found `%s`", + vim.inspect(o.paths), + vim.inspect(collection_roots) + ) + end + + log.info( + "Determined roots `%s` for lazy_paths `%s`.", + vim.inspect(lazy_roots), + vim.inspect(o.lazy_paths) + ) + if o.lazy_paths and #o.lazy_paths ~= #lazy_roots then + log.warn( + "Could not resolve all collection-roots for lazy_paths `%s`: only found `%s`", + vim.inspect(o.lazy_paths), + vim.inspect(lazy_roots) + ) + end + + for paths_lazy, roots in pairs({ + [true] = lazy_roots, + [false] = collection_roots, + }) do + for _, collection_root in ipairs(roots) do + local ok, coll_or_err = pcall( + Collection.new, + collection_root, + lazy, + o.include, + o.exclude, + o.add_opts, + paths_lazy, + o.fs_event_providers + ) + + if not ok then + log.error( + "Could not create collection at %s: %s", + collection_root, + coll_or_err + ) + else + table.insert(Data.snipmate_collections, coll_or_err) + end + end + end +end + +--- Load snipmate-snippet-collections immediately. +--- @param opts LuaSnip.Loaders.LoadOpts? +function M.load(opts) + _load(false, opts) +end + +--- Load snipmate-snippet-collections on demand. +--- @param opts LuaSnip.Loaders.LoadOpts? +function M.lazy_load(opts) + _load(true, opts) + -- load for current buffer on startup. + for _, ft in + ipairs(loader_util.get_load_fts(vim.api.nvim_get_current_buf())) + do + M._load_lazy_loaded_ft(ft) + end +end + +function M.clean() + for _, collection in ipairs(Data.snipmate_collections) do + collection:stop() + end + Data.snipmate_ft_paths = autotable(2) + -- don't reset cache, snippets are correctly updated on file-change anyway, + -- and there is no persistent data passed on. +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_vscode.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_vscode.lua new file mode 100644 index 00000000..f8c3f7b6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/from_vscode.lua @@ -0,0 +1,645 @@ +local util = require("luasnip.util.util") +local loader_util = require("luasnip.loaders.util") +local Path = require("luasnip.util.path") +local log = require("luasnip.util.log").new("vscode-loader") +local autotable = require("luasnip.util.auto_table").autotable +local path_watcher = require("luasnip.loaders.fs_watchers").path +local Data = require("luasnip.loaders.data") +local session = require("luasnip.session") +local refresh_notify = + require("luasnip.session.enqueueable_operations").refresh_notify +local clean_invalidated = + require("luasnip.session.enqueueable_operations").clean_invalidated + +-- create snippetProxy which does not trim lines and dedent text. +-- It's fair to use passed test as-is, if it's from json. +local parse = require("luasnip.util.parser").parse_snippet +local sp = require("luasnip.util.extend_decorator").apply( + require("luasnip.nodes.snippetProxy"), + {}, + { + parse_fn = function(ctx, body) + return parse(ctx, body, { trim_empty = false, dedent = false }) + end, + } +) + +local json_decoders = { + json = util.json_decode, + jsonc = require("luasnip.util.jsonc").decode, + ["code-snippets"] = require("luasnip.util.jsonc").decode, +} + +local function read_json(fname) + local data_ok, data = pcall(Path.read_file, fname) + if not data_ok then + log.error("Could not read file %s", fname) + return nil + end + + local fname_extension = Path.extension(fname) + if json_decoders[fname_extension] == nil then + log.error( + "`%s` was expected to have file-extension either `json`, `jsonc` or `code-snippets`, but doesn't.", + fname + ) + return nil + end + local fname_decoder = json_decoders[fname_extension] + + local status, result = pcall(fname_decoder, data) + if status then + return result + else + log.error("Could not parse file %s: %s", fname, result) + return nil + end +end + +--- Load snippets from vscode-snippet-file. +--- @param file string Path to file +---@return LuaSnip.Loaders.SnippetFileData +local function get_file_snippets(file) + local source = require("luasnip.session.snippet_collection.source") + local multisnippet = require("luasnip.nodes.multiSnippet") + + -- since most snippets we load don't have a scope-field, we just insert + -- them here by default. + local snippets = {} + + local snippet_set_data = read_json(file) + if snippet_set_data == nil then + log.error("Reading json from file `%s` failed, skipping it.", file) + + return { + snippets = {}, + autosnippets = {}, + misc = {}, + } + end + + for name, parts in pairs(snippet_set_data) do + local body = type(parts.body) == "string" and parts.body + or table.concat(parts.body, "\n") + + local ls_conf = parts.luasnip or {} + + -- we may generate multiple interfaces to the same snippet + -- (different filetype, different triggers) + + -- context common to all snippets generated here. + local common_context = { + name = name, + desc = parts.description or name, + wordTrig = ls_conf.wordTrig, + priority = ls_conf.priority, + snippetType = ls_conf.autotrigger and "autosnippet" or "snippet", + } + + -- Sometimes it's a list of prefixes instead of a single one + local prefixes = type(parts.prefix) == "table" and parts.prefix + or { parts.prefix } + + -- vscode documents `,`, but `.` also works. + -- an entry `false` in this list will cause a `ft=nil` for the snippet. + local filetypes = parts.scope and vim.split(parts.scope, "[.,]") + or { false } + + local contexts = {} + for _, prefix in ipairs(prefixes) do + for _, filetype in ipairs(filetypes) do + table.insert( + contexts, + { filetype = filetype or nil, trig = prefix } + ) + end + end + + local snip + if #contexts > 1 then + -- only construct multisnippet if it is actually necessary. + contexts.common = common_context + snip = multisnippet._raw_ms(contexts, sp(nil, body), {}) + elseif #contexts == 1 then + -- have to add options from common context to the trig/filetype-context. + snip = sp(vim.tbl_extend("keep", contexts[1], common_context), body) + end + + if snip then + if session.config.loaders_store_source then + -- only know file, not line or line_end. + snip._source = source.from_location(file) + end + + table.insert(snippets, snip) + end + end + + return { + snippets = snippets, + autosnippets = {}, + misc = {}, + } +end + +-- has to be set in separate module to allow different module-path-separators +-- in `require`. +Data.vscode_cache = + require("luasnip.loaders.snippet_cache").new(get_file_snippets) + +--- Parse package.json(c), determine all files that contribute snippets, and +--- which filetype is associated with them. +--- @param manifest string +--- @return table> +local function get_snippet_files(manifest) + -- if root doesn't contain a package.json, or it contributes no snippets, + -- return no snippets. + if not Path.exists(manifest) then + log.warn("Manifest %s does not exist", manifest) + return {} + end + + local package_data = read_json(manifest) + if not package_data then + -- since it is a `.json/jsonc`, the json not being correct should be an error. + log.error("Could not read json from `%s`", manifest) + return {} + end + + if + not package_data.contributes or not package_data.contributes.snippets + then + log.warn("Manifest %s does not contribute any snippets.", manifest) + return {} + end + + -- stores ft -> files -> true|nil, allow iterating files and their + -- filetypes while preventing duplicates. + local ft_file_set = autotable(2, { warn = false }) + + -- parent-directory of package.json(c), all files in the package.json(c) + -- are relative to it. + local package_parent = Path.parent(manifest) + + for _, snippet_entry in pairs(package_data.contributes.snippets) do + local absolute_path = Path.join(package_parent, snippet_entry.path) + + local normalized_snippet_file = Path.normalize(absolute_path) + + if not normalized_snippet_file then + -- path does not exist (yet), try and guess the correct path anyway. + normalized_snippet_file = Path.normalize_nonexisting(absolute_path) + log.warn( + "Could not find file %s advertised in %s, guessing %s as the absolute and normalized path.", + absolute_path, + manifest, + normalized_snippet_file + ) + end + + local langs = snippet_entry.language + if type(langs) ~= "table" then + langs = { langs } + end + for _, ft in ipairs(langs) do + ft_file_set[ft][normalized_snippet_file] = true + end + end + + return ft_file_set +end + +-- Responsible for watching a single json-snippet-file. +local SnippetfileWatcher = {} +local SnippetfileWatcher_mt = { __index = SnippetfileWatcher } + +function SnippetfileWatcher.new( + path, + initial_ft, + fs_event_providers, + lazy, + load_cb +) + local o = setmetatable({ + path = path, + load_cb = load_cb, + -- track which filetypes this file has been loaded for, so we can + -- reload for all of them. + loaded_fts = { [initial_ft] = true }, + }, SnippetfileWatcher_mt) + + local load_all_fts = function() + for ft, _ in pairs(o.loaded_fts) do + load_cb(path, ft) + refresh_notify(ft) + end + end + local ok, err_or_watcher = pcall(path_watcher, path, { + add = load_all_fts, + change = function() + load_all_fts() + + -- clean snippets if enough were removed. + clean_invalidated() + end, + }, { lazy = lazy, fs_event_providers = fs_event_providers }) + + if not ok then + -- has to be handled by caller, we can't really proceed if the creation + -- failed. + error( + ("Could not create path_watcher for path %s: %s"):format( + path, + err_or_watcher + ) + ) + end + + o.watcher = err_or_watcher + + return o +end + +-- called by collection. +function SnippetfileWatcher:add_ft(ft) + if self.loaded_fts[ft] then + -- already loaded. + return + end + self.loaded_fts[ft] = true + self.load_cb(self.path, ft) +end + +function SnippetfileWatcher:stop() + self.watcher:stop() +end + +--- Collection watches all files that belong to a collection of snippets below +--- some root, and registers new files. +local Collection = {} +local Collection_mt = { + __index = Collection, +} + +function Collection.new( + manifest_path, + lazy, + include_ft, + exclude_ft, + add_opts, + lazy_watcher, + fs_event_providers +) + local ft_filter = loader_util.ft_filter(include_ft, exclude_ft) + local o = setmetatable({ + lazy = lazy, + -- store ft -> set of files that should be lazy-loaded. + lazy_files = autotable(2, { warn = false }), + fs_event_providers = fs_event_providers, + + -- store path-watchers (so we don't register more than one for one + -- path), and so we can disable them. + path_watchers = {}, + -- for really loading a file. + -- this is not done in Collection:load itself, since it may have to be + -- performed as a callback on file-creation. + load_callback = function(path, ft) + local data = Data.vscode_cache:fetch(path) + -- autosnippets are included in snippets for this loader. + local snippets = data.snippets + loader_util.add_file_snippets(ft, path, snippets, {}, add_opts) + end, + -- initialized in a bit, we have to store+reset a watcher for the manifest-file. + manifest_watcher = nil, + }, Collection_mt) + + -- callback for updating the file-filetype-associations from the manifest. + local update_manifest = function() + local manifest_ft_paths = get_snippet_files(manifest_path) + for ft, path_set in pairs(manifest_ft_paths) do + if ft_filter(ft) then + for path, _ in pairs(path_set) do + o:add_file(path, ft) + end + end + end + end + + local ok, watcher_or_err = pcall(path_watcher, manifest_path, { + -- don't handle removals for now. + add = update_manifest, + change = update_manifest, + }, { lazy = lazy_watcher, fs_event_providers = fs_event_providers }) + + if not ok then + error(("Could not create watcher: %s"):format(watcher_or_err)) + end + o.manifest_watcher = watcher_or_err + + log.info("Initialized snippet-collection with manifest %s", manifest_path) + + return o +end + +-- Add file with some filetype to collection, load according to lazy_load. +function Collection:add_file(path, ft) + Data.vscode_ft_paths[ft][path] = true + + if self.lazy then + if not session.loaded_fts[ft] then + log.info( + "Registering lazy-load-snippets for ft `%s` from file `%s`", + ft, + path + ) + + -- only register to load later. + self.lazy_files[ft][path] = true + return + else + log.info( + "Filetype `%s` is already active, loading immediately.", + ft + ) + end + end + + self:load_file(path, ft) +end + +function Collection:load_file(path, ft) + log.info("Registering file %s with filetype %s for loading.", path, ft) + if not self.path_watchers[path] then + -- always register these lazily, that way an upate to the package.json + -- without the snippet-file existing will work! + -- Also make sure we use the same fs_event_providers. + local ok, watcher_or_err = pcall( + SnippetfileWatcher.new, + path, + ft, + self.fs_event_providers, + true, + self.load_callback + ) + if not ok then + log.error( + "Could not create SnippetFileWatcher for path %s: %s", + path, + watcher_or_err + ) + return + end + self.path_watchers[path] = watcher_or_err + else + -- make new filetype known to existing watcher. + self.path_watchers[path]:add_ft(ft) + end +end + +-- stop all watchers associated with this collection, to make sure no snippets +-- are added from this collection again. +function Collection:stop() + self.manifest_watcher:stop() + for _, watcher in pairs(self.path_watchers) do + watcher:stop() + end +end + +function Collection:do_lazy_load(ft) + for file, _ in pairs(self.lazy_files[ft]) do + self:load_file(file, ft) + end +end + +local M = {} + +local function get_rtp_paths() + return vim.list_extend( + -- would be very surprised if this yields duplicates :D + vim.api.nvim_get_runtime_file("package.json", true), + vim.api.nvim_get_runtime_file("package.jsonc", true) + ) +end + +--- Generate list of manifest-paths from list of directory-paths. +--- If nil, search rtp. +--- If a given directory, or the mani +--- +--- @param paths string|table? List of existing directories. If nil, search runtimepath. +---@return string[] manifest_paths +local function get_manifests(paths) + local manifest_paths = {} + -- list of paths to crawl for loading (could be a table or a comma-separated-list) + if paths then + -- Get path to package.json/package.jsonc, or continue if it does not exist. + for _, dir in ipairs(paths) do + local tentative_manifest_path = + Path.expand_keep_symlink(Path.join(dir, "package.json")) + if Path.exists(tentative_manifest_path) then + table.insert(manifest_paths, tentative_manifest_path) + else + tentative_manifest_path = + Path.expand_keep_symlink(Path.join(dir, "package.jsonc")) + if Path.exists(tentative_manifest_path) then + table.insert(manifest_paths, tentative_manifest_path) + else + log.warn( + "Could not find package.json(c) in path %s (expanded to %s).", + dir, + Path.expand(dir) + ) + end + end + end + else + manifest_paths = get_rtp_paths() + end + + return manifest_paths +end + +--- Generate list of paths to manifests that may not yet exist, from list of +--- directories (which also may not yet exist). +--- One peculiarity: This will generate two paths for each directory, since we +--- don't know if the package.json or the package.jsonc will be created. +--- This may cause a bit of overhead (not much due to snippet-cache) if both +--- are created and contribute the same snippets, but that's unlikely and/or +--- user error :P +--- @param paths string[] +---@return string[] +local function get_lazy_manifests(paths) + local lazy_manifest_paths = {} + if paths then + -- list of directories, convert to list of existing manifest-files. + if type(paths) == "string" then + paths = vim.split(paths, ",") + end + for _, dir in ipairs(paths) do + local absolute_dir = Path.expand_maybe_nonexisting(dir) + + table.insert( + lazy_manifest_paths, + Path.join(absolute_dir, "package.json") + ) + table.insert( + lazy_manifest_paths, + Path.join(absolute_dir, "package.jsonc") + ) + end + end + + return lazy_manifest_paths +end + +local function _load(lazy, opts) + local o = loader_util.normalize_opts(opts) + + local manifests = get_manifests(o.paths) + local lazy_manifests = get_lazy_manifests(o.lazy_paths) + + log.info( + "Found manifests `%s` for paths `%s`.", + vim.inspect(manifests), + vim.inspect(o.paths) + ) + if o.paths and #o.paths ~= #manifests then + log.warn( + "Could not resolve all manifests for paths `%s`: only found `%s`", + vim.inspect(o.paths), + vim.inspect(manifests) + ) + end + + log.info( + "Determined roots `%s` for lazy_paths `%s`.", + vim.inspect(lazy_manifests), + vim.inspect(o.lazy_paths) + ) + -- two lazy manifests from each lazy directory. + if o.lazy_paths and #o.lazy_paths ~= 2 * #lazy_manifests then + log.warn( + "Could not resolve all manifests for lazy_paths `%s`: only found `%s`", + vim.inspect(o.lazy_paths), + vim.inspect(lazy_manifests) + ) + end + + for is_lazy, manifest_paths in pairs({ + [true] = lazy_manifests, + [false] = manifests, + }) do + for _, manifest_path in ipairs(manifest_paths) do + local ok, coll_or_err = pcall( + Collection.new, + manifest_path, + lazy, + o.include, + o.exclude, + o.add_opts, + is_lazy, + o.fs_event_providers + ) + + if not ok then + log.error( + "Could not create collection for manifest %s: %s", + manifest_path, + coll_or_err + ) + else + table.insert(Data.vscode_package_collections, coll_or_err) + end + end + end +end + +function M._load_lazy_loaded_ft(ft) + log.info("Loading lazy-load-snippets for filetype `%s`", ft) + + for _, collection in ipairs(Data.vscode_package_collections) do + collection:do_lazy_load(ft) + end + -- no need to lazy_load standalone-snippets. +end + +function M.load(opts) + _load(false, opts) +end + +function M.lazy_load(opts) + _load(true, opts) + + -- load for current buffer on startup. + for _, ft in + ipairs(loader_util.get_load_fts(vim.api.nvim_get_current_buf())) + do + M._load_lazy_loaded_ft(ft) + end +end + +function M.load_standalone(opts) + opts = opts or {} + + local lazy = vim.F.if_nil(opts.lazy, false) + local add_opts = loader_util.make_add_opts(opts) + local fs_event_providers = + vim.F.if_nil(opts.fs_event_providers, { autocmd = true, libuv = false }) + + local path + if not lazy then + path = Path.expand(opts.path) + if not path then + log.error( + "Expanding path %s does not produce an existing path.", + opts.path + ) + return + end + else + path = Path.expand_maybe_nonexisting(opts.path) + end + + Data.vscode_ft_paths["all"][path] = true + + local ok, watcher_or_err = pcall( + SnippetfileWatcher.new, + path, + "all", + fs_event_providers, + lazy, + function() + local data = Data.vscode_cache:fetch(path) + -- autosnippets are included in snippets for this loader. + local snippets = data.snippets + loader_util.add_file_snippets("all", path, snippets, {}, add_opts) + end + ) + + if not ok then + log.error( + "Could not create SnippetFileWatcher for path %s: %s", + path, + watcher_or_err + ) + return + end + + table.insert(Data.vscode_standalone_watchers, watcher_or_err) +end + +function M.clean() + for _, collection in ipairs(Data.vscode_package_collections) do + collection:stop() + end + Data.vscode_package_collections = {} + for _, standalone_watcher in ipairs(Data.vscode_standalone_watchers) do + standalone_watcher:stop() + end + Data.vscode_standalone_watchers = {} + + Data.vscode_ft_paths = autotable(2) + -- don't reset cache, there's no reason to discard the already-loaded + -- snippets as long as they're unchanged. +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/fs_watchers.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/fs_watchers.lua new file mode 100644 index 00000000..11f90539 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/fs_watchers.lua @@ -0,0 +1,723 @@ +local Path = require("luasnip.util.path") +local uv = vim.uv or vim.loop +local util = require("luasnip.util.util") +local log_tree = require("luasnip.util.log").new("tree-watcher") +local log_path = require("luasnip.util.log").new("path-watcher") +local log = require("luasnip.util.log").new("fs-watchers") + +local M = {} + +-- used by both watchers. +local callback_mt = { + __index = function() + return util.nop + end, +} + +--- @alias LuaSnip.FSWatcher.FSEventProviders +--- | '"autocmd"' Hook into BufWritePost to receive notifications on file-changes. +--- | '"libuv"' Register uv.fs_event to receive notifications on file-changes. + +--- @alias LuaSnip.FSWatcher.Callback fun(full_path: string) + +--- @class LuaSnip.FSWatcher.TreeCallbacks +--- @field new_file LuaSnip.FSWatcher.Callback? +--- @field new_dir LuaSnip.FSWatcher.Callback? +--- @field remove_file LuaSnip.FSWatcher.Callback? +--- @field remove_dir LuaSnip.FSWatcher.Callback? +--- @field remove_root LuaSnip.FSWatcher.Callback? +--- @field change_file LuaSnip.FSWatcher.Callback? +--- @field change_dir LuaSnip.FSWatcher.Callback? +--- The callbacks are called with the full path to the file/directory that is +--- affected. +--- Callbacks that are not set will be replaced by a nop. + +--- @class LuaSnip.FSWatcher.PathCallbacks +--- @field add LuaSnip.FSWatcher.Callback? +--- @field remove LuaSnip.FSWatcher.Callback? +--- @field change LuaSnip.FSWatcher.Callback? +--- The callbacks are called with the full path to the file that path-watcher +--- is registered on. +--- Callbacks that are not set will be replaced by a nop. + +--- @class LuaSnip.FSWatcher.Options +--- @field lazy boolean? +--- If set, the watcher will be initialized even if the root/watched path does +--- not yet exist, and start notifications once it is created. +--- @field fs_event_providers table? +--- Which providers to use for receiving file-changes. + +local function get_opts(opts) + opts = opts or {} + local lazy = vim.F.if_nil(opts.lazy, false) + local fs_event_providers = + vim.F.if_nil(opts.fs_event_providers, { autocmd = true, libuv = false }) + + return lazy, fs_event_providers +end + +-- plain list, don't use map-style table since we'll only need direct access to +-- a watcher when it is stopped, which seldomly happens (at least, compared to +-- how often it is iterated in the autocmd-callback). +M.autocmd_watchers = {} + +vim.api.nvim_create_augroup("_luasnip_fs_watcher", {}) +vim.api.nvim_create_autocmd({ "BufWritePost" }, { + callback = function(args) + log.debug("Received BufWritePost for file %s.", args.file) + local realpath = Path.normalize(args.file) + if not realpath then + -- if nil, the path does not exist for some reason. + log.info( + "Registered BufWritePost with %s, but realpath does not exist. Aborting fs-watcher-notification.", + args.file + ) + return + end + log.debug( + "Received update for file %s, using realpath %s.", + args.file, + realpath + ) + + -- remove stopped watchers. + -- Does not really matter whether we do this before or after the + -- callbacks, since stopped watchers already take care to not do + -- callbacks. + -- Doing this during the callback-invocations, however, would incur + -- some more complexity since ipairs does not support removal of + -- elements during the iteration. + M.autocmd_watchers = vim.tbl_filter(function(watcher) + -- this won't catch unstarted watchers, since they can't be in this + -- list in the first place. + return not watcher.stopped + end, M.autocmd_watchers) + + for _, watcher in ipairs(M.autocmd_watchers) do + watcher:BufWritePost_callback(realpath) + end + end, + group = "_luasnip_fs_watcher", +}) + +-- similar autocmd_watchers, only this list contains watchers that should be +-- notified on a manual update (which right now is every watcher). +M.active_watchers = {} +function M.write_notify(realpath) + M.active_watchers = vim.tbl_filter(function(watcher) + -- this won't catch unstarted watchers, since they can't be in this + -- list in the first place. + return not watcher.stopped + end, M.active_watchers) + + for _, watcher in ipairs(M.active_watchers) do + watcher:BufWritePost_callback(realpath) + end +end + +--- @class LuaSnip.FSWatcher.Tree +--- @field root string +--- @field fs_event userdata +--- @field files table +--- @field dir_watchers table +--- @field removed boolean +--- @field stopped boolean +--- @field callbacks LuaSnip.FSWatcher.TreeCallbacks +--- @field depth number How deep the root should be monitored. +--- @field fs_event_providers table +--- @field root_realpath string? Set as soon as the watcher is started. +local TreeWatcher = {} +local TreeWatcher_mt = { + __index = TreeWatcher, +} + +function TreeWatcher:stop() + for _, child_watcher in ipairs(self.dir_watchers) do + child_watcher:stop() + end + self:stop_self() +end + +function TreeWatcher:stop_self() + -- don't check which fs_event_providers were actually started, for both of + -- these it should not matter if they weren't. + self.stopped = true + self.send_notifications = false + + self.fs_event:stop() + -- will be removed from active_watchers/autocmd_watchers after the next event, but already won't receive it. +end + +function TreeWatcher:fs_event_callback(err, relpath, events) + if not self.send_notifications then + -- abort if we should not send notifications anymore. + return + end + vim.schedule_wrap(function() + log_tree.debug( + "raw: self.root: %s; err: %s; relpath: %s; change: %s; rename: %s", + self.root, + err, + relpath, + events.change, + events.rename + ) + local full_path = Path.join(self.root, relpath) + local path_stat = uv.fs_stat(full_path) + + -- try to figure out what happened in the directory. + if events.rename then + if not uv.fs_stat(self.root) then + self:remove_root() + return + end + if not path_stat then + self:remove_child(relpath, full_path) + return + end + + local f_type + -- if there is a link to a directory, we are notified on changes!! + if path_stat.type == "link" then + f_type = uv.fs_stat(uv.fs_realpath(full_path)) + else + f_type = path_stat.type + end + + if f_type == "file" then + if self.files[relpath] then + -- rename and file exists => a new file was moved into its + -- place => handle as changed file. + self:change_file(relpath, full_path) + else + self:new_file(relpath, full_path) + end + return + elseif f_type == "directory" then + if self.dir_watchers[relpath] then + -- rename and directory exists => directory is overwritten + -- => stop recursively, clear, and start a new watcher. + self.dir_watchers[relpath]:stop() + self.dir_watchers[relpath] = nil + end + self:new_dir(relpath, full_path) + return + end + elseif events.change then + self:change_child(relpath, full_path) + end + end)() +end + +-- May not recognize child correctly if there are symlinks on the path from the +-- child to the directory-root. +-- Should be fine, especially since, I think, fs_event can recognize those +-- correctly, which means that this is an issue only very seldomly. +function TreeWatcher:BufWritePost_callback(realpath) + if not self.send_notifications then + return + end + + if realpath:sub(1, #self.realpath_root) ~= self.realpath_root then + -- not inside this root. + return + end + + -- `#self.realpath_root+2`: remove root and path-separator. + local root_relative_components = + Path.components(realpath:sub(#self.realpath_root + 2)) + local rel = root_relative_components[1] + if #root_relative_components == 1 then + -- wrote file. + -- either new, or changed. + if self.files[rel] then + -- use regular root for notifications! + self:change_file(rel, Path.join(self.root, rel)) + else + self:new_file(rel, Path.join(self.root, rel)) + end + else + if self.dir_watchers[rel] then + if #root_relative_components == 2 then + -- only notify if the changed file is immediately in the + -- directory we're watching! + -- I think this is the behaviour of fs_event, and logically + -- makes sense. + self:change_dir(rel, Path.join(self.root, rel)) + end + else + -- does nothing if the directory already exists. + self:new_dir(rel, Path.join(self.root, rel)) + end + end +end + +function TreeWatcher:start() + if self.depth == 0 then + -- don't watch children for 0-depth. + return + end + + if self.stopped then + -- stopping overrides and prevents starting. + return + end + + self.send_notifications = true + + if self.fs_event_providers.libuv then + -- does not work on nfs-drive, at least if it's edited from another + -- machine. + local success, err = self.fs_event:start( + self.root, + {}, + function(err, relpath, events) + self:fs_event_callback(err, relpath, events) + end + ) + + if not success then + log_tree.error( + "Could not start libuv-monitor for path %s due to error %s", + self.path, + err + ) + else + log_tree.info( + "Monitoring root-directory %s with libuv-monitor.", + self.root + ) + end + end + + -- needed by BufWritePost-callback. + self.realpath_root = Path.normalize(self.root) + + if self.fs_event_providers.autocmd then + if self.realpath_root then + -- receive notifications on BufWritePost. + table.insert(M.autocmd_watchers, self) + log_tree.info( + "Monitoring root-directory %s with autocmd-monitor.", + self.root + ) + else + log_tree.error( + "Could not resolve realpath for root %s, not enabling autocmd-monitor", + self.root + ) + end + end + + if self.realpath_root then + table.insert(M.active_watchers, self) + end + + -- do initial scan after starting the watcher. + -- Scanning first, and then starting the watcher leaves a period of time + -- where a new file may be created (after scanning, before watching), where + -- we wont know about it. + -- If I understand the uv-eventloop correctly, this function, `new`, will + -- be executed completely before a callback is called, so self.files and + -- self.dir_watchers should be populated correctly when a callback is + -- received, even if it was received before all directories/files were + -- added. + -- This difference can be observed, at least on my machine, by watching a + -- directory A, and then creating a nested directory B, and children for it + -- in one command, ie. `mkdir -p A/B/{1,2,3,4,5,6,7,8,9}`. + -- If the callback is registered after the scan, the latter directories + -- (ie. 4-9) did not show up, whereas everything did work correctly if the + -- watcher was activated before the scan. + -- (almost everything, one directory was included in the initial scan and + -- the watch-event, but that seems okay for our purposes) + local files, dirs = Path.scandir(self.root) + for _, file in ipairs(files) do + local relpath = file:sub(#self.root + 2) + self:new_file(relpath, file) + end + for _, dir in ipairs(dirs) do + local relpath = dir:sub(#self.root + 2) + self:new_dir(relpath, dir) + end +end + +-- these functions maintain our logical view of the directory, and call +-- callbacks when we detect a change. +function TreeWatcher:new_file(rel, full) + if self.files[rel] then + -- already added + return + end + + log_tree.debug("new file %s %s", rel, full) + self.files[rel] = true + self.callbacks.new_file(full) +end +function TreeWatcher:new_dir(rel, full) + if self.dir_watchers[rel] then + -- already added + return + end + + log_tree.debug("new dir %s %s", rel, full) + -- first do callback for this directory, then look into (and potentially do + -- callbacks for) children. + self.callbacks.new_dir(full) + -- directory exists => don't need to set lazy. + -- inherit fs_event_providers. + self.dir_watchers[rel] = M.tree( + full, + self.depth - 1, + self.callbacks, + { lazy = false, fs_event_providers = self.fs_event_providers } + ) +end + +function TreeWatcher:change_file(rel, full) + log_tree.debug("changed file %s %s", rel, full) + self.callbacks.change_file(full) +end +function TreeWatcher:change_dir(rel, full) + log_tree.debug("changed dir %s %s", rel, full) + self.callbacks.change_dir(full) +end +function TreeWatcher:change_child(rel, full) + if self.dir_watchers[rel] then + self:change_dir(rel, full) + elseif self.files[rel] then + self:change_file(rel, full) + end +end + +function TreeWatcher:remove_child(rel, full) + if self.dir_watchers[rel] then + log_tree.debug("removing dir %s %s", rel, full) + -- should have been stopped by the watcher for the child, or it was not + -- even started due to depth. + self.dir_watchers[rel]:remove_root() + self.dir_watchers[rel] = nil + + self.callbacks.remove_dir(full) + elseif self.files[rel] then + log_tree.debug("removing file %s %s", rel, full) + self.files[rel] = nil + + self.callbacks.remove_file(full) + end +end + +function TreeWatcher:remove_root() + if self.removed then + -- already removed + return + end + log_tree.debug("removing root %s", self.root) + self.removed = true + -- stop own, children should have handled themselves, if they are watched + -- (and we don't need to do anything for unwatched children). + self:stop_self() + + -- removing entries (set them to nil) is apparently fine when iterating via + -- pairs. + for relpath, _ in pairs(self.files) do + local child_full = Path.join(self.root, relpath) + self:remove_child(relpath, child_full) + end + for relpath, _ in pairs(self.dir_watchers) do + local child_full = Path.join(self.root, relpath) + self:remove_child(relpath, child_full) + end + + self.callbacks.remove_root(self.root) +end + +--- Set up new watcher for a tree of files and directories. +--- @param root string Absolute path to the root. +--- @param depth number The depth up to which to monitor. 1 means that the +--- immediate children will be monitored, 2 includes their +--- children, and so on. +--- @param callbacks LuaSnip.FSWatcher.TreeCallbacks The callbacks to use for this watcher. +--- @param opts LuaSnip.FSWatcher.Options Options, described in their class. +--- @return LuaSnip.FSWatcher.Tree +function M.tree(root, depth, callbacks, opts) + local lazy, fs_event_providers = get_opts(opts) + + -- do nothing on missing callback. + callbacks = setmetatable(callbacks or {}, callback_mt) + + local o = setmetatable({ + root = root, + fs_event = uv.new_fs_event(), + files = {}, + dir_watchers = {}, + -- removed: have not yet triggered the removed-callback. + removed = false, + + -- track whether the watcher was stopped at some point, and if it as, + -- don't allow it to start again. + stopped = false, + -- whether notifications should be sent. + -- Modified by start/stop, wait for start to send any => start out as + -- false. + send_notifications = false, + + callbacks = callbacks, + depth = depth, + fs_event_providers = fs_event_providers, + }, TreeWatcher_mt) + + -- if the path does not yet exist, set watcher up s.t. it will start + -- watching when the directory is created. + if not uv.fs_stat(root) and lazy then + -- root does not yet exist, need to create a watcher that notifies us + -- of its creation. + local parent_path = Path.parent(root) + if not parent_path then + error(("Could not find parent-path for %s"):format(root)) + end + + log_tree.info( + "Path %s does not exist yet, watching %s for creation.", + root, + parent_path + ) + + local parent_watcher + parent_watcher = M.tree(parent_path, 1, { + new_dir = function(full) + if full == root then + o:start() + -- directory was created, stop watching. + parent_watcher:stop_self() + end + end, + -- use same providers. + }, { lazy = true, fs_event_providers = fs_event_providers }) + else + o:start() + end + + return o +end + +--- @class LuaSnip.FSWatcher.Path +--- @field private path string +--- @field private fs_event userdata +--- @field private removed boolean +--- @field private stopped boolean +--- @field private send_notifications boolean +--- @field private callbacks LuaSnip.FSWatcher.TreeCallbacks +--- @field private fs_event_providers table +--- @field private realpath string? Set as soon as the watcher is started. +local PathWatcher = {} + +local PathWatcher_mt = { + __index = PathWatcher, +} + +function PathWatcher:change(full) + log_path.info("detected change at path %s", full) + if self.removed then + -- this is certainly unexpected. + log_path.warn( + "PathWatcher at %s detected change, but path does not exist logically. Not triggering callback.", + full + ) + else + self.callbacks.change(self.path) + end +end + +function PathWatcher:add() + if not self.removed then + -- already added + return + end + log_path.info("adding path %s", self.path) + self.removed = false + + self.callbacks.add(self.path) +end +function PathWatcher:remove() + if self.removed then + -- already removed + return + end + log_path.debug("removing path %s", self.path) + log_path.info("path %s was removed, stopping watcher.", self.path) + + self.removed = true + + self.callbacks.remove(self.path) + + -- Would have to re-register for new file to receive new notifications. + self:stop() +end + +function PathWatcher:fs_event_callback(err, relpath, events) + if not self.send_notifications then + return + end + + vim.schedule_wrap(function() + log_path.debug( + "raw: path: %s; err: %s; relpath: %s; change: %s; rename: %s", + self.path, + err, + relpath, + events.change, + events.rename + ) + + if events.rename then + if not uv.fs_stat(self.path) then + self:remove() + else + self:add() + end + elseif events.change then + self:change() + end + end)() +end + +function PathWatcher:BufWritePost_callback(realpath) + if not self.send_notifications then + return + end + + if realpath == self.realpath then + -- notify using passed path, not realpath. + self:change(self.path) + end +end + +function PathWatcher:start() + if self.stopped then + -- stop() prevents start. + return + end + self.send_notifications = true + + if self.fs_event_providers.libuv then + -- does not work on nfs-drive, at least if it's edited from another + -- machine. + local success, err = self.fs_event:start( + self.path, + {}, + function(err, relpath, events) + self:fs_event_callback(err, relpath, events) + end + ) + + if not success then + log_path.error( + "Could not start libuv-monitor for file %s due to error %s", + self.path, + err + ) + else + log_path.info("Monitoring file %s with libuv-monitor.", self.path) + end + end + + local realpath = Path.normalize(self.path) + + if self.fs_event_providers.autocmd then + if realpath then + self.realpath = realpath + + -- path exists, add file-monitor. + table.insert(M.autocmd_watchers, self) + log_path.info("Monitoring file %s with autocmd-monitor.", self.path) + else + log_path.error( + "Could not resolve realpath for file %s, not enabling BufWritePost-monitor", + self.path + ) + end + end + + if realpath then + table.insert(M.active_watchers, self) + end + + if realpath then + -- path exists, notify. + self:add() + -- no else, never added the path, never call remove. + end +end + +function PathWatcher:stop() + -- don't check which fs_event_providers were actually started, for both of + -- these it should not matter if they weren't. + self.stopped = true + self.send_notifications = false + + self.fs_event:stop() +end + +--- Set up new watcher on a single path only. +--- @param path string Absolute path to the root. +--- @param callbacks LuaSnip.FSWatcher.PathCallbacks The callbacks to use for this watcher. +--- @param opts LuaSnip.FSWatcher.Options? Options, described in their class. +--- @return LuaSnip.FSWatcher.Path +function M.path(path, callbacks, opts) + local lazy, fs_event_providers = get_opts(opts) + + -- do nothing on missing callback. + callbacks = setmetatable(callbacks or {}, callback_mt) + + --- @as LuaSnip.FSWatcher.Path + local o = setmetatable({ + path = path, + fs_event = uv.new_fs_event(), + -- Don't send an initial remove-callback if the path does not yet + -- exist. + -- Always send add first, or send nothing. + removed = true, + -- these two are just like in TreeWatcher. + stopped = false, + -- wait for `start()` to send notifications. + send_notifications = false, + callbacks = callbacks, + fs_event_providers = fs_event_providers, + }, PathWatcher_mt) + + -- if the path does not yet exist, set watcher up s.t. it will start + -- watching when the directory is created. + if not uv.fs_stat(path) and lazy then + -- root does not yet exist, need to create a watcher that notifies us + -- of its creation. + local parent_path = Path.parent(path) + if not parent_path then + error(("Could not find parent-path for %s"):format(path)) + end + + log_path.info( + "Path %s does not exist yet, watching %s for creation.", + path, + parent_path + ) + + local parent_watcher + parent_watcher = M.tree(parent_path, 1, { + -- in path_watcher, watch for new file. + new_file = function(full) + log_path.info("Path: %s %s", full, path) + if full == path then + o:start() + -- directory was created, stop watching. + parent_watcher:stop_self() + end + end, + }, { lazy = true, fs_event_providers = fs_event_providers }) + else + o:start() + end + + return o +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/init.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/init.lua new file mode 100644 index 00000000..daf6064c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/init.lua @@ -0,0 +1,158 @@ +local util = require("luasnip.util.util") +local Path = require("luasnip.util.path") +local loader_util = require("luasnip.loaders.util") +local session = require("luasnip.session") +local loader_data = require("luasnip.loaders.data") +local fs_watchers = require("luasnip.loaders.fs_watchers") +local log = require("luasnip.util.log").new("loader") + +local M = {} + +-- used to map cache-name to name passed to format. +local clean_name = { + vscode_packages = "vscode", + vscode_standalone = "vscode-standalone", + snipmate = "snipmate", + lua = "lua", +} +local function default_format(path, _) + path = path:gsub( + vim.pesc(vim.fn.stdpath("data") .. "/site/pack/packer/start"), + "$PLUGINS" + ) + if vim.env.HOME then + path = path:gsub(vim.pesc(vim.env.HOME .. "/.config/nvim"), "$CONFIG") + end + return path +end + +local function default_edit(file) + vim.cmd("edit " .. file) +end + +--- Quickly jump to snippet-file from any source for the active filetypes. +---@param opts nil|table, options for this function: +--- - ft_filter: fn(filetype:string) -> bool +--- Optionally filter filetypes which can be picked from. `true` -> filetype +--- is listed, `false` -> not listed. +--- +--- - format: fn(path:string, source_name:string) -> string|nil +--- source_name is one of "vscode", "snipmate" or "lua". +--- May be used to format the displayed items. For example, replace the +--- excessively long packer-path with something shorter. +--- If format returns nil for some item, the item will not be displayed. +--- +--- - edit: fn(file:string): this function is called with the snippet-file as +--- the lone argument. +--- The default is a function which just calls `vim.cmd("edit " .. file)`. +function M.edit_snippet_files(opts) + opts = opts or {} + local format = opts.format or default_format + local edit = opts.edit or default_edit + local extend = opts.extend or function(_, _) + return {} + end + + local function ft_edit_picker(ft, _) + if ft then + local ft_paths = {} + local items = {} + + -- concat files from all loaders for the selected filetype ft. + for cache_name, ft_file_set in pairs({ + vscode_packages = loader_data.vscode_ft_paths[ft], + vscode_standalone = {}, + snipmate = loader_data.snipmate_ft_paths[ft], + lua = loader_data.lua_ft_paths[ft], + }) do + for path, _ in pairs(ft_file_set or {}) do + local fmt_name = format(path, clean_name[cache_name]) + if fmt_name then + table.insert(ft_paths, path) + table.insert(items, fmt_name) + end + end + end + + -- extend filetypes with user-defined function. + local extended = extend(ft, ft_paths) + assert( + type(extended) == "table", + "You must return a table in extend function" + ) + for _, pair in ipairs(extended) do + table.insert(items, pair[1]) + table.insert(ft_paths, pair[2]) + end + + -- prompt user again if there are multiple files providing this filetype. + if #ft_paths > 1 then + vim.ui.select(items, { + prompt = "Multiple files for this filetype, choose one:", + }, function(_, indx) + if indx and ft_paths[indx] then + edit(ft_paths[indx]) + end + end) + elseif ft_paths[1] then + edit(ft_paths[1]) + end + end + end + + local ft_filter = opts.ft_filter or util.yes + + local all_fts = {} + vim.list_extend(all_fts, util.get_snippet_filetypes()) + vim.list_extend( + all_fts, + loader_util.get_load_fts(vim.api.nvim_get_current_buf()) + ) + all_fts = util.deduplicate(all_fts) + + local filtered_fts = {} + for _, ft in ipairs(all_fts) do + if ft_filter(ft) then + table.insert(filtered_fts, ft) + end + end + + if #filtered_fts == 1 then + ft_edit_picker(filtered_fts[1]) + elseif #filtered_fts > 1 then + vim.ui.select(filtered_fts, { + prompt = "Select filetype:", + }, ft_edit_picker) + end +end + +function M.cleanup() + require("luasnip.loaders.from_lua").clean() + require("luasnip.loaders.from_snipmate").clean() + require("luasnip.loaders.from_vscode").clean() +end + +function M.load_lazy_loaded(bufnr) + local fts = loader_util.get_load_fts(bufnr) + + for _, ft in ipairs(fts) do + if not session.loaded_fts[ft] then + require("luasnip.loaders.from_lua")._load_lazy_loaded_ft(ft) + require("luasnip.loaders.from_snipmate")._load_lazy_loaded_ft(ft) + require("luasnip.loaders.from_vscode")._load_lazy_loaded_ft(ft) + end + session.loaded_fts[ft] = true + end +end + +function M.reload_file(path) + local realpath = Path.normalize(path) + if not realpath then + return nil, ("Could not reload file %s: does not exist."):format(path) + else + fs_watchers.write_notify(realpath) + return true + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/snippet_cache.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/snippet_cache.lua new file mode 100644 index 00000000..42a590cb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/snippet_cache.lua @@ -0,0 +1,93 @@ +local uv = vim.uv or vim.loop +local duplicate = require("luasnip.nodes.duplicate") + +--- @class LuaSnip.Loaders.SnippetCache.Mtime +--- @field sec number +--- @field nsec number +--- Stores modified time for a file. + +--- @class LuaSnip.Loaders.SnippetCache.TimeCacheEntry +--- @field mtime LuaSnip.Loaders.SnippetCache.Mtime? +--- @field data LuaSnip.Loaders.SnippetFileData +--- mtime is nil if the file does not currently exist. Since `get_fn` may still +--- return data, there's no need to treat this differently. + +--- @class LuaSnip.Loaders.SnippetCache +--- SnippetCache stores snippets and other data loaded by files. +--- @field private get_fn fun(file: string): LuaSnip.Loaders.SnippetFileData +--- @field private cache table +local SnippetCache = {} +SnippetCache.__index = SnippetCache + +local M = {} + +--- @class LuaSnip.Loaders.SnippetFileData +--- @field snippets LuaSnip.Addable[] +--- @field autosnippets LuaSnip.Addable[] +--- @field misc table any data. + +--- Create new cache. +--- @param get_fn fun(file: string): LuaSnip.Loaders.SnippetFileData +--- @return LuaSnip.Loaders.SnippetCache +function M.new(get_fn) + return setmetatable({ + get_fn = get_fn, + cache = {}, + }, SnippetCache) +end + +--- Copy addables from data to new table. +--- @param data LuaSnip.Loaders.SnippetFileData +--- @return LuaSnip.Loaders.SnippetFileData +local function copy_filedata(data) + --- @as LuaSnip.Loaders.SnippetFileData + return { + snippets = vim.tbl_map(duplicate.duplicate_addable, data.snippets), + autosnippets = vim.tbl_map( + duplicate.duplicate_addable, + data.autosnippets + ), + misc = vim.deepcopy(data.misc), + } +end + +--- Retrieve loaded data for any file, either from the cache, or directly from +--- the file. +--- For storage-efficiency (and to elide the otherwise necessary deepcopy), the +--- snippets are duplicated, which should not leak. +--- @param fname string +--- @return LuaSnip.Loaders.SnippetFileData +function SnippetCache:fetch(fname) + local cached = self.cache[fname] + local current_stat = uv.fs_stat(fname) + + --- @as LuaSnip.Loaders.SnippetCache.Mtime + local mtime = current_stat and current_stat.mtime + + if + cached + and mtime + and mtime.sec == cached.mtime.sec + and mtime.nsec == cached.mtime.nsec + then + -- happy path: data is cached, and valid => just return cached data. + return copy_filedata(cached.data) + end + + -- data is stale (cache entry does not exist, file was written after + -- cache-creation, or the file was deleted). + -- fetch data from updated file + local res = self.get_fn(fname) + + -- store it. + self.cache[fname] = { + data = res, + mtime = mtime, + } + + -- return it. + -- Don't copy here, no need to. + return res +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/types.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/types.lua new file mode 100644 index 00000000..b1297e14 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/types.lua @@ -0,0 +1,8 @@ +--- @class LuaSnip.Loaders.LoadOpts +--- @field paths string[]?|string? Either a list of paths, or ","-delimited paths. If nil, searches rtp for snippet-collections. +--- @field lazy_paths string[]?|string? Like paths, but these will be watched, and loaded when creation is detected. +--- @field include string[]? If set, all filetypes not in include will be excluded from loading. +--- @field exclude string[]? Exclude these filetypes, even if they are set in include. +--- @field override_priority number? load all snippets with this priority. +--- @field default_priority number? snippet-priority, unless the snippet sets its own priority. +--- @field fs_event_providers table? How to monitor the filesystem diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/util.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/util.lua new file mode 100644 index 00000000..59333af6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/loaders/util.lua @@ -0,0 +1,299 @@ +local Path = require("luasnip.util.path") +local util = require("luasnip.util.util") +local session = require("luasnip.session") +local snippet_collection = require("luasnip.session.snippet_collection") +local log = require("luasnip.util.log").new("loaders") + +local function filetypelist_to_set(list) + vim.validate({ list = { list, "table", true } }) + if not list then + return list + end + local out = {} + for _, ft in ipairs(list) do + out[ft] = true + end + return out +end + +local function split_lines(filestring) + local newline_code + if vim.endswith(filestring, "\r\n") then -- dos + newline_code = "\r\n" + elseif vim.endswith(filestring, "\r") then -- mac + -- both mac and unix-files contain a trailing newline which would lead + -- to an additional empty line being read (\r, \n _terminate_ lines, they + -- don't _separate_ them) + newline_code = "\r" + filestring = filestring:sub(1, -2) + elseif vim.endswith(filestring, "\n") then -- unix + newline_code = "\n" + filestring = filestring:sub(1, -2) + else -- dos + newline_code = "\r\n" + end + return vim.split( + filestring, + newline_code, + { plain = true, trimemtpy = false } + ) +end + +local function non_nil(v) + return v ~= nil +end + +local function resolve_root_paths(paths, rtp_dirname) + if not paths then + paths = vim.api.nvim_get_runtime_file(rtp_dirname, true) + end + + paths = vim.tbl_map(Path.expand, paths) + paths = vim.tbl_filter(non_nil, paths) + paths = util.deduplicate(paths) + + return paths +end + +local function resolve_lazy_root_paths(paths) + paths = vim.tbl_map(Path.expand_maybe_nonexisting, paths) + paths = vim.tbl_filter(non_nil, paths) + paths = util.deduplicate(paths) + + return paths +end + +local function ft_filter(include, exclude) + exclude = filetypelist_to_set(exclude) + include = filetypelist_to_set(include) + + return function(lang) + if exclude and exclude[lang] then + return false + end + if include == nil or include[lang] then + return true + end + end +end + +local function _append(tbl, name, elem) + if tbl[name] == nil then + tbl[name] = {} + end + table.insert(tbl[name], elem) +end + +---Get paths of .snippets files +---@param root string @snippet directory path +---@return table @keys are file types, values are paths +local function get_ft_paths(root, extension) + local ft_path = {} + local files, dirs = Path.scandir(root) + for _, file in ipairs(files) do + local ft, ext = Path.basename(file, true) + if ext == extension then + _append(ft_path, ft, file) + end + end + for _, dir in ipairs(dirs) do + -- directory-name is ft for snippet-files. + local ft = vim.fn.fnamemodify(dir, ":t") + files, _ = Path.scandir(dir) + for _, file in ipairs(files) do + if vim.endswith(file, extension) then + -- produce normalized filenames. + local normalized_fname = Path.normalize(file) + if normalized_fname then + _append(ft_path, ft, normalized_fname) + end + end + end + end + return ft_path +end + +-- fname must be in the directory-tree below root. +-- collection_root may not end with a path-separator. +-- If both are from "realpath", and fname belongs to the collection, this +-- should be a given. +local function collection_file_ft(collection_root, fname) + local collection_components = Path.components(collection_root) + local fname_components = Path.components(fname) + + if #fname_components == #collection_components + 1 then + -- if the file is a direct child of the collection-root, get the text + -- before the last dot. + return fname_components[#collection_components + 1]:match( + "(.*)%.[^%.]*" + ) + else + -- if the file is nested deeper, the name of the directory immediately + -- below the root is the filetype. + return fname_components[#collection_components + 1] + end +end + +-- extend table like {lua = {path1}, c = {path1, path2}, ...}, new_paths has the same layout. +local function extend_ft_paths(paths, new_paths) + for ft, path in pairs(new_paths) do + if paths[ft] then + vim.list_extend(paths[ft], path) + else + paths[ft] = vim.deepcopy(path) + end + end +end + +--- Find +--- 1. all files that belong to a collection and +--- 2. the files from that +--- collection that should actually be loaded. +---@param opts table: straight from `load`/`lazy_load`. +---@param rtp_dirname string: if no path is given in opts, we look for a +--- directory named `rtp_dirname` in the runtimepath. +---@param extension string: extension of valid snippet-files for the given +--- collection (eg `.lua` or `.snippets`) +---@return table: a list of tables, each of the inner tables contains two +--- entries: +--- - collection_paths: ft->files for the entire collection and +--- - load_paths: ft->files for only the files that should be loaded. +--- All produced filenames are normalized, eg. links are resolved and +--- unnecessary . or .. removed. +local function get_load_paths_snipmate_like(opts, rtp_dirname, extension) + local collections_load_paths = {} + + for _, path in ipairs(resolve_root_paths(opts.paths, rtp_dirname)) do + local collection_ft_paths = get_ft_paths(path, extension) + + local load_paths = vim.deepcopy(collection_ft_paths) + -- remove files for excluded/non-included filetypes here. + local collection_filter = ft_filter(opts.exclude, opts.include) + for ft, _ in pairs(load_paths) do + if not collection_filter(ft) then + load_paths[ft] = nil + end + end + + table.insert(collections_load_paths, { + collection_paths = collection_ft_paths, + load_paths = load_paths, + }) + end + + return collections_load_paths +end + +--- Asks (via vim.ui.select) to edit a file that currently provides snippets +---@param ft_files table, map filetype to a number of files. +local function edit_snippet_files(ft_files) + local fts = util.get_snippet_filetypes() + vim.ui.select(fts, { + prompt = "Select filetype:", + }, function(item, _) + if item then + local ft_paths = ft_files[item] + if ft_paths then + -- prompt user again if there are multiple files providing this filetype. + if #ft_paths > 1 then + vim.ui.select(ft_paths, { + prompt = "Multiple files for this filetype, choose one:", + }, function(multi_item) + if multi_item then + vim.cmd("edit " .. multi_item) + end + end) + else + vim.cmd("edit " .. ft_paths[1]) + end + else + print("No file for this filetype.") + end + end + end) +end + +local function make_add_opts(opts) + return { + override_priority = opts.override_priority, + default_priority = opts.default_priority, + } +end + +local function get_load_fts(bufnr) + local fts = session.config.load_ft_func(bufnr) + -- also add "all", loaded by all buffers. + table.insert(fts, "all") + + return util.deduplicate(util.redirect_filetypes(fts)) +end + +local function add_file_snippets(ft, filename, snippets, autosnippets, add_opts) + snippet_collection.add_snippets( + { [ft] = snippets }, + vim.tbl_extend("keep", { + type = "snippets", + key = "__snippets__" .. ft .. "__" .. filename, + }, add_opts) + ) + snippet_collection.add_snippets( + { [ft] = autosnippets }, + vim.tbl_extend("keep", { + type = "autosnippets", + key = "__autosnippets__" .. ft .. "__" .. filename, + }, add_opts) + ) + log.info( + "Adding %s snippets and %s autosnippets from %s to ft `%s`", + #snippets, + #autosnippets, + filename, + ft + ) +end + +local function normalize_opts(opts) + opts = opts or {} + + local paths = opts.paths + if type(paths) == "string" then + paths = vim.split(paths, ",") + end + + local add_opts = make_add_opts(opts) + local include = opts.include + local exclude = opts.exclude + local lazy_paths = opts.lazy_paths or {} + if type(lazy_paths) == "string" then + lazy_paths = vim.split(lazy_paths, ",") + end + + local fs_event_providers = + vim.F.if_nil(opts.fs_event_providers, { autocmd = true, libuv = false }) + + return { + paths = paths, + lazy_paths = lazy_paths, + include = include, + exclude = exclude, + add_opts = add_opts, + fs_event_providers = fs_event_providers, + } +end + +return { + filetypelist_to_set = filetypelist_to_set, + split_lines = split_lines, + resolve_root_paths = resolve_root_paths, + resolve_lazy_root_paths = resolve_lazy_root_paths, + ft_filter = ft_filter, + get_ft_paths = get_ft_paths, + get_load_paths_snipmate_like = get_load_paths_snipmate_like, + extend_ft_paths = extend_ft_paths, + edit_snippet_files = edit_snippet_files, + make_add_opts = make_add_opts, + collection_file_ft = collection_file_ft, + get_load_fts = get_load_fts, + add_file_snippets = add_file_snippets, + normalize_opts = normalize_opts, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/absolute_indexer.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/absolute_indexer.lua new file mode 100644 index 00000000..18546bdb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/absolute_indexer.lua @@ -0,0 +1,27 @@ +-- absolute_indexer[0][1][2][3] -> { absolute_insert_position = {0,1,2,3} } + +local function new() + return setmetatable({ + absolute_insert_position = {}, + }, { + __index = function(table, key) + table.absolute_insert_position[#table.absolute_insert_position + 1] = + key + return table + end, + }) +end + +return setmetatable({}, { + __index = function(_, key) + -- create new table and index it. + return new()[key] + end, + __call = function(_, ...) + return { + -- passing ... to a function passes only the first of the + -- variable number of args. + absolute_insert_position = type(...) == "number" and { ... } or ..., + } + end, +}) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/choiceNode.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/choiceNode.lua new file mode 100644 index 00000000..22de611b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/choiceNode.lua @@ -0,0 +1,427 @@ +local Node = require("luasnip.nodes.node").Node +local ChoiceNode = Node:new() +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local mark = require("luasnip.util.mark").mark +local session = require("luasnip.session") +local sNode = require("luasnip.nodes.snippet").SN +local extend_decorator = require("luasnip.util.extend_decorator") + +function ChoiceNode:init_nodes() + for i, choice in ipairs(self.choices) do + -- setup jumps + choice.next = self + choice.prev = self + + -- forward values for unknown keys from choiceNode. + choice.choice = self + local node_mt = getmetatable(choice) + setmetatable(choice, { + __index = function(node, key) + return node_mt[key] or node.choice[key] + end, + }) + + -- replace nodes' original update_dependents with function that also + -- calls this choiceNodes' update_dependents. + -- + -- cannot define as `function node:update_dependents()` as _this_ + -- choiceNode would be `self`. + -- Also rely on node.choice, as using `self` there wouldn't be caught + -- by copy and the wrong node would be updated. + choice.update_dependents = function(node) + node:_update_dependents() + node.choice:update_dependents() + end + + choice.next_choice = self.choices[i + 1] + choice.prev_choice = self.choices[i - 1] + end + self.choices[#self.choices].next_choice = self.choices[1] + self.choices[1].prev_choice = self.choices[#self.choices] + + self.active_choice = self.choices[1] +end + +local function C(pos, choices, opts) + opts = opts or {} + if opts.restore_cursor == nil then + -- disable by default, can affect performance. + opts.restore_cursor = false + end + + -- allow passing table of nodes in choices, will be turned into a + -- snippetNode. + for indx, choice in ipairs(choices) do + if not getmetatable(choice) then + -- is a normal table, not a node. + choices[indx] = sNode(nil, choice) + end + end + + local c = ChoiceNode:new({ + active = false, + pos = pos, + choices = choices, + type = types.choiceNode, + mark = nil, + dependents = {}, + -- default to true. + restore_cursor = opts.restore_cursor, + }, opts) + c:init_nodes() + return c +end +extend_decorator.register(C, { arg_indx = 3 }) + +function ChoiceNode:subsnip_init() + node_util.subsnip_init_children(self.parent, self.choices) +end + +ChoiceNode.init_positions = node_util.init_child_positions_func( + "absolute_position", + "choices", + "init_positions" +) +ChoiceNode.init_insert_positions = node_util.init_child_positions_func( + "absolute_insert_position", + "choices", + "init_insert_positions" +) + +function ChoiceNode:make_args_absolute() + -- relative indices are relative to the parent of the choiceNode, + -- temporarily remove last component of position + local last_indx = #self.absolute_insert_position + local last = self.absolute_insert_position[last_indx] + self.absolute_insert_position[#self.absolute_insert_position] = nil + + for _, choice in ipairs(self.choices) do + -- relative to choiceNode!! + choice:make_args_absolute(self.absolute_insert_position) + end + + self.absolute_insert_position[last_indx] = last +end + +function ChoiceNode:put_initial(pos) + local old_pos = vim.deepcopy(pos) + + self.active_choice:put_initial(pos) + + local mark_opts = vim.tbl_extend("keep", { + right_gravity = false, + end_right_gravity = false, + }, self.active_choice:get_passive_ext_opts()) + + self.active_choice.mark = mark(old_pos, pos, mark_opts) + self.visible = true +end + +function ChoiceNode:indent(indentstr) + for _, node in ipairs(self.choices) do + node:indent(indentstr) + end +end + +function ChoiceNode:expand_tabs(tabwidth, indentstringlen) + for _, node in ipairs(self.choices) do + node:expand_tabs(tabwidth, indentstringlen) + end +end + +function ChoiceNode:input_enter(_, dry_run) + if dry_run then + dry_run.active[self] = true + return + end + + self.mark:update_opts(self.ext_opts.active) + self:focus() + + self.prev_choice_node = + session.active_choice_nodes[vim.api.nvim_get_current_buf()] + session.active_choice_nodes[vim.api.nvim_get_current_buf()] = self + self.visited = true + self.active = true + + self:event(events.enter) +end + +function ChoiceNode:input_leave(_, dry_run) + if dry_run then + dry_run.active[self] = false + return + end + + self:event(events.leave) + + self.mark:update_opts(self:get_passive_ext_opts()) + self:update_dependents() + session.active_choice_nodes[vim.api.nvim_get_current_buf()] = + self.prev_choice_node + self.active = false +end + +function ChoiceNode:set_old_text() + self.old_text = self:get_text() + self.active_choice.old_text = self.old_text +end + +function ChoiceNode:get_static_text() + return self.choices[1]:get_static_text() +end + +function ChoiceNode:get_docstring() + return util.string_wrap( + self.choices[1]:get_docstring(), + rawget(self, "pos") + ) +end + +function ChoiceNode:jump_into(dir, no_move, dry_run) + self:init_dry_run_active(dry_run) + + if self:is_active(dry_run) then + self:input_leave(no_move, dry_run) + + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + else + self:input_enter(no_move, dry_run) + + return self.active_choice:jump_into(dir, no_move, dry_run) + end +end + +function ChoiceNode:update() + self.active_choice:update() +end + +function ChoiceNode:update_static_all() + for _, choice in ipairs(self.choices) do + choice:update_static() + end +end + +function ChoiceNode:update_static() + self.active_choice:update_static() +end + +function ChoiceNode:update_restore() + self.active_choice:update_restore() +end + +function ChoiceNode:setup_choice_jumps() end + +function ChoiceNode:find_node(predicate) + if self.active_choice then + if predicate(self.active_choice) then + return self.active_choice + else + return self.active_choice:find_node(predicate) + end + end + return nil +end + +-- used to uniquely identify this change-choice-action. +local change_choice_id = 0 + +function ChoiceNode:set_choice(choice, current_node) + change_choice_id = change_choice_id + 1 + -- to uniquely identify this node later (storing the pointer isn't enough + -- because this is supposed to work with restoreNodes, which are copied). + current_node.change_choice_id = change_choice_id + + local insert_pre_cc = vim.fn.mode() == "i" + -- is byte-indexed! Doesn't matter here, but important to be aware of. + local cursor_pos_pre_relative = + util.pos_sub(util.get_cursor_0ind(), current_node.mark:pos_begin_raw()) + + self.active_choice:store() + + -- tear down current choice. + -- leave all so the choice (could be a snippet) is in the correct state for the next enter. + node_util.leave_nodes_between(self.active_choice, current_node) + + self.active_choice:exit() + + -- clear text. + -- + -- active_choice has to be disabled (nilled?) to prevent reading from + -- cleared mark in set_mark_rgrav (which will be called in + -- self:set_text({""}) a few lines below). + self.active_choice = nil + self:set_text({ "" }) + + self.active_choice = choice + + self.active_choice.mark = self.mark:copy_pos_gravs( + vim.deepcopy(self.active_choice:get_passive_ext_opts()) + ) + + -- re-init positions for child-restoreNodes (they will update their + -- children in put_initial, but their own position has to be changed here). + self:init_positions(self.absolute_position) + self:init_insert_positions(self.absolute_insert_position) + + -- self is still focused, from `set_text`. + self.active_choice:put_initial(self.mark:pos_begin_raw()) + -- adjust gravity in left side of inserted node, such that it matches the + -- current gravity of self. + local _, to = self.mark:pos_begin_end_raw() + self.active_choice:subtree_set_pos_rgrav(to, -1, true) + + self.active_choice:update_restore() + self.active_choice:update_all_dependents() + self:update_dependents() + + -- Another node may have been entered in update_dependents. + self:focus() + self:event(events.change_choice) + + if self.restore_cursor then + local target_node = self:find_node(function(test_node) + return test_node.change_choice_id == change_choice_id + end) + + if target_node then + -- the node that the cursor was in when changeChoice was called exists + -- in the active choice! Enter it and all nodes between it and this choiceNode, + -- then set the cursor. + -- Pass no_move=true, we will set the cursor ourselves. + node_util.enter_nodes_between(self, target_node, true) + + if insert_pre_cc then + util.set_cursor_0ind( + util.pos_add( + target_node.mark:pos_begin_raw(), + cursor_pos_pre_relative + ) + ) + else + node_util.select_node(target_node) + end + return target_node + end + end + + return self.active_choice:jump_into(1) +end + +function ChoiceNode:change_choice(dir, current_node) + -- stylua: ignore + return self:set_choice( + dir == 1 and self.active_choice.next_choice + or self.active_choice.prev_choice, + current_node ) +end + +function ChoiceNode:copy() + local o = vim.deepcopy(self) + for i, node in ipairs(self.choices) do + if node.type == types.snippetNode or node.type == types.choiceNode then + o.choices[i] = node:copy() + else + setmetatable(o.choices[i], getmetatable(node)) + end + end + setmetatable(o, getmetatable(self)) + return o +end + +function ChoiceNode:exit() + self.visible = false + if self.active_choice then + self.active_choice:exit() + end + self.mark:clear() + if self.active then + session.active_choice_nodes[vim.api.nvim_get_current_buf()] = + self.prev_choice_node + end + self.active = false +end + +function ChoiceNode:set_ext_opts(name) + Node.set_ext_opts(self, name) + + self.active_choice:set_ext_opts(name) +end + +function ChoiceNode:store() + self.active_choice:store() +end + +function ChoiceNode:insert_to_node_absolute(position) + if #position == 0 then + return self.absolute_position + end + local front = util.pop_front(position) + return self.choices[front]:insert_to_node_absolute(position) +end + +function ChoiceNode:set_dependents() + for _, node in ipairs(self.choices) do + node:set_dependents() + end +end + +function ChoiceNode:set_argnodes(dict) + Node.set_argnodes(self, dict) + + for _, node in ipairs(self.choices) do + node:set_argnodes(dict) + end +end + +function ChoiceNode:update_all_dependents() + -- call the version that only updates this node. + self:_update_dependents() + + self.active_choice:update_all_dependents() +end + +function ChoiceNode:update_all_dependents_static() + -- call the version that only updates this node. + self:_update_dependents_static() + + self.active_choice:update_all_dependents_static() +end + +function ChoiceNode:resolve_position(position) + return self.choices[position] +end + +function ChoiceNode:static_init() + Node.static_init(self) + self.active_choice:static_init() +end + +function ChoiceNode:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + if self.active_choice then + self.active_choice:subtree_set_pos_rgrav(pos, direction, rgrav) + end +end + +function ChoiceNode:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + if self.active_choice then + self.active_choice:subtree_set_rgrav(rgrav) + end +end + +function ChoiceNode:extmarks_valid() + return node_util.generic_extmarks_valid(self, self.active_choice) +end + +return { + C = C, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/duplicate.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/duplicate.lua new file mode 100644 index 00000000..eac2a31e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/duplicate.lua @@ -0,0 +1,73 @@ +local snip_mod = require("luasnip.nodes.snippet") + +local M = {} + +local DupExpandable = {} + +-- just pass these through to _expandable. +function DupExpandable:get_docstring() + return self._expandable:get_docstring() +end +function DupExpandable:copy() + local copy = self._expandable:copy() + copy.id = self.id + + return copy +end + +-- this is modified in `self:invalidate` _and_ needs to be called on _expandable. +function DupExpandable:matches(...) + -- use snippet-module matches, self._expandable might have had its match + -- overwritten by invalidate. + -- (if there are more issues with this, consider some other mechanism for + -- invalidating) + return snip_mod.Snippet.matches(self._expandable, ...) +end + +-- invalidate has to be called on this snippet itself. +function DupExpandable:invalidate() + snip_mod.Snippet.invalidate(self) +end + +local dup_mt = { + -- index DupExpandable for own functions, and then the expandable stored in + -- self/t. + __index = function(t, k) + if DupExpandable[k] then + return DupExpandable[k] + end + + return t._expandable[k] + end, +} + +function M.duplicate_expandable(expandable) + return setmetatable({ + _expandable = expandable, + -- copy these! + -- if `expandable` is invalidated, we don't necessarily want this + -- expandable to be invalidated as well. + hidden = expandable.hidden, + invalidated = expandable.invalidated, + }, dup_mt) +end + +local DupAddable = {} + +function DupAddable:retrieve_all() + -- always return the same set of items, necessary when for invalidate via + -- key to work correctly. + return self._all +end +local DupAddable_mt = { + __index = DupAddable, +} + +function M.duplicate_addable(addable) + return setmetatable({ + addable = addable, + _all = vim.tbl_map(M.duplicate_expandable, addable:retrieve_all()), + }, DupAddable_mt) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/dynamicNode.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/dynamicNode.lua new file mode 100644 index 00000000..0daad6f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/dynamicNode.lua @@ -0,0 +1,439 @@ +local DynamicNode = require("luasnip.nodes.node").Node:new() +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local Node = require("luasnip.nodes.node").Node +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local FunctionNode = require("luasnip.nodes.functionNode").FunctionNode +local SnippetNode = require("luasnip.nodes.snippet").SN +local extend_decorator = require("luasnip.util.extend_decorator") + +local function D(pos, fn, args, opts) + opts = opts or {} + + return DynamicNode:new({ + pos = pos, + fn = fn, + args = node_util.wrap_args(args), + type = types.dynamicNode, + mark = nil, + user_args = opts.user_args or {}, + dependents = {}, + active = false, + }, opts) +end +extend_decorator.register(D, { arg_indx = 4 }) + +function DynamicNode:input_enter(_, dry_run) + if dry_run then + dry_run.active[self] = true + return + end + + self.visited = true + self.active = true + self.mark:update_opts(self.ext_opts.active) + + self:event(events.enter) +end + +function DynamicNode:input_leave(_, dry_run) + if dry_run then + dry_run.active[self] = false + return + end + self:event(events.leave) + + self:update_dependents() + self.active = false + self.mark:update_opts(self:get_passive_ext_opts()) +end + +function DynamicNode:get_static_text() + if self.static_snip then + return self.static_snip:get_static_text() + else + self:update_static() + if self.static_snip then + return self.static_snip:get_static_text() + else + return { "" } + end + end +end + +function DynamicNode:get_docstring() + if not self.docstring then + if self.static_snip then + self.docstring = self.static_snip:get_docstring() + else + self.docstring = { "" } + end + end + return self.docstring +end + +-- DynamicNode's don't have static text, only set as visible. +function DynamicNode:put_initial(_) + self.visible = true +end + +function DynamicNode:indent(_) end + +function DynamicNode:expand_tabs(_) end + +function DynamicNode:jump_into(dir, no_move, dry_run) + -- init dry_run-state for this node. + self:init_dry_run_active(dry_run) + + if self:is_active(dry_run) then + self:input_leave(no_move, dry_run) + + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + else + self:input_enter(no_move, dry_run) + + if self.snip then + return self.snip:jump_into(dir, no_move, dry_run) + else + -- this will immediately enter and leave, but IMO that's expected + -- behaviour. + self:input_leave(no_move, dry_run) + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + end + end +end + +function DynamicNode:update() + local args = self:get_args() + if vim.deep_equal(self.last_args, args) then + -- no update, the args still match. + return + end + + if not self.parent.snippet:extmarks_valid() then + error("Refusing to update inside a snippet with invalid extmarks") + end + + local tmp + if self.snip then + if not args then + -- a snippet exists, don't delete it. + return + end + + -- build new snippet before exiting, markers may be needed for construncting. + tmp = self.fn( + args, + self.parent, + self.snip.old_state, + unpack(self.user_args) + ) + self.snip:exit() + self.snip = nil + + -- focuses node. + self:set_text({ "" }) + else + self:focus() + if not args then + -- no snippet exists, set an empty one. + tmp = SnippetNode(nil, {}) + else + -- also enter node here. + tmp = self.fn(args, self.parent, nil, unpack(self.user_args)) + end + end + self.last_args = args + + -- act as if snip is directly inside parent. + tmp.parent = self.parent + tmp.indx = self.indx + + tmp.next = self + tmp.prev = self + + tmp.snippet = self.parent.snippet + + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() + tmp:subsnip_init() + + tmp.mark = + self.mark:copy_pos_gravs(vim.deepcopy(tmp:get_passive_ext_opts())) + tmp.dynamicNode = self + tmp.update_dependents = function(node) + node:_update_dependents() + node.dynamicNode:update_dependents() + end + + tmp:init_positions(self.snip_absolute_position) + tmp:init_insert_positions(self.snip_absolute_insert_position) + + tmp:make_args_absolute() + + tmp:set_dependents() + tmp:set_argnodes(self.parent.snippet.dependents_dict) + + if vim.bo.expandtab then + tmp:expand_tabs(util.tab_width(), #self.parent.indentstr) + end + tmp:indent(self.parent.indentstr) + + -- sets own extmarks false,true + self:focus() + local from, to = self.mark:pos_begin_end_raw() + -- inserts nodes with extmarks false,false + tmp:put_initial(from) + -- adjust gravity in left side of snippet, such that it matches the current + -- gravity of self. + tmp:subtree_set_pos_rgrav(to, -1, true) + + self.snip = tmp + + -- Update, tbh no idea how that could come in handy, but should be done. + -- Both are needed, because + -- - a node could only depend on nodes outside of tmp + -- - a node outside of tmp could depend on one inside of tmp + tmp:update() + tmp:update_all_dependents() + + self:update_dependents() +end + +local update_errorstring = [[ +Error while evaluating dynamicNode@%d for snippet '%s': +%s + +:h luasnip-docstring for more info]] +function DynamicNode:update_static() + local args = self:get_static_args() + if vim.deep_equal(self.last_static_args, args) then + -- no update, the args still match. + return + end + + local tmp, ok + if self.static_snip then + if not args then + -- a snippet exists, don't delete it. + return + end + + -- build new snippet before exiting, markers may be needed for construncting. + ok, tmp = pcall( + self.fn, + args, + self.parent, + self.snip.old_state, + unpack(self.user_args) + ) + else + if not args then + -- no snippet exists, set an empty one. + tmp = SnippetNode(nil, {}) + else + -- also enter node here. + ok, tmp = + pcall(self.fn, args, self.parent, nil, unpack(self.user_args)) + end + end + if not ok then + print( + update_errorstring:format(self.indx, self.parent.snippet.name, tmp) + ) + -- set empty snippet on failure + tmp = SnippetNode(nil, {}) + end + self.last_static_args = args + + -- act as if snip is directly inside parent. + tmp.parent = self.parent + tmp.indx = self.indx + tmp.pos = rawget(self, "pos") + + tmp.next = self + tmp.prev = self + + -- doesn't matter here, but they'll have to be set. + tmp.ext_opts = self.parent.ext_opts + tmp.snippet = self.parent.snippet + + tmp.dynamicNode = self + tmp.update_dependents_static = function(node) + node:_update_dependents_static() + node.dynamicNode:update_dependents_static() + end + + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() + tmp:subsnip_init() + + tmp:init_positions(self.snip_absolute_position) + tmp:init_insert_positions(self.snip_absolute_insert_position) + + tmp:make_args_absolute() + + tmp:set_dependents() + tmp:set_argnodes(self.parent.snippet.dependents_dict) + + -- do not expand tabs!! This is only necessary if the snippet is inserted + -- in a buffer, some information is lost if tabs (indent) is replaced with + -- whitespace. + -- This might make a difference when another f/dynamicNode depends on this + -- one, and the function expects expanded tabs... imo the function should + -- be adjusted to accept any whitespace. + tmp:indent(self.parent.indentstr) + + tmp:static_init() + + tmp:update_static() + -- updates dependents in tmp. + tmp:update_all_dependents_static() + + self.static_snip = tmp + -- updates own dependents. + self:update_dependents_static() +end + +function DynamicNode:exit() + self.visible = false + self.mark:clear() + -- check if snip actually exists, may not be the case if + -- the surrounding snippet was deleted just before. + if self.snip then + self.snip:exit() + end + self.stored_snip = self.snip + self.snip = nil + self.active = false +end + +function DynamicNode:set_ext_opts(name) + Node.set_ext_opts(self, name) + + -- might not have been generated (missing nodes). + if self.snip then + self.snip:set_ext_opts(name) + end +end + +function DynamicNode:store() + if self.snip then + self.snip:store() + end +end + +function DynamicNode:update_restore() + -- only restore snippet if arg-values still match. + if self.stored_snip and vim.deep_equal(self:get_args(), self.last_args) then + local tmp = self.stored_snip + + tmp.mark = + self.mark:copy_pos_gravs(vim.deepcopy(tmp:get_passive_ext_opts())) + + -- position might (will probably!!) still have changed, so update it + -- here too (as opposed to only in update). + tmp:init_positions(self.snip_absolute_position) + tmp:init_insert_positions(self.snip_absolute_insert_position) + + tmp:make_args_absolute() + + tmp:set_dependents() + tmp:set_argnodes(self.parent.snippet.dependents_dict) + + -- sets own extmarks false,true + self:focus() + -- inserts nodes with extmarks false,false + local from, to = self.mark:pos_begin_end_raw() + tmp:put_initial(from) + -- adjust gravity in left side of snippet, such that it matches the current + -- gravity of self. + tmp:subtree_set_pos_rgrav(to, -1, true) + + -- set snip before update_restore, since update_restore involves + -- calling `focus`, and that needs `snip` to be set. + -- If it is not set, tmp is not reachable via get_nodes_between. + -- (TODO: This is pretty bad, have to rethink design sometime). + self.snip = tmp + + tmp:update_restore() + else + self:update() + end +end + +function DynamicNode:find_node(predicate) + if self.snip then + if predicate(self.snip) then + return self.snip + else + return self.snip:find_node(predicate) + end + end + return nil +end + +function DynamicNode:insert_to_node_absolute(position) + if #position == 0 then + return self.absolute_position + end + return self.snip and self.snip:insert_to_node_absolute(position) +end + +function DynamicNode:init_insert_positions(position_so_far) + Node.init_insert_positions(self, position_so_far) + self.snip_absolute_insert_position = + vim.deepcopy(self.absolute_insert_position) + -- nodes of current snippet should have a 0 before. + self.snip_absolute_insert_position[#self.snip_absolute_insert_position + 1] = + 0 +end + +function DynamicNode:init_positions(position_so_far) + Node.init_positions(self, position_so_far) + self.snip_absolute_position = vim.deepcopy(self.absolute_position) + -- Reach current snippet as snip_absolute_position..0. + self.snip_absolute_position[#self.snip_absolute_position + 1] = 0 +end + +DynamicNode.make_args_absolute = FunctionNode.make_args_absolute +DynamicNode.set_dependents = FunctionNode.set_dependents + +function DynamicNode:resolve_position(position) + -- position must be 0, there are no other options. + return self.snip +end + +function DynamicNode:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + if self.snip then + self.snip:subtree_set_pos_rgrav(pos, direction, rgrav) + end +end + +function DynamicNode:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + if self.snip then + self.snip:subtree_set_rgrav(rgrav) + end +end + +function DynamicNode:extmarks_valid() + if self.snip then + return node_util.generic_extmarks_valid(self, self.snip) + end + return true +end + +return { + D = D, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/functionNode.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/functionNode.lua new file mode 100644 index 00000000..cb1a77ae --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/functionNode.lua @@ -0,0 +1,155 @@ +local Node = require("luasnip.nodes.node").Node +local FunctionNode = Node:new() +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local types = require("luasnip.util.types") +local tNode = require("luasnip.nodes.textNode").textNode +local extend_decorator = require("luasnip.util.extend_decorator") +local key_indexer = require("luasnip.nodes.key_indexer") + +local function F(fn, args, opts) + opts = opts or {} + + return FunctionNode:new({ + fn = fn, + args = node_util.wrap_args(args), + type = types.functionNode, + mark = nil, + user_args = opts.user_args or {}, + }, opts) +end +extend_decorator.register(F, { arg_indx = 3 }) + +FunctionNode.input_enter = tNode.input_enter + +function FunctionNode:get_static_text() + -- static_text will already have been generated, if possible. + -- If it isn't generated, prevent errors by just setting it to empty text. + if not self.static_text then + self.static_text = { "" } + end + return self.static_text +end + +-- function-text will not stand out in any way in docstring. +FunctionNode.get_docstring = FunctionNode.get_static_text + +function FunctionNode:update() + local args = self:get_args() + -- skip this update if + -- - not all nodes are available. + -- - the args haven't changed. + if not args or vim.deep_equal(args, self.last_args) then + return + end + + if not self.parent.snippet:extmarks_valid() then + error("Refusing to update inside a snippet with invalid extmarks") + end + + self.last_args = args + local text = + util.to_string_table(self.fn(args, self.parent, unpack(self.user_args))) + if vim.bo.expandtab then + util.expand_tabs(text, util.tab_width(), #self.parent.indentstr) + end + + -- don't expand tabs in parent.indentstr, use it as-is. + self:set_text(util.indent(text, self.parent.indentstr)) + self:update_dependents() +end + +local update_errorstring = [[ +Error while evaluating functionNode@%d for snippet '%s': +%s + +:h luasnip-docstring for more info]] +function FunctionNode:update_static() + local args = self:get_static_args() + -- skip this update if + -- - not all nodes are available. + -- - the args haven't changed. + if not args or vim.deep_equal(args, self.last_args) then + return + end + -- should be okay to set last_args even if `fn` potentially fails, future + -- updates will fail aswell, if not the `fn` also doesn't always work + -- correctly in normal expansion. + self.last_args = args + local ok, static_text = + pcall(self.fn, args, self.parent, unpack(self.user_args)) + if not ok then + print( + update_errorstring:format( + self.indx, + self.parent.snippet.name, + static_text + ) + ) + static_text = { "" } + end + self.static_text = + util.indent(util.to_string_table(static_text), self.parent.indentstr) +end + +function FunctionNode:update_restore() + -- only if args still match. + if self.static_text and vim.deep_equal(self:get_args(), self.last_args) then + self:set_text(self.static_text) + else + self:update() + end +end + +-- FunctionNode's don't have static text, only set visibility. +function FunctionNode:put_initial(_) + self.visible = true +end + +function FunctionNode:indent(_) end + +function FunctionNode:expand_tabs(_) end + +function FunctionNode:make_args_absolute(position_so_far) + self.args_absolute = {} + node_util.make_args_absolute(self.args, position_so_far, self.args_absolute) +end + +function FunctionNode:set_dependents() + local dict = self.parent.snippet.dependents_dict + local append_list = + vim.list_extend({ "dependents" }, self.absolute_position) + append_list[#append_list + 1] = "dependent" + + for _, arg in ipairs(self.args_absolute) do + -- if arg is a luasnip-node, just insert it as the key. + -- important!! rawget, because indexing absolute_indexer with some key + -- appends the key. + -- Maybe this is stupid?? + if rawget(arg, "type") ~= nil then + dict:set(vim.list_extend({ arg }, append_list), self) + elseif arg.absolute_insert_position then + -- copy absolute_insert_position, list_extend mutates. + dict:set( + vim.list_extend( + vim.deepcopy(arg.absolute_insert_position), + append_list + ), + self + ) + elseif key_indexer.is_key(arg) then + dict:set(vim.list_extend({ "key", arg.key }, append_list), self) + end + end +end + +function FunctionNode:is_interactive() + -- the function node is only evaluated once if it has no argnodes -> it's + -- not interactive then. + return #self.args ~= 0 +end + +return { + F = F, + FunctionNode = FunctionNode, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/insertNode.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/insertNode.lua new file mode 100644 index 00000000..b8b456fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/insertNode.lua @@ -0,0 +1,330 @@ +local Node = require("luasnip.nodes.node") +local InsertNode = Node.Node:new() +local ExitNode = InsertNode:new() +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local extend_decorator = require("luasnip.util.extend_decorator") + +local function I(pos, static_text, opts) + static_text = util.to_string_table(static_text) + + if pos == 0 then + return ExitNode:new({ + pos = pos, + static_text = static_text, + mark = nil, + dependents = {}, + type = types.exitNode, + -- will only be needed for 0-node, -1-node isn't set with this. + ext_gravities_active = { false, false }, + }, opts) + else + return InsertNode:new({ + pos = pos, + static_text = static_text, + mark = nil, + dependents = {}, + type = types.insertNode, + inner_active = false, + }, opts) + end +end +extend_decorator.register(I, { arg_indx = 3 }) + +function ExitNode:input_enter(no_move, dry_run) + if dry_run then + return + end + + -- Don't enter node for -1-node, it isn't in the node-table. + if self.pos == 0 then + InsertNode.input_enter(self, no_move, dry_run) + else + -- -1-node: + -- set rgrav true on left side of snippet. Text inserted now pushes the + -- snippet, and is not contained in it. + local begin_pos = self.mark:pos_begin_raw() + self.parent:subtree_set_pos_rgrav(begin_pos, 1, true) + + if not no_move then + if vim.fn.mode() == "i" then + util.insert_move_on(begin_pos) + else + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes("", true, false, true), + "n", + true + ) + util.normal_move_on_insert(begin_pos) + end + end + + self:event(events.enter) + end +end + +function ExitNode:focus() + local lrgrav, rrgrav + local snippet = self.parent + -- if last of first node of the snippet, make inserted text move out of snippet. + if snippet.nodes[#snippet.nodes] == self then + lrgrav = false + rrgrav = false + elseif snippet.nodes[1] == self then + lrgrav = true + rrgrav = true + else + lrgrav = false + rrgrav = true + end + + Node.focus_node(self, lrgrav, rrgrav) +end + +function ExitNode:input_leave(no_move, dry_run) + if dry_run then + return + end + + if self.pos == 0 then + InsertNode.input_leave(self, no_move, dry_run) + else + self:event(events.leave) + end +end + +function ExitNode:_update_dependents() end +function ExitNode:update_dependents() end +function ExitNode:update_all_dependents() end + +function ExitNode:_update_dependents_static() end +function ExitNode:update_dependents_static() end +function ExitNode:update_all_dependents_static() end +function ExitNode:is_interactive() + return true +end + +function InsertNode:input_enter(no_move, dry_run) + if dry_run then + return + end + + self.visited = true + self.mark:update_opts(self.ext_opts.active) + + -- no_move only prevents moving the cursor, but the active node should + -- still be focused. + self:focus() + + if not no_move then + -- SELECT snippet text only when there is text to select (more oft than not there isnt). + local mark_begin_pos, mark_end_pos = self.mark:pos_begin_end_raw() + if not util.pos_equal(mark_begin_pos, mark_end_pos) then + util.any_select(mark_begin_pos, mark_end_pos) + else + -- if current and target mode is INSERT, there's no reason to leave it. + if vim.fn.mode() == "i" then + util.insert_move_on(mark_begin_pos) + else + -- mode might be VISUAL or something else, but always leads to normal. + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes("", true, false, true), + "n", + true + ) + util.normal_move_on_insert(mark_begin_pos) + end + end + end + + self:event(events.enter) +end + +-- only necessary for insertNodes, inner_active (unlike `active`) does not occur +-- in other nodes. +-- Since insertNodes don't have `active`, we can use the dry_run.active-field +-- for this. +function InsertNode:init_dry_run_inner_active(dry_run) + if dry_run and dry_run.active[self] == nil then + dry_run.active[self] = self.inner_active + end +end +function InsertNode:is_inner_active(dry_run) + return (not dry_run and self.inner_active) + or (dry_run and dry_run.active[self]) +end + +function InsertNode:jump_into(dir, no_move, dry_run) + self:init_dry_run_inner_active(dry_run) + + if self:is_inner_active(dry_run) then + if dir == 1 then + if self.next then + self:input_leave_children(dry_run) + self:input_leave(no_move, dry_run) + return self.next:jump_into(dir, no_move, dry_run) + else + return nil + end + else + if self.prev then + self:input_leave_children(dry_run) + self:input_leave(no_move, dry_run) + return self.prev:jump_into(dir, no_move, dry_run) + else + return nil + end + end + else + self:input_enter(no_move, dry_run) + return self + end +end + +function ExitNode:jump_from(dir, no_move, dry_run) + self:init_dry_run_inner_active(dry_run) + + local next_node = util.ternary(dir == 1, self.next, self.prev) + local next_inner_node = + util.ternary(dir == 1, self.inner_first, self.inner_last) + + if next_inner_node then + self:input_enter_children(dry_run) + return next_inner_node:jump_into(dir, no_move, dry_run) + else + if next_node then + local next_node_dry_run = { active = {} } + -- don't have to `init_dry_run_inner_active` since this node does + -- not have children active if jump_from is called. + + -- true: don't move + local target_node = + next_node:jump_into(dir, true, next_node_dry_run) + -- if there is no node that can serve as jump-target, just remain + -- here. + -- Regular insertNodes don't have to handle this, since there is + -- always an exitNode or another insertNode at their endpoints. + if not target_node then + return self + end + + self:input_leave(no_move, dry_run) + return next_node:jump_into(dir, no_move, dry_run) or self + else + return self + end + end +end + +function InsertNode:jump_from(dir, no_move, dry_run) + self:init_dry_run_inner_active(dry_run) + + local next_node = util.ternary(dir == 1, self.next, self.prev) + local next_inner_node = + util.ternary(dir == 1, self.inner_first, self.inner_last) + + if next_inner_node then + self:input_enter_children(dry_run) + return next_inner_node:jump_into(dir, no_move, dry_run) + else + if next_node then + self:input_leave(no_move, dry_run) + return next_node:jump_into(dir, no_move, dry_run) + end + end +end + +function InsertNode:input_enter_children(dry_run) + if dry_run then + dry_run.active[self] = true + else + self.inner_active = true + end +end +function InsertNode:input_leave_children(dry_run) + if dry_run then + dry_run.active[self] = false + else + self.inner_active = false + end +end + +function InsertNode:input_leave(_, dry_run) + if dry_run then + return + end + + self:event(events.leave) + + self:update_dependents() + self.mark:update_opts(self:get_passive_ext_opts()) +end + +function InsertNode:exit() + if self.inner_first then + self.inner_first:exit() + end + self.visible = false + self.inner_first = nil + self.inner_last = nil + self.inner_active = false + self.mark:clear() +end + +function InsertNode:get_docstring() + -- copy as to not in-place-modify static text. + return util.string_wrap(self.static_text, rawget(self, "pos")) +end + +function InsertNode:is_interactive() + return true +end + +function InsertNode:child_snippets() + local own_child_snippets = {} + for _, child_snippet in ipairs(self.parent.snippet.child_snippets) do + if child_snippet.parent_node == self then + table.insert(own_child_snippets, child_snippet) + end + end + return own_child_snippets +end + +function InsertNode:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + + local own_child_snippets = self:child_snippets() + + local child_from_indx + if direction == 1 then + child_from_indx = 1 + else + child_from_indx = #own_child_snippets + end + + node_util.nodelist_adjust_rgravs( + own_child_snippets, + child_from_indx, + pos, + direction, + rgrav, + -- don't assume that the child-snippets are all adjacent. + false + ) +end + +function InsertNode:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + + local own_child_snippets = self:child_snippets() + + for _, child_snippet in ipairs(own_child_snippets) do + child_snippet:subtree_set_rgrav(rgrav) + end +end + +return { + I = I, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/key_indexer.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/key_indexer.lua new file mode 100644 index 00000000..04e6f7f3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/key_indexer.lua @@ -0,0 +1,12 @@ +local M = {} + +local key_mt = {} +function M.new_key(key) + return setmetatable({ key = key }, key_mt) +end + +function M.is_key(t) + return getmetatable(t) == key_mt +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/multiSnippet.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/multiSnippet.lua new file mode 100644 index 00000000..108206ea --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/multiSnippet.lua @@ -0,0 +1,117 @@ +local snip_mod = require("luasnip.nodes.snippet") +local node_util = require("luasnip.nodes.util") +local extend_decorator = require("luasnip.util.extend_decorator") + +local VirtualSnippet = {} +local VirtualSnippet_mt = { __index = VirtualSnippet } + +function VirtualSnippet:get_docstring() + return self.snippet:get_docstring() +end +function VirtualSnippet:copy() + local copy = self.snippet:copy() + copy.id = self.id + + return copy +end + +-- VirtualSnippet has all the fields for executing these methods. +VirtualSnippet.matches = snip_mod.Snippet.matches +VirtualSnippet.invalidate = snip_mod.Snippet.invalidate + +---Create new virtual snippet, ie. an object which is capable of performning +---all the functions expected from a snippet which is yet to be expanded +---(`matches`,`get_docstring`,`invalidate`,`retrieve_all`,`copy`) +---@param context context as defined for snippet-constructor. Table, not nil. +---@param snippet The snippet this virtual snippet will return on `copy`, also not nil. +---@param opts opts as defined for snippet-constructor. Has to be a table, may be empty. +local function new_virtual_snippet(context, snippet, opts) + -- init fields necessary for matches, invalidate, adding the snippet. + local o = snip_mod.init_snippet_context(context, opts) + o.snippet = snippet + + setmetatable(o, VirtualSnippet_mt) + + return o +end + +local MultiSnippet = {} +local MultiSnippet_mt = { __index = MultiSnippet } + +function MultiSnippet:retrieve_all() + return self.v_snips +end + +local function multisnippet_from_snippet_obj(contexts, snippet, snippet_opts) + assert( + type(contexts) == "table", + "multisnippet: expected contexts to be a table." + ) + local common_context = node_util.wrap_context(contexts.common) or {} + + local v_snips = {} + for _, context in ipairs(contexts) do + local complete_context = vim.tbl_extend( + "keep", + node_util.wrap_context(context), + common_context + ) + table.insert( + v_snips, + new_virtual_snippet(complete_context, snippet, snippet_opts) + ) + end + + local o = { + v_snips = v_snips, + } + + setmetatable(o, MultiSnippet_mt) + + return o +end + +local function multisnippet_from_nodes(contexts, nodes, opts) + opts = opts or {} + local common_snip_opts = opts.common_opts or {} + + -- create snippet without `context`-fields! + -- compare to `S` (aka `s`, the default snippet-constructor) in + -- `nodes/snippet.lua`. + return multisnippet_from_snippet_obj( + contexts, + snip_mod._S( + snip_mod.init_snippet_opts(common_snip_opts), + nodes, + common_snip_opts + ), + common_snip_opts + ) +end + +local function extend_multisnippet_contexts(passed_arg, extend_arg) + -- extend passed arg with contexts passed in extend-call + vim.list_extend(passed_arg, extend_arg) + + -- extend ("keep") valid keyword-arguments. + passed_arg.common = vim.tbl_deep_extend( + "keep", + node_util.wrap_context(passed_arg.common) or {}, + node_util.wrap_context(extend_arg.common) or {} + ) + + return passed_arg +end +extend_decorator.register( + multisnippet_from_nodes, + -- first arg needs special handling (extend list of contexts (index i + -- becomes i+#passed_arg, not i again)) + { arg_indx = 1, extend = extend_multisnippet_contexts }, + -- opts can just be `vim.tbl_extend`ed. + { arg_indx = 3 } +) + +return { + new_multisnippet = multisnippet_from_nodes, + _raw_ms = multisnippet_from_snippet_obj, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/node.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/node.lua new file mode 100644 index 00000000..ad41c923 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/node.lua @@ -0,0 +1,643 @@ +local session = require("luasnip.session") +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local ext_util = require("luasnip.util.ext_opts") +local events = require("luasnip.util.events") +local key_indexer = require("luasnip.nodes.key_indexer") +local types = require("luasnip.util.types") + +local Node = {} + +function Node:new(o, opts) + o = o or {} + + -- visible is true if the node is visible on-screen, during normal + -- expansion, static_visible is needed for eg. get_static_text, where + -- argnodes in inactive choices will happily provide their static text, + -- which leads to inaccurate docstrings. + o.visible = false + o.static_visible = false + o.old_text = {} + o.visited = false + -- override existing keys, might be necessary due to double-init from + -- snippetProxy, but shouldn't hurt. + o = vim.tbl_extend("force", o, node_util.init_node_opts(opts or {})) + + setmetatable(o, self) + self.__index = self + + return o +end + +function Node:get_static_text() + -- return nil if not visible. + -- This will prevent updates if not all nodes are visible during + -- docstring/static_text-generation. (One example that would otherwise fail + -- is the following snippet: + -- + -- s("trig", { + -- i(1, "cccc"), + -- t" ", + -- c(2, { + -- t"aaaa", + -- i(nil, "bbbb") + -- }), + -- f(function(args) return args[1][1]..args[2][1] end, {ai[2][2], 1} ) + -- }) + -- + -- ) + -- By also allowing visible, and not only static_visible, the docstrings + -- generated during `get_current_choices` (ie. without having the whole + -- snippet `static_init`ed) get better. + if not self.visible and not self.static_visible then + return nil + end + return self.static_text +end + +function Node:get_docstring() + -- visibility only matters for get_static_text because that's called for + -- argnodes whereas get_docstring will only be called for actually + -- visible nodes. + return self.static_text +end + +function Node:put_initial(pos) + -- access static text directly, get_static_text() won't work due to + -- static_visible not being set. + util.put(self.static_text, pos) + self.visible = true +end + +function Node:input_enter(_, _) + self.visited = true + self.mark:update_opts(self.ext_opts.active) + + self:event(events.enter) +end + +-- dry_run: if not nil, it has to be a table with the key `active` also a table. +-- dry_run.active[node] stores whether the node is "active" in the dry run (we +-- can't change the `active`-state in the actual node, so changes to the +-- active-state are stored in the `dry_run`-table, which is passed to all nodes +-- that participate in the jump) +-- The changes to `active` have to be stored. Otherwise, `dry_run` can lead to +-- endless loops in cases like: +-- ```lua +-- s({ trig = 'n' } , { i(1, "1"), sn(2, {t"asdf"}), i(3, "3") }) +-- ``` +-- +-- Here, jumping from 1 will first set active on the snippetNode, then, since +-- there are no interactive nodes inside it, and since active is set, we will +-- jump to the `i(3)`. +-- If active is not set during the dry_run, we will just keep jumping into the +-- inner textNode. +-- +-- A similar problem occurs in nested expansions (insertNode.inner_active +-- is not set). +function Node:jump_into(_, no_move, dry_run) + if not dry_run then + self:input_enter(no_move, dry_run) + end + return self +end + +function Node:jump_from(dir, no_move, dry_run) + self:input_leave(no_move, dry_run) + if dir == 1 then + if self.next then + return self.next:jump_into(dir, no_move, dry_run) + else + return nil + end + else + if self.prev then + return self.prev:jump_into(dir, no_move, dry_run) + else + return nil + end + end +end + +function Node:jumpable(dir) + if dir == 1 then + return self.next ~= nil + else + return self.prev ~= nil + end +end + +function Node:get_text() + if not self.visible then + return nil + end + local ok, text = pcall(function() + local from_pos, to_pos = self.mark:pos_begin_end_raw() + + -- end-exclusive indexing. + local lines = + vim.api.nvim_buf_get_lines(0, from_pos[1], to_pos[1] + 1, false) + + if #lines == 1 then + lines[1] = string.sub(lines[1], from_pos[2] + 1, to_pos[2]) + else + lines[1] = string.sub(lines[1], from_pos[2] + 1, #lines[1]) + + -- node-range is end-exclusive. + lines[#lines] = string.sub(lines[#lines], 1, to_pos[2]) + end + return lines + end) + -- if deleted. + return ok and text or { "" } +end + +function Node:set_old_text() + self.old_text = self:get_text() +end + +function Node:exit() + self.visible = false + self.mark:clear() +end + +function Node:get_passive_ext_opts() + if self.visited then + return self.ext_opts.visited + else + return self.ext_opts.unvisited + end +end + +function Node:input_leave(_, dry_run) + if dry_run then + return + end + self:event(events.leave) + + self.mark:update_opts(self:get_passive_ext_opts()) +end +function Node:input_leave_children() end +function Node:input_enter_children() end + +local function find_dependents(self, position_self, dict) + local nodes = {} + + -- this might also be called from a node which does not possess a position! + -- (for example, a functionNode may be depended upon via its key) + if position_self then + position_self[#position_self + 1] = "dependents" + vim.list_extend(nodes, dict:find_all(position_self, "dependent") or {}) + position_self[#position_self] = nil + end + + vim.list_extend( + nodes, + dict:find_all({ self, "dependents" }, "dependent") or {} + ) + + if self.key then + vim.list_extend( + nodes, + dict:find_all({ "key", self.key, "dependents" }, "dependent") or {} + ) + end + + return nodes +end + +function Node:_update_dependents() + local dependent_nodes = find_dependents( + self, + self.absolute_insert_position, + self.parent.snippet.dependents_dict + ) + if #dependent_nodes == 0 then + return + end + for _, node in ipairs(dependent_nodes) do + if node.visible then + node:update() + end + end +end + +-- _update_dependents is the function to update the nodes' dependents, +-- update_dependents is what will actually be called. +-- This allows overriding update_dependents in a parent-node (eg. snippetNode) +-- while still having access to the original function (for subsequent overrides). +Node.update_dependents = Node._update_dependents +-- update_all_dependents is used to update all nodes' dependents in a +-- snippet-tree. Necessary in eg. set_choice (especially since nodes may have +-- dependencies outside the tree itself, so update_all_dependents should take +-- care of those too.) +Node.update_all_dependents = Node._update_dependents + +function Node:_update_dependents_static() + local dependent_nodes = find_dependents( + self, + self.absolute_insert_position, + self.parent.snippet.dependents_dict + ) + if #dependent_nodes == 0 then + return + end + for _, node in ipairs(dependent_nodes) do + if node.static_visible then + node:update_static() + end + end +end + +Node.update_dependents_static = Node._update_dependents_static +Node.update_all_dependents_static = Node._update_dependents_static + +function Node:update() end + +function Node:update_static() end + +function Node:expand_tabs(tabwidth, indentstr) + util.expand_tabs(self.static_text, tabwidth, indentstr) +end + +function Node:indent(indentstr) + util.indent(self.static_text, indentstr) +end + +function Node:subsnip_init() end + +function Node:init_positions(position_so_far) + self.absolute_position = vim.deepcopy(position_so_far) +end + +function Node:init_insert_positions(position_so_far) + self.absolute_insert_position = vim.deepcopy(position_so_far) +end + +function Node:event(event) + local node_callback = self.node_callbacks[event] + if node_callback then + node_callback(self) + end + + -- try to get the callback from the parent. + if self.pos then + -- node needs position to get callback (nodes may not have position if + -- defined in a choiceNode, ie. c(1, { + -- i(nil, {"works!"}) + -- })) + -- works just fine. + local parent_callback = self.parent.callbacks[self.pos][event] + if parent_callback then + parent_callback(self) + end + end + + session.event_node = self + vim.api.nvim_exec_autocmds("User", { + pattern = "Luasnip" .. events.to_string(self.type, event), + modeline = false, + }) +end + +local function get_args(node, get_text_func_name) + local argnodes_text = {} + for _, arg in ipairs(node.args_absolute) do + local argnode + if key_indexer.is_key(arg) then + argnode = node.parent.snippet.dependents_dict:get({ + "key", + arg.key, + "node", + }) + else + -- since arg may be a node, it may not be initialized in the snippet + -- and therefore not have an absolute_insert_position. Check for that. + if not arg.absolute_insert_position then + -- the node is not (yet, maybe) visible. + return nil + end + local dict_key = arg.absolute_insert_position + -- will append to arg.absolute_insert_position, but it's restored + -- two lines down. + -- (dict:get shouldn't (yeah yeah, you never know, but this really + -- shouldn't) fail, so we don't worry with pcall) + table.insert(dict_key, "node") + argnode = node.parent.snippet.dependents_dict:get(dict_key) + dict_key[#dict_key] = nil + end + -- maybe the node is part of a dynamicNode and not yet generated. + if not argnode then + return nil + end + + local argnode_text = argnode[get_text_func_name](argnode) + -- can only occur with `get_text`. If one returns nil, the argnode + -- isn't visible or some other error occured. Either way, return nil + -- to signify that not all argnodes are available. + if not argnode_text then + return nil + end + table.insert(argnodes_text, argnode_text) + end + + return argnodes_text +end + +function Node:get_args() + return get_args(self, "get_text") +end +function Node:get_static_args() + return get_args(self, "get_static_text") +end + +function Node:get_jump_index() + return self.pos +end + +function Node:set_ext_opts(name) + -- differentiate, either visited or unvisited needs to be set. + if name == "passive" then + self.mark:update_opts(self:get_passive_ext_opts()) + else + self.mark:update_opts(self.ext_opts[name]) + end +end + +-- for insert,functionNode. +function Node:store() + self.static_text = self:get_text() +end + +function Node:update_restore() end + +-- find_node only needs to check children, self is checked by the parent. +function Node:find_node() + return nil +end + +Node.ext_gravities_active = { false, true } + +function Node:insert_to_node_absolute(position) + -- this node is a leaf, just return its position + return self.absolute_position +end + +function Node:set_dependents() end + +function Node:set_argnodes(dict) + if self.absolute_insert_position then + -- append+remove "node" from absolute_insert_position to quickly create + -- key for dict. + table.insert(self.absolute_insert_position, "node") + dict:set(self.absolute_insert_position, self) + self.absolute_insert_position[#self.absolute_insert_position] = nil + end + if self.key then + dict:set({ "key", self.key, "node" }, self) + end +end + +function Node:make_args_absolute() end + +function Node:resolve_position(position) + error( + string.format( + "invalid resolve_position(%d) on node at %s", + position, + vim.inspect(self.absolute_position) + ) + ) +end + +function Node:static_init() + self.static_visible = true +end + +-- resolve_*node*_ext_opts because snippet(Node)s have child_ext_opts, which +-- also have to be resolved. +-- This function generates a nodes ext_opts (those actually used in highlighting). +function Node:resolve_node_ext_opts(base_prio, parent_ext_opts) + if self.merge_node_ext_opts then + self.ext_opts = ext_util.extend( + vim.deepcopy(self.node_ext_opts), + parent_ext_opts or self.parent.effective_child_ext_opts[self.type] + ) + else + self.ext_opts = self.node_ext_opts + end + + ext_util.set_abs_prio( + self.ext_opts, + (base_prio or self.parent.ext_opts.base_prio) + + session.config.ext_prio_increase + ) +end + +function Node:is_interactive() + -- safe default. + return true +end + +-- initialize active-setting in dry_run-table for `self`. +function Node:init_dry_run_active(dry_run) + if dry_run and dry_run.active[self] == nil then + dry_run.active[self] = self.active + end +end +-- determine whether this node is currently active. +-- This is its own function (and not just a flat table-check) since we have to +-- check the data in the dry_run-table or the node, depending on `dry_run`. +function Node:is_active(dry_run) + return (not dry_run and self.active) or (dry_run and dry_run.active[self]) +end + +function Node:get_buf_position(opts) + opts = opts or {} + local raw = opts.raw ~= nil and opts.raw or true + + if raw then + return self.mark:pos_begin_end_raw() + else + return self.mark:pos_begin_end() + end +end + +-- only does something for insert- and snippetNode. +function Node:set_sibling_rgravs(_, _, _, _) end + +-- when an insertNode receives text, its mark/region should contain all the +-- text that is inserted. +-- This can be achieved by setting the left and right "right-gravity"(rgrav) of +-- the mark, which are responsible for controlling the direction an endpoint of +-- the mark is moved when text is inserted. +-- When a regular insertNode is focused/entered, we would like the left and +-- right rgrav to be false and true, respectively. Example: +-- this is an insertNodeAnd this is another insertNode +-- mark1: l r +-- mark2: l r +-- if `this is an insertNode` should be focused, we have to set the rgrav of +-- l1 false (because inserting text at the column of l1 should not shift l1 to +-- the right). Similarly, the rgrav of r1 has to be set true, text inserted at +-- its column SHOULD move it to the right. +-- Complicating this whole thing: if like above there is an adjacent +-- insertNode, its gravities have to be adjusted as well (if they are not, the +-- insertNodes regions would overlap, which is obviously confusing). So, when +-- adjusting some nodes rgravs, those of the siblings may have to be adjusted as well. +-- Another example: +-- aacc +-- mark1: l r +-- mark2: l +-- r +-- mark3: l r +-- (the insertNode for mark2 is not visible at all, l2 and r2 are in the same +-- column) +-- This example highlights that not only the immediate sibling might need +-- adjusting, but all siblings that share a mark-boundary with the node that +-- should be focused. +-- Even further complicating the matter: Snippets are trees, and failing to +-- set the rgrav of snippet adjacent to (sharing an endpoint with) the node we +-- want to focus, regardless of its position in the tree, will lead to extmarks +-- covering the wrong regions. +-- +-- More complications: focusing a node does not always mean setting the rgravs +-- such that text will end up inside the node! +-- For example, in the case of a terminating i(0) (like s("trig", {i(1, +-- "text"), t" ", i(0)})), we would like to NOT include the text entered into +-- it in the snippet. Thus, the gravities of it and all its parents have to be +-- set (in this case) false,false, if the i(0) were at the beginning of the +-- snippet (weird??) they'd have to be true,true. +-- +-- +-- Unfortunately, we cannot guarantee that two extmarks on the same position +-- also have the same gravities, for exmample if the text inside a focused node +-- is deleted, and then another unrelated node is focused, the two endpoints of +-- the previously focused node will have opposing rgravs. +-- Maybe this whole procedure could be sped up further if we can assume that +-- identical endpoints imply identical rgravs. +local function focus_node(self, lrgrav, rrgrav) + -- find nodes on path from self to root. + local nodes_path = node_util.root_path(self) + + -- direction is the direction away from this node, towards the outside of + -- the tree-representation of the snippet. + -- This is dubbed "direction" because it is the direction we will search in + -- to find nodes on one endpoint of self. + for _, direction in ipairs({ -1, 1 }) do + local self_direction_endpoint = self.mark:get_endpoint(direction) + local direction_rgrav = util.ternary(direction == -1, lrgrav, rrgrav) + local effective_direction_rgrav = direction_rgrav + + -- adjust left rgrav of all nodes on path upwards to root/snippet: + -- (i st. self and the snippet are both handled) + for i = 1, #nodes_path do + local node = nodes_path[i] + local node_direction_endpoint = node.mark:get_endpoint(direction) + + if + not util.pos_equal( + node_direction_endpoint, + self_direction_endpoint + ) + then + -- stop adjusting rgravs once self no longer is on the boundary of + -- its parents, or if the rgrav is already set correctly. + break + end + + node.mark:set_rgrav(direction, effective_direction_rgrav) + + -- Once self's snippet is reached on the root-path, we will only + -- adjust nodes self should be completely contained inside. + -- Since the rgravs, however, may be set up otherwise (for example + -- when focusing on an $0 that is the last node of the snippet), we + -- have to adjust them now. + if node.snippet == node then + effective_direction_rgrav = direction == 1 + end + + -- can't use node.parent, since that might skip nodes (in the case of + -- dynamicNode, for example, the generated snippets parent is not the + -- dynamicNode, but its parent). + -- also: don't need to check for nil, because the + local node_above = nodes_path[i + 1] + if node_above then + node_above:set_sibling_rgravs( + node, + self_direction_endpoint, + direction, + effective_direction_rgrav + ) + end + end + self:subtree_set_pos_rgrav( + self_direction_endpoint, + -direction, + direction_rgrav + ) + end +end + +function Node:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) +end + +function Node:subtree_set_pos_rgrav(_, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) +end + +function Node:focus() + focus_node(self, false, true) +end + +function Node:set_text(text) + self:focus() + + local node_from, node_to = self.mark:pos_begin_end_raw() + local ok = pcall( + vim.api.nvim_buf_set_text, + 0, + node_from[1], + node_from[2], + node_to[1], + node_to[2], + text + ) + -- we can assume that (part of) the snippet was deleted; remove it from + -- the jumplist. + if not ok then + error("[LuaSnip Failed]: " .. vim.inspect(text)) + end +end + +-- since parents validate the adjacency, nodes where we don't know anything +-- about the text inside them just have to assume they haven't been deleted :D +function Node:extmarks_valid() + return true +end + +function Node:linkable() + -- linkable if insert or exitNode. + return vim.tbl_contains( + { types.insertNode, types.exitNode }, + rawget(self, "type") + ) +end +function Node:interactive() + -- interactive if immediately inside choiceNode. + return vim.tbl_contains( + { types.insertNode, types.exitNode }, + rawget(self, "type") + ) or rawget(self, "choice") ~= nil +end +function Node:leaf() + return vim.tbl_contains( + { types.textNode, types.functionNode, types.insertNode, types.exitNode }, + rawget(self, "type") + ) +end + +return { + Node = Node, + focus_node = focus_node, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/restoreNode.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/restoreNode.lua new file mode 100644 index 00000000..48c8448a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/restoreNode.lua @@ -0,0 +1,307 @@ +-- restoreNode is implemented similarly to dynamicNode, only that it gets the snippetNode not from some function, but from self.snip.stored[key]. + +local Node = require("luasnip.nodes.node").Node +local wrap_nodes_in_snippetNode = + require("luasnip.nodes.snippet").wrap_nodes_in_snippetNode +local RestoreNode = Node:new() +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local mark = require("luasnip.util.mark").mark +local extend_decorator = require("luasnip.util.extend_decorator") + +local function R(pos, key, nodes, opts) + -- don't create nested snippetNodes, unnecessary. + nodes = nodes and wrap_nodes_in_snippetNode(nodes) + + return RestoreNode:new({ + pos = pos, + key = key, + mark = nil, + snip = nodes, + type = types.restoreNode, + dependents = {}, + -- TODO: find out why it's necessary only for this node. + active = false, + }, opts) +end +extend_decorator.register(R, { arg_indx = 4 }) + +function RestoreNode:exit() + if not self.visible then + -- already exited. + return + end + + self.visible = false + self.mark:clear() + -- snip should exist if exit is called. + self.snip:store() + -- will be copied on restore, no need to copy here too. + self.parent.snippet.stored[self.key] = self.snip + self.snip:exit() + self.snip = nil + self.active = false +end + +function RestoreNode:input_enter(_, dry_run) + if dry_run then + dry_run.active[self] = true + return + end + + self.active = true + self.visited = true + self.mark:update_opts(self.ext_opts.active) + + self:event(events.enter) +end + +function RestoreNode:input_leave(_, dry_run) + if dry_run then + dry_run.active[self] = false + return + end + + self:event(events.leave) + + self:update_dependents() + self.active = false + + self.mark:update_opts(self:get_passive_ext_opts()) +end + +-- set snippetNode for this key here. +function RestoreNode:subsnip_init() + -- don't overwrite potentially stored snippetNode. + -- due to metatable, there will always be a node set, but only those set + -- by it (should) have the is_default set to true. + if self.parent.snippet.stored[self.key].is_default and self.snip then + self.parent.snippet.stored[self.key] = self.snip + end +end + +-- don't need these, will be done in put_initial and get_static/docstring. +function RestoreNode:indent(_) end + +function RestoreNode:expand_tabs(_) end + +-- will be called when before expansion but after snip.parent was initialized. +-- Get the actual snippetNode here. +function RestoreNode:put_initial(pos) + local tmp = self.parent.snippet.stored[self.key] + + -- act as if snip is directly inside parent. + tmp.parent = self.parent + tmp.indx = self.indx + + tmp.next = self + tmp.prev = self + + tmp.snippet = self.parent.snippet + + tmp.restore_node = self + tmp.update_dependents = function(node) + node:_update_dependents() + -- self is restoreNode. + node.restore_node:update_dependents() + end + + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() + tmp:subsnip_init() + + tmp:init_positions(self.snip_absolute_position) + tmp:init_insert_positions(self.snip_absolute_insert_position) + + tmp:make_args_absolute() + + tmp:set_dependents() + tmp:set_argnodes(self.parent.snippet.dependents_dict) + + if vim.bo.expandtab then + tmp:expand_tabs(util.tab_width(), self.parent.indentstring) + end + + -- correctly set extmark for node. + -- does not modify ext_opts[node.type]. + local mark_opts = vim.tbl_extend("keep", { + right_gravity = false, + end_right_gravity = false, + }, tmp:get_passive_ext_opts()) + + local old_pos = vim.deepcopy(pos) + tmp:put_initial(pos) + tmp.mark = mark(old_pos, pos, mark_opts) + + -- no need to call update here, will be done by function calling this + -- function. + + self.snip = tmp + self.visible = true +end + +-- the same as DynamicNode. +function RestoreNode:jump_into(dir, no_move, dry_run) + self:init_dry_run_active(dry_run) + + if self:is_active(dry_run) then + self:input_leave(no_move, dry_run) + + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + else + self:input_enter(no_move, dry_run) + + return self.snip:jump_into(dir, no_move, dry_run) + end +end + +function RestoreNode:set_ext_opts(name) + Node.set_ext_opts(self, name) + + self.snip:set_ext_opts(name) +end + +function RestoreNode:update() + self.snip:update() +end + +function RestoreNode:update_static() + -- *_static-methods can use the stored snippet, since they don't require + -- the snip to actually be inside the restoreNode. + self.parent.snippet.stored[self.key]:update_static() +end + +local function snip_init(self, snip) + snip.parent = self.parent + + snip.snippet = self.parent.snippet + -- pos should be nil if the restoreNode is inside a choiceNode. + snip.pos = rawget(self, "pos") + + snip:resolve_child_ext_opts() + snip:resolve_node_ext_opts() + snip:subsnip_init() + + snip:init_positions(self.snip_absolute_position) + snip:init_insert_positions(self.snip_absolute_insert_position) + + snip:make_args_absolute() + + snip:set_dependents() + snip:set_argnodes(self.parent.snippet.dependents_dict) + + snip:static_init() +end + +function RestoreNode:static_init() + Node.static_init(self) + self.snip = self.parent.snippet.stored[self.key] + snip_init(self, self.snip) +end + +function RestoreNode:get_static_text() + -- cache static_text, no need to recalculate function. + if not self.static_text then + self.static_text = + self.parent.snippet.stored[self.key]:get_static_text() + end + return self.static_text +end + +function RestoreNode:get_docstring() + if not self.docstring then + self.docstring = self.parent.snippet.stored[self.key]:get_docstring() + end + return self.docstring +end + +function RestoreNode:store() end + +-- will be restored through other means. +function RestoreNode:update_restore() + self.snip:update_restore() +end + +function RestoreNode:find_node(predicate) + if self.snip then + if predicate(self.snip) then + return self.snip + else + return self.snip:find_node(predicate) + end + end + return nil +end + +function RestoreNode:insert_to_node_absolute(position) + if #position == 0 then + return self.absolute_position + end + -- nil if not yet available. + return self.snip and self.snip:insert_to_node_absolute(position) +end + +function RestoreNode:update_all_dependents() + self:_update_dependents() + self.snip:update_all_dependents() +end + +function RestoreNode:update_all_dependents_static() + self:_update_dependents_static() + self.parent.snippet.stored[self.key]:_update_dependents_static() +end + +function RestoreNode:init_insert_positions(position_so_far) + Node.init_insert_positions(self, position_so_far) + self.snip_absolute_insert_position = + vim.deepcopy(self.absolute_insert_position) + -- nodes of current snippet should have a 0 before. + self.snip_absolute_insert_position[#self.snip_absolute_insert_position + 1] = + 0 +end + +function RestoreNode:init_positions(position_so_far) + Node.init_positions(self, position_so_far) + self.snip_absolute_position = vim.deepcopy(self.absolute_position) + -- Reach current snippet as snip_absolute_position..0. + self.snip_absolute_position[#self.snip_absolute_position + 1] = 0 +end + +function RestoreNode:resolve_position(position) + -- position must be 0, there are no other options. + return self.snip +end + +function RestoreNode:is_interactive() + -- shouldn't be called, but revisit this once is_interactive is used in + -- places other than lsp-snippets. + return true +end + +function RestoreNode:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + if self.snip then + self.snip:subtree_set_pos_rgrav(pos, direction, rgrav) + end +end + +function RestoreNode:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + if self.snip then + self.snip:subtree_set_rgrav(rgrav) + end +end + +function RestoreNode:extmarks_valid() + return node_util.generic_extmarks_valid(self, self.snip) +end + +return { + R = R, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/snippet.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/snippet.lua new file mode 100644 index 00000000..a0be2fb9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/snippet.lua @@ -0,0 +1,1572 @@ +local node_mod = require("luasnip.nodes.node") +local iNode = require("luasnip.nodes.insertNode") +local tNode = require("luasnip.nodes.textNode") +local util = require("luasnip.util.util") +local ext_util = require("luasnip.util.ext_opts") +local node_util = require("luasnip.nodes.util") +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local mark = require("luasnip.util.mark").mark +local Environ = require("luasnip.util.environ") +local session = require("luasnip.session") +local pattern_tokenizer = require("luasnip.util.pattern_tokenizer") +local dict = require("luasnip.util.dict") +local snippet_collection = require("luasnip.session.snippet_collection") +local extend_decorator = require("luasnip.util.extend_decorator") +local source = require("luasnip.session.snippet_collection.source") +local loader_util = require("luasnip.loaders.util") +local trig_engines = require("luasnip.nodes.util.trig_engines") + +local true_func = function() + return true +end + +local generate_resolve_expand_params_func = function(condition, user_resolve) + return function(self, line_to_cursor, match, captures) + if condition then + if not condition(line_to_cursor, match, captures) then + return nil + end + end + + local default_expand_params = { + trigger = match, + captures = captures, + } + + if user_resolve then + local res = user_resolve(self, line_to_cursor, match, captures) + if res == nil then + return nil + end + return vim.tbl_extend("force", default_expand_params, res) + else + return default_expand_params + end + end +end + +local callbacks_mt = { + __index = function(table, key) + rawset(table, key, {}) + return {} + end, +} + +-- declare SN here, is needed in metatable. +local SN + +local stored_mt = { + __index = function(table, key) + -- default-node is just empty text. + local val = SN(nil, { iNode.I(1) }) + val.is_default = true + rawset(table, key, val) + return val + end, +} + +local Snippet = node_mod.Node:new() + +local Parent_indexer = {} + +function Parent_indexer:new(o) + setmetatable(o, self) + self.__index = self + return o +end + +-- Returns referred node from parent (or parents' parent). +function Parent_indexer:resolve(snippet) + -- recurse if index is a parent_indexer + if getmetatable(self.indx) == Parent_indexer then + return self.indx:resolve(snippet.parent) + else + return snippet.parent.insert_nodes[self.indx] + end +end + +local function P(indx) + return Parent_indexer:new({ indx = indx }) +end + +function Snippet:init_nodes() + local insert_nodes = {} + for i, node in ipairs(self.nodes) do + node.parent = self + node.indx = i + if + node.type == types.insertNode + or node.type == types.exitNode + or node.type == types.snippetNode + or node.type == types.choiceNode + or node.type == types.dynamicNode + or node.type == types.restoreNode + then + if node.pos then + insert_nodes[node.pos] = node + end + end + + node.update_dependents = function(node) + node:_update_dependents() + node.parent:update_dependents() + end + end + + if insert_nodes[1] then + insert_nodes[1].prev = self + for i = 2, #insert_nodes do + insert_nodes[i].prev = insert_nodes[i - 1] + insert_nodes[i - 1].next = insert_nodes[i] + end + insert_nodes[#insert_nodes].next = self + + self.inner_first = insert_nodes[1] + self.inner_last = insert_nodes[#insert_nodes] + else + self.inner_first = self + self.inner_last = self + end + + self.insert_nodes = insert_nodes +end + +local function wrap_nodes_in_snippetNode(nodes) + if getmetatable(nodes) then + -- is a node, not a table. + if nodes.type ~= types.snippetNode then + -- is not a snippetNode. + + -- pos might have been nil, just set it correctly here. + nodes.pos = 1 + return SN(nil, { nodes }) + else + -- is a snippetNode, wrapping it twice is unnecessary. + return nodes + end + else + -- is a table of nodes. + return SN(nil, nodes) + end +end + +local function init_snippetNode_opts(opts) + local in_node = {} + + in_node.child_ext_opts = + ext_util.child_complete(vim.deepcopy(opts.child_ext_opts or {})) + + if opts.merge_child_ext_opts == nil then + in_node.merge_child_ext_opts = true + else + in_node.merge_child_ext_opts = opts.merge_child_ext_opts + end + + in_node.callbacks = opts.callbacks or {} + -- return empty table for non-specified callbacks. + setmetatable(in_node.callbacks, callbacks_mt) + + return in_node +end + +local function init_snippet_opts(opts) + local in_node = {} + + -- return sn(t("")) for so-far-undefined keys. + in_node.stored = setmetatable(opts.stored or {}, stored_mt) + + -- wrap non-snippetNode in snippetNode. + for key, nodes in pairs(in_node.stored) do + in_node.stored[key] = wrap_nodes_in_snippetNode(nodes) + end + + return vim.tbl_extend("error", in_node, init_snippetNode_opts(opts)) +end + +-- context, opts non-nil tables. +local function init_snippet_context(context, opts) + local effective_context = {} + + -- trig is set by user, trigger is used internally. + -- not worth a breaking change, we just make it compatible here. + effective_context.trigger = context.trig + + effective_context.name = context.name or context.trig + + -- context.{desc,dscr} could be nil, string or table. + -- (defaults to trigger) + effective_context.description = + util.to_line_table(context.desc or context.dscr or context.trig) + -- (keep dscr to avoid breaking downstream usages) + effective_context.dscr = effective_context.description + + -- might be nil, but whitelisted in snippetProxy. + effective_context.priority = context.priority + + -- might be nil, but whitelisted in snippetProxy. + -- shall be a string, allowed values: "snippet", "autosnippet" + -- stylua: ignore + assert( + context.snippetType == nil + or context.snippetType == "snippet" + or context.snippetType == "autosnippet", + "snippetType has to be either 'snippet' or 'autosnippet' (or unset)" + ) + -- switch to plural forms so that we can use this for indexing + -- stylua: ignore + effective_context.snippetType = + context.snippetType == "autosnippet" and "autosnippets" + or context.snippetType == "snippet" and "snippets" + or nil + + -- may be nil. + effective_context.filetype = context.filetype + + -- maybe do this in a better way when we have more parameters, but this is + -- fine for now: + + -- not a necessary argument. + if context.docstring ~= nil then + effective_context.docstring = util.to_line_table(context.docstring) + end + + -- can't use `cond and ... or ...` since we have truthy values. + effective_context.wordTrig = + util.ternary(context.wordTrig ~= nil, context.wordTrig, true) + effective_context.hidden = + util.ternary(context.hidden ~= nil, context.hidden, false) + + effective_context.regTrig = + util.ternary(context.regTrig ~= nil, context.regTrig, false) + + effective_context.docTrig = context.docTrig + local engine + if type(context.trigEngine) == "function" then + -- if trigEngine is function, just use that. + engine = context.trigEngine + else + -- otherwise, it is nil or string, if it is string, that is the name, + -- otherwise use "pattern" if regTrig is set, and finally fall back to + -- "plain" if it is not. + local engine_name = util.ternary( + context.trigEngine ~= nil, + context.trigEngine, + util.ternary(context.regTrig ~= nil, "pattern", "plain") + ) + engine = trig_engines[engine_name] + end + -- make sure to pass through nil-trigEngineOpts, they will be recognized and + -- we will get a default-version of that function instead of generating a + -- curried (?) version of it (which would waste space I think). + effective_context.trig_matcher = + engine(effective_context.trigger, context.trigEngineOpts) + + effective_context.resolveExpandParams = generate_resolve_expand_params_func( + context.condition or opts.condition, + context.resolveExpandParams + ) + effective_context.show_condition = context.show_condition + or opts.show_condition + or true_func + + -- init invalidated here. + -- This is because invalidated is a key that can be populated without any + -- information on the actual snippet (it can be used by snippetProxy!) and + -- it should be also available to the snippet-representations in the + -- snippet-list, and not in the expanded snippet, as doing this in + -- `init_snippet_opts` would suggest. + effective_context.invalidated = false + + return effective_context +end + +-- Create snippet without initializing opts+context. +-- this might be called from snippetProxy. +local function _S(snip, nodes, opts) + nodes = util.wrap_nodes(nodes) + -- tbl_extend creates a new table! Important with Proxy, metatable of snip + -- will be changed later. + snip = Snippet:new( + vim.tbl_extend("error", snip, { + nodes = nodes, + insert_nodes = {}, + current_insert = 0, + mark = nil, + dependents = {}, + active = false, + type = types.snippet, + -- dependents_dict is responsible for associating + -- function/dynamicNodes ("dependents") with their argnodes. + -- There are a few important requirements that have to be + -- fulfilled: + -- Allow associating present dependent with non-present argnode + -- (and vice-versa). + -- This is required, because a node outside some dynamicNode + -- could depend on a node inside it, and since the content of a + -- dynamicNode changes, it is possible that the argnode will be + -- generated. + -- As soon as that happens, it should be possible to immediately + -- find the dependents that depend on the newly-generated argnode, + -- without searching the snippet. + -- + -- The dependents_dict enables all of this by storing every node + -- which is addressable by either `absolute_indexer` or + -- `key_indexer` (or even directly, just with its own + -- table, ie. `self`) under its path. + -- * `absolute_indexer`: the path is the sequence of jump_indices + -- which leads to this node, for example {1,3,1}. + -- * `key_indexer`: the path is {"key", }. + -- * `node`: the path is {node}. + -- With each type of node-reference (absolute_indexer, key, node), + -- the node which is referenced by it, is stored under path .. + -- {"node"} (if it exists inside the current snippet!!), while the + -- dependents are stored at path .. {"dependents"}. + -- The manner in which the dependents are stored is also + -- interesting: + -- They are not stored in eg a list, since we would then have to + -- deal with explicitly invalidating them (remove them from the + -- list to prevent its growing too large). No, the dependents are + -- stored under their own absolute position (not absolute _insert_ + -- position, functionNodes don't have a jump-index, and thus can't + -- be addressed using absolute insert position), which means that + -- + -- a) once a dependent is re-generated, for example by a + -- dynamicNode, it will not take up new space, but simply overwrite + -- the old one (which is very desirable!!) + -- b) we will still store some older, unnecessary dependents + -- + -- (imo) a outweighs b, which is why this design was chosen. + -- (non-visible nodes are ignored by tracking their visibility in + -- the snippet separately, it is then queried in eg. + -- `update_dependents`) + -- + -- Related functions: + -- * `dependent:set_dependents` to insert argnode+dependent in + -- `dependents_dict`, in the according to the above description. + -- * `set_argnodes` to insert the absolute_insert_position .. + -- {"node"} into dependents_dict. + -- * `get_args` to get the text of the argnodes to some dependent + -- node. + -- * `update_dependents` can be called to find all dependents, and + -- update the visible ones. + dependents_dict = dict.new(), + + -- list of snippets expanded within the region of this snippet. + -- sorted by their buffer-position, for quick searching. + child_snippets = {}, + }), + opts + ) + + -- is propagated to all subsnippets, used to quickly find the outer snippet + snip.snippet = snip + + -- if the snippet is expanded inside another snippet (can be recognized by + -- non-nil parent_node), the node of the snippet this one is inside has to + -- update its dependents. + function snip:_update_dependents() + if self.parent_node then + self.parent_node:update_dependents() + end + end + snip.update_dependents = snip._update_dependents + + snip:init_nodes() + + if not snip.insert_nodes[0] then + -- Generate implied i(0) + local i0 = iNode.I(0) + local i0_indx = #nodes + 1 + i0.parent = snip + i0.indx = i0_indx + snip.insert_nodes[0] = i0 + snip.nodes[i0_indx] = i0 + end + + return snip +end + +local function S(context, nodes, opts) + opts = opts or {} + + local snip = init_snippet_context(node_util.wrap_context(context), opts) + snip = vim.tbl_extend("error", snip, init_snippet_opts(opts)) + + snip = _S(snip, nodes, opts) + + if __luasnip_get_loaded_file_frame_debuginfo ~= nil then + -- this snippet is being lua-loaded, and the source should be recorded. + snip._source = + source.from_debuginfo(__luasnip_get_loaded_file_frame_debuginfo()) + end + + return snip +end +extend_decorator.register( + S, + { arg_indx = 1, extend = node_util.snippet_extend_context }, + { arg_indx = 3 } +) + +function SN(pos, nodes, opts) + opts = opts or {} + + local snip = Snippet:new( + vim.tbl_extend("error", { + pos = pos, + nodes = util.wrap_nodes(nodes), + insert_nodes = {}, + current_insert = 0, + mark = nil, + dependents = {}, + active = false, + type = types.snippetNode, + }, init_snippetNode_opts(opts)), + opts + ) + snip:init_nodes() + + return snip +end +extend_decorator.register(SN, { arg_indx = 3 }) + +local function ISN(pos, nodes, indent_text, opts) + local snip = SN(pos, nodes, opts) + + local function get_indent(parent_indent) + local indentstring = "" + if vim.bo.expandtab then + -- preserve content of $PARENT_INDENT, but expand tabs before/after it + for str in vim.gsplit(indent_text, "$PARENT_INDENT", true) do + -- append expanded text and parent_indent, we'll remove the superfluous one after the loop. + indentstring = indentstring + .. util.expand_tabs( + { str }, + util.tab_width(), + #indentstring + #parent_indent + )[1] + .. parent_indent + end + indentstring = indentstring:sub(1, -#parent_indent - 1) + else + indentstring = indent_text:gsub("$PARENT_INDENT", parent_indent) + end + + return indentstring + end + + function snip:indent(parent_indent) + Snippet.indent(self, get_indent(parent_indent)) + end + + -- expand_tabs also needs to be modified: the children of the isn get the + -- indent of the isn, so we'll have to calculate it now. + -- This is done with a dummy-indentstring of the correct length. + function snip:expand_tabs(tabwidth, indentstrlen) + Snippet.expand_tabs( + self, + tabwidth, + #get_indent(string.rep(" ", indentstrlen)) + ) + end + + return snip +end +extend_decorator.register(ISN, { arg_indx = 4 }) + +function Snippet:remove_from_jumplist() + if not self.visible then + -- snippet not visible => already removed. + -- Don't remove it twice. + return + end + + -- prev is i(-1)(startNode), prev of that is the outer/previous snippet. + -- pre is $0 or insertNode. + local pre = self.prev.prev + -- similar for next, self.next is the i(0). + -- nxt is snippet. + local nxt = self.next.next + + self:exit() + + local sibling_list = self.parent_node ~= nil + and self.parent_node.parent.snippet.child_snippets + or session.snippet_roots[vim.api.nvim_get_current_buf()] + local self_indx + for i, snip in ipairs(sibling_list) do + if snip == self then + self_indx = i + end + end + table.remove(sibling_list, self_indx) + + -- previous snippet jumps to this one => redirect to jump to next one. + if pre then + if pre.inner_first == self then + if pre == nxt then + pre.inner_first = nil + else + pre.inner_first = nxt + end + elseif pre.next == self then + pre.next = nxt + end + end + if nxt then + if nxt.inner_last == self.next then + if pre == nxt then + nxt.inner_last = nil + else + nxt.inner_last = pre + end + -- careful here!! nxt.prev is its start_node, nxt.prev.prev is this + -- snippet. + elseif nxt.prev.prev == self.next then + nxt.prev.prev = pre + end + end +end + +local function insert_into_jumplist( + snippet, + start_node, + current_node, + parent_node, + sibling_snippets, + own_indx +) + local prev_snippet = sibling_snippets[own_indx - 1] + -- have not yet inserted self!! + local next_snippet = sibling_snippets[own_indx] + + -- only consider sibling-snippets with the same parent-node as + -- previous/next snippet for linking-purposes. + -- They are siblings because they are expanded in the same snippet, not + -- because they have the same parent_node. + local prev, next + if prev_snippet ~= nil and prev_snippet.parent_node == parent_node then + prev = prev_snippet + end + if next_snippet ~= nil and next_snippet.parent_node == parent_node then + next = next_snippet + end + + -- whether roots should be linked together. + local link_roots = session.config.link_roots + + -- whether children of the same snippet should be linked to their parent + -- and eachother. + local link_children = session.config.link_children + + if parent_node then + if node_util.linkable_node(parent_node) then + -- snippetNode (which has to be empty to be viable here) and + -- insertNode can both deal with inserting a snippet inside them + -- (ie. hooking it up st. it can be visited after jumping back to + -- the snippet of parent). + -- in all cases + if link_children and prev ~= nil then + -- if we have a previous snippet we can link to, just do that. + prev.next.next = snippet + start_node.prev = prev.insert_nodes[0] + else + -- only jump from parent to child if link_children is set. + if link_children then + -- prev is nil, but we can link up using the parent. + parent_node.inner_first = snippet + end + -- make sure we can jump back to the parent. + start_node.prev = parent_node + end + + -- exact same reasoning here as in prev-case above, omitting comments. + if link_children and next ~= nil then + -- jump from next snippets start_node to $0. + next.prev.prev = snippet.insert_nodes[0] + -- jump from $0 to next snippet (skip its start_node) + snippet.insert_nodes[0].next = next + else + if link_children then + parent_node.inner_last = snippet.insert_nodes[0] + end + snippet.insert_nodes[0].next = parent_node + end + else + -- naively, even if the parent is linkable, there might be snippets + -- before/after that share the same parent, so we could + -- theoretically link up with them. + -- This, however, can cause cyclic jumps, for example if the + -- previous child-snippet contains the current node: we will jump + -- from the end of the new snippet into the previous child-snippet, + -- and from its last node into the new snippet. + -- Since cycles should be avoided (very weird if the jumps just go + -- in a circle), we have no choice but to fall back to this + -- old-style linkage. + + -- Don't jump from current_node to this snippet (I feel + -- like that should be good: one can still get back to ones + -- previous history, and we don't mess up whatever jumps + -- are set up around current_node) + start_node.prev = current_node + snippet.insert_nodes[0].next = current_node + end + -- don't link different root-nodes for unlinked_roots. + elseif link_roots then + -- inserted into top-level snippet-forest, just hook up with prev, next. + -- prev and next have to be snippets or nil, in this case. + if prev ~= nil then + prev.next.next = snippet + start_node.prev = prev.insert_nodes[0] + end + if next ~= nil then + snippet.insert_nodes[0].next = next + next.prev.prev = snippet.insert_nodes[0] + end + end + + table.insert(sibling_snippets, own_indx, snippet) +end + +function Snippet:trigger_expand(current_node, pos_id, env, indent_nodes) + local pos = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, pos_id, {}) + + -- find tree-node the snippet should be inserted at (could be before another node). + local _, sibling_snippets, own_indx, parent_node = + node_util.snippettree_find_undamaged_node(pos, { + tree_respect_rgravs = false, + tree_preference = node_util.binarysearch_preference.outside, + snippet_mode = "linkable", + }) + local n_siblings_pre = #sibling_snippets + + if current_node then + if parent_node then + if node_util.linkable_node(parent_node) then + node_util.refocus(current_node, parent_node) + parent_node:input_enter_children() + else + -- enter extmarks of parent_node, but don't enter it + -- "logically", it will not be the parent of the snippet. + parent_node:focus() + -- enter current node, it will contain the new snippet. + current_node:input_enter_children() + end + else + -- if no parent_node, completely leave. + node_util.refocus(current_node, nil) + + -- in this branch, it may happen that the snippet we leave is + -- invalid and removed from the snippet-list during `refocus`. + -- This is not catastrophic, but we have to recognize it here, and + -- update the `own_indx` among the snippet-roots (one was deleted, + -- the computed index is no longer valid since there may have been + -- a shift down over `own_indx`) + if n_siblings_pre ~= #sibling_snippets then + -- only own_indx can change, since the text in the buffer is + -- unchanged, while the number of roots is. + _, _, own_indx, _ = + node_util.snippettree_find_undamaged_node(pos, { + tree_respect_rgravs = false, + tree_preference = node_util.binarysearch_preference.outside, + snippet_mode = "linkable", + }) + end + end + + -- There may be other snippets inside of this parent_node/on this level + -- of the snippet-tree whose extmarks have to be adjusted s.t. they + -- don't contain the text that will be inserted during put_initial. + -- Node's/snippet's extmarks that are outside of this parent_node/not + -- siblings of this node will be adjusted during the refocus above, if + -- applicable. + -- + -- The following adjustments may do too much, but there's no issue + -- with that, and we're on the safe side. + + -- set rgrav false for snippets/nodes where the right boundary + -- coincides with the position we insert at now... + for i = 1, own_indx - 1 do + sibling_snippets[i]:subtree_set_pos_rgrav(pos, -1, false) + end + -- set rgrav true for snippets/nodes where the left boundary + -- coincides with the position we insert at now... + for i = own_indx, #sibling_snippets do + sibling_snippets[i]:subtree_set_pos_rgrav(pos, 1, true) + end + end + + local pre_expand_res = self:event(events.pre_expand, { expand_pos = pos }) + or {} + -- update pos, event-callback might have moved the extmark. + pos = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, pos_id, {}) + + Environ:override(env, pre_expand_res.env_override or {}) + + if indent_nodes then + local indentstring = util.line_chars_before(pos):match("^%s*") + -- expand tabs before indenting to keep indentstring unmodified + if vim.bo.expandtab then + self:expand_tabs(util.tab_width(), #indentstring) + end + self:indent(indentstring) + end + + -- (possibly) keep user-set opts. + if self.merge_child_ext_opts then + self.effective_child_ext_opts = ext_util.child_extend( + vim.deepcopy(self.child_ext_opts), + session.config.ext_opts + ) + else + self.effective_child_ext_opts = vim.deepcopy(self.child_ext_opts) + end + + local parent_ext_base_prio + -- if inside another snippet, increase priority accordingly. + -- parent_node is only set if this snippet is expanded inside another one. + if parent_node then + parent_ext_base_prio = parent_node.parent.ext_opts.base_prio + else + parent_ext_base_prio = session.config.ext_base_prio + end + + -- own highlight comes from self.child_ext_opts.snippet. + self:resolve_node_ext_opts( + parent_ext_base_prio, + self.effective_child_ext_opts[self.type] + ) + + self.env = env + self:subsnip_init() + + self:init_positions({}) + self:init_insert_positions({}) + + self:make_args_absolute() + + self:set_dependents() + self:set_argnodes(self.dependents_dict) + + -- at this point `stored` contains the snippetNodes that will actually + -- be used, indent them once here. + for _, node in pairs(self.stored) do + node:indent(self.indentstr) + end + + local start_node = iNode.I(0) + + local old_pos = vim.deepcopy(pos) + self:put_initial(pos) + + local mark_opts = vim.tbl_extend("keep", { + right_gravity = false, + end_right_gravity = false, + }, self:get_passive_ext_opts()) + self.mark = mark(old_pos, pos, mark_opts) + + self:update() + self:update_all_dependents() + + -- Marks should stay at the beginning of the snippet, only the first mark is needed. + start_node.mark = self.nodes[1].mark + start_node.pos = -1 + -- needed for querying node-path from snippet to this node. + start_node.absolute_position = { -1 } + start_node.parent = self + + -- hook up i0 and start_node, and then the snippet itself. + -- they are outside, not inside the snippet. + -- This should clearly be the case for start_node, but also for $0 since + -- jumping to $0 should make/mark the snippet non-active (for example via + -- extmarks) + start_node.next = self + self.prev = start_node + self.insert_nodes[0].prev = self + self.next = self.insert_nodes[0] + + -- parent_node is nil if the snippet is toplevel. + self.parent_node = parent_node + + insert_into_jumplist( + self, + start_node, + current_node, + parent_node, + sibling_snippets, + own_indx + ) + + return parent_node +end + +-- returns copy of snip if it matches, nil if not. +function Snippet:matches(line_to_cursor) + local match, captures = self.trig_matcher(line_to_cursor, self.trigger) + + -- Trigger or regex didn't match. + if not match then + return nil + end + + local expand_params = + self:resolveExpandParams(line_to_cursor, match, captures) + if not expand_params then + return nil + end + + local from = #line_to_cursor - #match + 1 + + -- if wordTrig is set, the char before the trigger can't be \w or the + -- word has to start at the beginning of the line. + if + self.wordTrig + and not ( + from == 1 + or string.match( + string.sub(line_to_cursor, from - 1, from - 1), + "[%w_]" + ) + == nil + ) + then + return nil + end + + return expand_params +end + +-- https://gist.github.com/tylerneylon/81333721109155b2d244 +local function copy3(obj, seen) + -- Handle non-tables and previously-seen tables. + if type(obj) ~= "table" then + return obj + end + if seen and seen[obj] then + return seen[obj] + end + + -- New table; mark it as seen an copy recursively. + local s = seen or {} + local res = {} + s[obj] = res + for k, v in next, obj do + res[copy3(k, s)] = copy3(v, s) + end + return setmetatable(res, getmetatable(obj)) +end + +function Snippet:copy() + return copy3(self) +end + +function Snippet:del_marks() + for _, node in ipairs(self.nodes) do + vim.api.nvim_buf_del_extmark(0, session.ns_id, node.mark.id) + end +end + +function Snippet:is_interactive(info) + for _, node in ipairs(self.nodes) do + -- return true if any node depends on another node or is an insertNode. + if node:is_interactive(info) then + return true + end + end + return false +end + +function Snippet:dump() + for i, node in ipairs(self.nodes) do + print(i) + print(node.mark.opts.right_gravity, node.mark.opts.end_right_gravity) + local from, to = node.mark:pos_begin_end() + print(from[1], from[2]) + print(to[1], to[2]) + end +end + +function Snippet:put_initial(pos) + for _, node in ipairs(self.nodes) do + -- save pos to compare to later. + local old_pos = vim.deepcopy(pos) + node:put_initial(pos) + + -- correctly set extmark for node. + -- does not modify ext_opts[node.type]. + local mark_opts = vim.tbl_extend("keep", { + right_gravity = false, + end_right_gravity = false, + }, node:get_passive_ext_opts()) + node.mark = mark(old_pos, pos, mark_opts) + end + self.visible = true +end + +-- populate env,inden,captures,trigger(regex),... but don't put any text. +-- the env may be passed in opts via opts.env, if none is passed a new one is +-- generated. +function Snippet:fake_expand(opts) + if not opts then + opts = {} + end + -- set eg. env.TM_SELECTED_TEXT to $TM_SELECTED_TEXT + if opts.env then + self.env = opts.env + else + self.env = Environ.fake() + end + + self.captures = {} + setmetatable(self.captures, { + __index = function(_, key) + return "$CAPTURE" .. tostring(key) + end, + }) + if self.docTrig then + -- use docTrig as entire line up to cursor, this assumes that it + -- actually matches the trigger. + local _ + _, self.captures = self.trig_matcher(self.docTrig, self.trigger) + self.trigger = self.docTrig + else + self.trigger = "$TRIGGER" + end + self.ext_opts = vim.deepcopy(session.config.ext_opts) + + self:indent("") + + -- ext_opts don't matter here, just use convenient values. + self.effective_child_ext_opts = self.child_ext_opts + self.ext_opts = self.node_ext_opts + + self:subsnip_init() + + self:init_positions({}) + self:init_insert_positions({}) + + self:make_args_absolute() + + self:set_dependents() + self:set_argnodes(self.dependents_dict) + + self:static_init() + + -- no need for update_dependents_static, update_static alone will cause updates for all child-nodes. + self:update_static() +end + +-- to work correctly, this may require that the snippets' env,indent,captures? are +-- set. +function Snippet:get_static_text() + if self.static_text then + return self.static_text + -- copy+fake_expand the snippet here instead of in whatever code needs to know the docstring. + elseif not self.ext_opts then + -- not a snippetNode and not yet initialized + local snipcop = self:copy() + -- sets env, captures, etc. + snipcop:fake_expand() + local static_text = snipcop:get_static_text() + self.static_text = static_text + return static_text + end + + if not self.static_visible then + return nil + end + local text = { "" } + for _, node in ipairs(self.nodes) do + local node_text = node:get_static_text() + -- append first line to last line of text so far. + text[#text] = text[#text] .. node_text[1] + for i = 2, #node_text do + text[#text + 1] = node_text[i] + end + end + -- cache computed text, may be called multiple times for + -- function/dynamicNodes. + self.static_text = text + return text +end + +function Snippet:get_docstring() + if self.docstring then + return self.docstring + -- copy+fake_expand the snippet here instead of in whatever code needs to know the docstring. + elseif not self.ext_opts then + -- not a snippetNode and not yet initialized + local snipcop = self:copy() + -- sets env, captures, etc. + snipcop:fake_expand() + local docstring = snipcop:get_docstring() + self.docstring = docstring + return docstring + end + local docstring = { "" } + for _, node in ipairs(self.nodes) do + local node_text = node:get_docstring() + -- append first line to last line of text so far. + docstring[#docstring] = docstring[#docstring] .. node_text[1] + for i = 2, #node_text do + docstring[#docstring + 1] = node_text[i] + end + end + -- cache computed text, may be called multiple times for + -- function/dynamicNodes. + -- if not outer snippet, wrap it in ${}. + self.docstring = self.type == types.snippet and docstring + or util.string_wrap(docstring, rawget(self, "pos")) + return self.docstring +end + +function Snippet:update() + for _, node in ipairs(self.nodes) do + node:update() + end +end + +function Snippet:update_static() + for _, node in ipairs(self.nodes) do + node:update_static() + end +end + +function Snippet:update_restore() + for _, node in ipairs(self.nodes) do + node:update_restore() + end +end + +function Snippet:store() + for _, node in ipairs(self.nodes) do + node:store() + end +end + +function Snippet:indent(prefix) + self.indentstr = prefix + for _, node in ipairs(self.nodes) do + node:indent(prefix) + end +end + +function Snippet:expand_tabs(tabwidth, indenstringlen) + for _, node in ipairs(self.nodes) do + node:expand_tabs(tabwidth, indenstringlen) + end +end + +function Snippet:subsnip_init() + node_util.subsnip_init_children(self, self.nodes) +end + +Snippet.init_positions = node_util.init_child_positions_func( + "absolute_position", + "nodes", + "init_positions" +) +Snippet.init_insert_positions = node_util.init_child_positions_func( + "absolute_insert_position", + "insert_nodes", + "init_insert_positions" +) + +function Snippet:make_args_absolute() + for _, node in ipairs(self.nodes) do + node:make_args_absolute(self.absolute_insert_position) + end +end + +function Snippet:input_enter(_, dry_run) + if dry_run then + dry_run.active[self] = true + return + end + + self.visited = true + self.active = true + + if self.type == types.snippet then + -- set snippet-passive -> visited/unvisited for all children. + self:set_ext_opts("passive") + end + self.mark:update_opts(self.ext_opts.active) + + self:event(events.enter) +end + +function Snippet:input_leave(_, dry_run) + if dry_run then + dry_run.active[self] = false + return + end + + self:event(events.leave) + self:update_dependents() + + -- set own ext_opts to snippet-passive, there is no passive for snippets. + self.mark:update_opts(self.ext_opts.snippet_passive) + if self.type == types.snippet then + -- also override all nodes' ext_opt. + self:set_ext_opts("snippet_passive") + end + + self.active = false +end + +function Snippet:set_ext_opts(opt_name) + for _, node in ipairs(self.nodes) do + node:set_ext_opts(opt_name) + end +end + +function Snippet:jump_into(dir, no_move, dry_run) + self:init_dry_run_active(dry_run) + + -- if dry_run, ignore self.active + if self:is_active(dry_run) then + self:input_leave(no_move, dry_run) + + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + else + self:input_enter(no_move, dry_run) + + if dir == 1 then + return self.inner_first:jump_into(dir, no_move, dry_run) + else + return self.inner_last:jump_into(dir, no_move, dry_run) + end + end +end + +-- Snippets inherit Node:jump_from, it shouldn't occur normally, but may be +-- used in LSP-Placeholders. + +function Snippet:exit() + if self.type == types.snippet then + -- if exit is called, this will not be visited again. + -- Thus, also clean up the child-snippets, which will also not be + -- visited again, since they can only be visited through self. + for _, child in ipairs(self.child_snippets) do + child:exit() + end + end + + self.visible = false + for _, node in ipairs(self.nodes) do + node:exit() + end + self.mark:clear() + self.active = false +end + +function Snippet:text_only() + for _, node in ipairs(self.nodes) do + if node.type ~= types.textNode then + return false + end + end + return true +end + +function Snippet:event(event, event_args) + -- there are 3 sources of a callback, for a snippetNode: + -- self.callbacks[-1], self.node_callbacks, and parent.callbacks[self.pos]. + local m1_cb, cb, parent_cb + -- since we handle pre-expand callbacks here, we need to handle the + -- event_res, which may be returned by more than one callback. + -- In order to keep it simple, we just return any non-nil result. + local m1_cb_res, cb_res, parent_cb_res + + m1_cb = self.callbacks[-1][event] + if m1_cb then + m1_cb_res = m1_cb(self, event_args) + end + + cb = self.node_callbacks[event] + if cb then + cb_res = cb(self, event_args) + end + + if self.type == types.snippetNode and self.pos then + -- if snippetNode, also do callback for position in parent. + parent_cb = self.parent.callbacks[self.pos][event] + if parent_cb then + parent_cb_res = parent_cb(self) + end + end + + session.event_node = self + session.event_args = event_args + vim.api.nvim_exec_autocmds("User", { + pattern = "Luasnip" .. events.to_string(self.type, event), + modeline = false, + }) + + return vim.F.if_nil(cb_res, m1_cb_res, parent_cb_res) +end + +local function nodes_from_pattern(pattern) + local nodes = {} + local text_active = true + local iNode_indx = 1 + local tokens = pattern_tokenizer.tokenize(pattern) + for _, text in ipairs(tokens) do + if text_active then + nodes[#nodes + 1] = tNode.T(text) + else + nodes[#nodes + 1] = iNode.I(iNode_indx, text) + iNode_indx = iNode_indx + 1 + end + text_active = not text_active + end + -- This is done so the user ends up at the end of the snippet either way + -- and may use their regular expand-key to expand the snippet. + -- Autoexpanding doesn't quite work, if the snippet ends with an + -- interactive part and the user overrides whatever is put in there, the + -- jump to the i(0) may trigger an expansion, and the helper-snippet could + -- not easily be removed, as the snippet the user wants to actually use is + -- inside of it. + -- Because of that it is easier to let the user do the actual expanding, + -- but help them on the way to it (by providing an easy way to override the + -- "interactive" parts of the pattern-trigger). + -- + -- if even number of nodes, the last is an insertNode (nodes begins with + -- textNode and alternates between the two). + if #nodes % 2 == 0 then + nodes[#nodes] = iNode.I(0, tokens[#tokens]) + else + nodes[#nodes + 1] = iNode.I(0) + end + return nodes +end + +-- only call on actual snippets, snippetNodes don't have trigger. +function Snippet:get_pattern_expand_helper() + if not self.expand_helper_snippet then + local nodes = nodes_from_pattern(self.trigger) + self.expand_helper_snippet = S(self.trigger, nodes, { + callbacks = { + [0] = { + [events.enter] = function(_) + vim.schedule(function() + -- Remove this helper snippet as soon as the i(0) + -- is reached. + require("luasnip").unlink_current() + end) + end, + }, + }, + }) + end + -- will be copied in actual expand. + return self.expand_helper_snippet +end + +function Snippet:find_node(predicate) + for _, node in ipairs(self.nodes) do + if predicate(node) then + return node + else + local node_in_child = node:find_node(predicate) + if node_in_child then + return node_in_child + end + end + end + return nil +end + +function Snippet:insert_to_node_absolute(position) + if #position == 0 then + return self.absolute_position + end + local insert_indx = util.pop_front(position) + return self.insert_nodes[insert_indx]:insert_to_node_absolute(position) +end + +function Snippet:set_dependents() + for _, node in ipairs(self.nodes) do + node:set_dependents() + end +end + +function Snippet:set_argnodes(dict) + node_mod.Node.set_argnodes(self, dict) + for _, node in ipairs(self.nodes) do + node:set_argnodes(dict) + end +end + +function Snippet:update_all_dependents() + -- call the version that only updates this node. + self:_update_dependents() + -- only for insertnodes, others will not have dependents. + for _, node in ipairs(self.insert_nodes) do + node:update_all_dependents() + end +end +function Snippet:update_all_dependents_static() + -- call the version that only updates this node. + self:_update_dependents_static() + -- only for insertnodes, others will not have dependents. + for _, node in ipairs(self.insert_nodes) do + node:update_all_dependents_static() + end +end + +function Snippet:resolve_position(position) + -- only snippets have -1-node. + if position == -1 and self.type == types.snippet then + return self.prev + end + + return self.nodes[position] +end + +function Snippet:static_init() + node_mod.Node.static_init(self) + for _, node in ipairs(self.nodes) do + node:static_init() + end +end + +-- called only for snippetNodes! +function Snippet:resolve_child_ext_opts() + if self.merge_child_ext_opts then + self.effective_child_ext_opts = ext_util.child_extend( + vim.deepcopy(self.child_ext_opts), + self.parent.effective_child_ext_opts + ) + else + self.effective_child_ext_opts = vim.deepcopy(self.child_ext_opts) + end +end + +local function no_match() + return nil +end + +function Snippet:invalidate() + self.hidden = true + -- override matching-function. + self.matches = no_match + self.invalidated = true + snippet_collection.invalidated_count = snippet_collection.invalidated_count + + 1 +end + +-- used in add_snippets to get variants of snippet. +function Snippet:retrieve_all() + return { self } +end + +function Snippet:get_keyed_node(key) + -- get key-node from dependents_dict. + return self.dependents_dict:get({ "key", key, "node" }) +end + +-- adjust rgrav of nodes left (direction=-1) or right (direction=1) of node at +-- child_indx. +-- (direction is the direction into which is searched, from child_indx outward) +-- assumption: direction-endpoint of node is on child_endpoint. (caller +-- responsible) +function Snippet:set_sibling_rgravs(node, child_endpoint, direction, rgrav) + node_util.nodelist_adjust_rgravs( + self.nodes, + node.absolute_position[#node.absolute_position] + direction, + child_endpoint, + direction, + rgrav, + true + ) +end + +-- called only if the "-direction"-endpoint has to be changed, but the +-- "direction"-endpoint not. +function Snippet:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + + local child_from_indx + if direction == 1 then + child_from_indx = 1 + else + child_from_indx = #self.nodes + end + + node_util.nodelist_adjust_rgravs( + self.nodes, + child_from_indx, + pos, + direction, + rgrav, + true + ) +end +-- changes rgrav of all nodes and all endpoints in this snippetNode to `rgrav`. +function Snippet:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + for _, node in ipairs(self.nodes) do + node:subtree_set_rgrav(rgrav) + end +end + +-- for this to always return a node if pos is withing the snippet-boundaries, +-- the snippet must have valid extmarks. +-- Looks for a node that has a specific property (either linkable, or +-- interactive), which can be indicated by setting mode to either of the two +-- (as string). +function Snippet:node_at(pos, mode) + if #self.nodes == 0 then + -- special case: no children (can naturally occur with dynamicNode, + -- when its function could not be evaluated, or if a user passed an empty snippetNode). + return self + end + + -- collect nodes where pos is either in gravity-adjusted boundaries, .. + local gravity_matches = {} + -- .. or just inside the regular boundaries. + -- Both are needed, so we can fall back to matches if there is no gravity_match + -- with the desired mode ("linkable" or "interactive"), fall back to + -- extmark_matches if there is also no regular match with the desired mode, + -- and finally fall back to any match (still preferring extmark-match) if + -- there is no match with the desired mode at all. + -- Unfortunately, all this is necessary, since there are many cases where + -- we may return no linkable node, despite there apparently being one in + -- reach of the cursor. + local matches = {} + -- find_node visits all nodes in-order until the predicate returns true. + self:find_node(function(node) + if not node:leaf() then + -- not a leaf-node. + return false + end + + local node_mark = node.mark + local node_from, node_to = node_mark:pos_begin_end_raw() + -- if pos certainly beyond node, quickly continue. + -- This means a little more work for the nodes in range of pos, while + -- all nodes well before it are quickly skipped => should benefit + -- all cases where the runtime of this is noticeable, and which are not + -- unrealistic (lots of zero-width nodes). + if util.pos_cmp(pos, { node_to[1], node_to[2] + 1 }) > 0 then + return false + end + + -- generate gravity-adjusted endpoints. + local grav_adjusted_from = { node_from[1], node_from[2] } + local grav_adjusted_to = { node_to[1], node_to[2] } + if node_mark:get_rgrav(-1) then + grav_adjusted_from[2] = grav_adjusted_from[2] + 1 + end + if node_mark:get_rgrav(1) then + grav_adjusted_to[2] = grav_adjusted_to[2] + 1 + end + + local cmp_pos_to = util.pos_cmp(pos, node_to) + local cmp_pos_from = util.pos_cmp(pos, node_from) + local cmp_grav_from = util.pos_cmp(pos, grav_adjusted_from) + local cmp_grav_to = util.pos_cmp(pos, grav_adjusted_to) + + if cmp_pos_from < 0 then + -- abort once the first node is definitely beyond pos. + -- (extmark-gravity can't move column to the left). + return true + end + + -- pos between from,to <=> from <= pos < to is used when choosing which + -- extmark to insert text into, so we should adopt it here. + if cmp_grav_from >= 0 and cmp_grav_to < 0 then + table.insert(gravity_matches, node) + end + -- matches does not have to respect the extmark-conventions, just catch + -- all possible nodes. + if cmp_pos_from >= 0 and cmp_pos_to <= 0 then + table.insert(matches, node) + end + end) + + -- instead of stupid nesting ifs, and because we can't use goto since + -- non-luajit-users should also be able to run luasnip :((( + return (function() + for _, node in ipairs(gravity_matches) do + if node[mode](node) then + return node + end + end + for _, node in ipairs(matches) do + if node[mode](node) then + return node + end + end + -- no interactive node found, fall back to any match. + return gravity_matches[1] or matches[1] + end)() +end + +-- return the node the snippet jumps to, or nil if there isn't one. +function Snippet:next_node() + -- self.next is $0, .next is either the surrounding node, or the next + -- snippet in the list, .prev is the i(-1) if the self.next.next is the + -- next snippet. + + if self.parent_node and self.next.next == self.parent_node then + return self.next.next + else + return (self.next.next and self.next.next.prev) + end +end + +function Snippet:extmarks_valid() + -- assumption: extmarks are contiguous, and all can be queried via pos_begin_end_raw. + local ok, current_from, self_to = + pcall(self.mark.pos_begin_end_raw, self.mark) + if not ok then + return false + end + + -- below code does not work correctly if the snippet(Node) does not have any children. + if #self.nodes == 0 then + return true + end + + for _, node in ipairs(self.nodes) do + local ok_, node_from, node_to = + pcall(node.mark.pos_begin_end_raw, node.mark) + -- this snippet is invalid if: + -- - we can't get the position of some node + -- - the positions aren't contiguous or don't completely fill the parent, or + -- - any child of this node violates these rules. + if + not ok_ + or util.pos_cmp(current_from, node_from) ~= 0 + or not node:extmarks_valid() + then + return false + end + current_from = node_to + end + if util.pos_cmp(current_from, self_to) ~= 0 then + return false + end + + return true +end + +return { + Snippet = Snippet, + S = S, + _S = _S, + SN = SN, + P = P, + ISN = ISN, + wrap_nodes_in_snippetNode = wrap_nodes_in_snippetNode, + init_snippet_context = init_snippet_context, + init_snippet_opts = init_snippet_opts, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/snippetProxy.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/snippetProxy.lua new file mode 100644 index 00000000..03e1c878 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/snippetProxy.lua @@ -0,0 +1,125 @@ +-- the idea of this class is to lazily parse snippet (eg. only on expansion). +-- +-- This is achieved by returning a proxy that has enough information to tell +-- whether the snippet should be expanded at a given point (eg. all fields +-- necessary to perform Snippet:matches()), but doesn't actually +-- have to parse the snippet, leaving up-front cost of loading a bunch of +-- snippets at a minimum. + +local lsp_parse_fn = require("luasnip.util.parser").parse_snippet +local snip_mod = require("luasnip.nodes.snippet") +local node_util = require("luasnip.nodes.util") +local extend_decorator = require("luasnip.util.extend_decorator") + +local SnippetProxy = {} + +-- add Snippet-functions SnippetProxy can perform using the available data. +SnippetProxy.matches = snip_mod.Snippet.matches +SnippetProxy.invalidate = snip_mod.Snippet.invalidate +SnippetProxy.retrieve_all = snip_mod.Snippet.retrieve_all + +function SnippetProxy:get_docstring() + return self.docstring +end + +function SnippetProxy:instantiate(parse_fn) + -- self already contains initialized context and opts, can just be passed + -- here, no problem. + -- Bonus: if some keys are set on the snippets in the table (from the + -- outside, for whatever reason), they are also present in the expanded + -- snippet. + -- + -- _S will copy self, so we can safely mutate (set metatables). + local snippet = snip_mod._S(self, parse_fn(nil, self._snippet_string)) + -- snippet will have snippetProxies `copy`, nil it in snippet so it calls + -- snippet-copy via metatable. + snippet.copy = nil + + self._snippet = snippet + -- directly call into snippet on missing keys. + setmetatable(self, { + __index = self._snippet, + }) + + -- return snippet so it can provide a missing key. + return snippet +end + +-- some values of the snippet are nil by default, list them here so snippets +-- aren't instantiated because of them. +local license_to_nil = + { priority = true, snippetType = true, _source = true, filetype = true } + +-- context and opts are (almost) the same objects as in s(contex, nodes, opts), snippet is a string representing the snippet. +-- opts can aditionally contain the key `parse_fn`, which will be used to parse +-- the snippet. This is useful, since snipmate-snippets are parsed with a +-- function than regular lsp-snippets. +-- context can be nil, in that case the resulting object can't be inserted into +-- the snippet-tables, but may be used after expansion (i.e. returned from +-- snippet:copy) +local function new(context, snippet, opts) + opts = opts or {} + + -- default to regular lsp-parse-function. + local parse_fn = lsp_parse_fn + + if opts.parse_fn then + parse_fn = opts.parse_fn + end + -- "error": there should not be duplicate keys, don't silently overwrite/keep. + local sp = vim.tbl_extend( + "error", + {}, + context + and snip_mod.init_snippet_context( + node_util.wrap_context(context), + opts + ) + or {}, + snip_mod.init_snippet_opts(opts), + node_util.init_node_opts(opts) + ) + + sp._snippet_string = snippet + -- override docstring + sp.docstring = snippet + + setmetatable(sp, { + __index = function(t, k) + if license_to_nil[k] then + -- k might be nil, return it. + return nil + end + + if SnippetProxy[k] then + -- if it is possible to perform this operation without actually parsing the snippet, just do it. + return SnippetProxy[k] + end + local snip = SnippetProxy.instantiate(t, parse_fn) + if k == "_snippet" then + return snip + else + return snip[k] + end + end, + }) + + -- snippetProxy has to be able to return snippet on copy even after parsing, + -- when the metatable has been changed. Therefore: set copy in each instance + -- of snippetProxy. + function sp:copy() + local copy = self._snippet:copy() + copy.id = self.id + + return copy + end + + return sp +end +extend_decorator.register( + new, + { arg_indx = 1, extend = node_util.snippet_extend_context }, + { arg_indx = 3 } +) + +return new diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/textNode.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/textNode.lua new file mode 100644 index 00000000..88fad040 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/textNode.lua @@ -0,0 +1,71 @@ +local node_mod = require("luasnip.nodes.node") +local util = require("luasnip.util.util") +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local extend_decorator = require("luasnip.util.extend_decorator") + +local TextNode = node_mod.Node:new() + +local function T(static_text, opts) + return TextNode:new({ + static_text = util.to_string_table(static_text), + mark = nil, + type = types.textNode, + }, opts) +end +extend_decorator.register(T, { arg_indx = 2 }) + +function TextNode:input_enter(no_move, dry_run) + if dry_run then + return + end + + self.mark:update_opts(self.ext_opts.active) + self.visited = true + + if not no_move then + local mark_begin_pos = self.mark:pos_begin_raw() + if vim.fn.mode() == "i" then + util.insert_move_on(mark_begin_pos) + else + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes("", true, false, true), + "n", + true + ) + util.normal_move_on_insert(mark_begin_pos) + end + end + + self:event(events.enter, no_move) +end + +function TextNode:update_all_dependents() end + +function TextNode:is_interactive() + -- a resounding false. + return false +end + +function TextNode:extmarks_valid() + local from, to = self.mark:pos_begin_end_raw() + if + util.pos_cmp(from, to) == 0 + and not ( + #self.static_text == 0 + or (#self.static_text == 1 and #self.static_text[1] == 0) + ) + then + -- assume the snippet is invalid if a textNode occupies zero space, + -- but has text which would occupy some. + -- This should allow some modifications, but as soon as a textNode is + -- deleted entirely, we sound the alarm :D + return false + end + return true +end + +return { + T = T, + textNode = TextNode, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/util.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/util.lua new file mode 100644 index 00000000..53d517b7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/util.lua @@ -0,0 +1,789 @@ +local util = require("luasnip.util.util") +local ext_util = require("luasnip.util.ext_opts") +local types = require("luasnip.util.types") +local key_indexer = require("luasnip.nodes.key_indexer") +local session = require("luasnip.session") + +local function subsnip_init_children(parent, children) + for _, child in ipairs(children) do + if child.type == types.snippetNode then + child.snippet = parent.snippet + child:resolve_child_ext_opts() + end + child:resolve_node_ext_opts() + child:subsnip_init() + end +end + +local function init_child_positions_func( + key, + node_children_key, + child_func_name +) + -- maybe via load()? + return function(node, position_so_far) + node[key] = vim.deepcopy(position_so_far) + local pos_depth = #position_so_far + 1 + + for indx, child in ipairs(node[node_children_key]) do + position_so_far[pos_depth] = indx + child[child_func_name](child, position_so_far) + end + -- undo changes to position_so_far. + position_so_far[pos_depth] = nil + end +end + +local function make_args_absolute(args, parent_insert_position, target) + for i, arg in ipairs(args) do + if type(arg) == "number" then + -- the arg is a number, should be interpreted relative to direct + -- parent. + local t = vim.deepcopy(parent_insert_position) + table.insert(t, arg) + target[i] = { absolute_insert_position = t } + else + -- insert node, absolute_indexer, or key itself, node's + -- absolute_insert_position may be nil, check for that during + -- usage. + target[i] = arg + end + end +end + +local function wrap_args(args) + -- stylua: ignore + if type(args) ~= "table" or + (type(args) == "table" and args.absolute_insert_position) or + key_indexer.is_key(args) then + -- args is one single arg, wrap it. + return { args } + else + return args + end +end + +-- includes child, does not include parent. +local function get_nodes_between(parent, child) + local nodes = {} + + -- special case for nodes without absolute_position (which is only + -- start_node). + if child.pos == -1 then + -- no nodes between, only child. + nodes[1] = child + return nodes + end + + local child_pos = child.absolute_position + + local indx = #parent.absolute_position + 1 + local prev = parent + while child_pos[indx] do + local next = prev:resolve_position(child_pos[indx]) + nodes[#nodes + 1] = next + prev = next + indx = indx + 1 + end + + return nodes +end + +-- assumes that children of child are not even active. +-- If they should also be left, do that separately. +-- Does not leave the parent. +local function leave_nodes_between(parent, child, no_move) + local nodes = get_nodes_between(parent, child) + if #nodes == 0 then + return + end + + -- reverse order, leave child first. + for i = #nodes, 2, -1 do + -- this only happens for nodes where the parent will also be left + -- entirely (because we stop at nodes[2], and handle nodes[1] + -- separately) + nodes[i]:input_leave(no_move) + nodes[i - 1]:input_leave_children() + end + nodes[1]:input_leave(no_move) +end + +local function enter_nodes_between(parent, child, no_move) + local nodes = get_nodes_between(parent, child) + if #nodes == 0 then + return + end + + for i = 1, #nodes - 1 do + -- only enter children for nodes before the last (lowest) one. + nodes[i]:input_enter(no_move) + nodes[i]:input_enter_children() + end + nodes[#nodes]:input_enter(no_move) +end + +local function select_node(node) + local node_begin, node_end = node.mark:pos_begin_end_raw() + util.any_select(node_begin, node_end) +end + +local function print_dict(dict) + print(vim.inspect(dict, { + process = function(item, path) + if path[#path] == "node" or path[#path] == "dependent" then + return "node@" .. vim.inspect(item.absolute_position) + elseif path[#path] ~= vim.inspect.METATABLE then + return item + end + end, + })) +end + +local function init_node_opts(opts) + local in_node = {} + if not opts then + opts = {} + end + + -- copy once here, the opts might be reused. + in_node.node_ext_opts = + ext_util.complete(vim.deepcopy(opts.node_ext_opts or {})) + + if opts.merge_node_ext_opts == nil then + in_node.merge_node_ext_opts = true + else + in_node.merge_node_ext_opts = opts.merge_node_ext_opts + end + + in_node.key = opts.key + + in_node.node_callbacks = opts.node_callbacks or {} + + return in_node +end + +local function snippet_extend_context(arg, extend) + if type(arg) == "string" then + arg = { trig = arg } + end + + -- both are table or nil now. + return vim.tbl_extend("keep", arg or {}, extend or {}) +end + +local function wrap_context(context) + if type(context) == "string" then + return { trig = context } + else + return context + end +end + +local function linkable_node(node) + -- node.type has to be one of insertNode, exitNode. + return vim.tbl_contains( + { types.insertNode, types.exitNode }, + rawget(node, "type") + ) +end + +-- mainly used internally, by binarysearch_pos. +-- these are the nodes that are definitely not linkable, there are nodes like +-- dynamicNode or snippetNode that might be linkable, depending on their +-- content. Could look into that to make this more complete, but that does not +-- feel appropriate (higher runtime), most cases should be served well by this +-- heuristic. +local function non_linkable_node(node) + return vim.tbl_contains( + { types.textNode, types.functionNode }, + rawget(node, "type") + ) +end +-- return whether a node is certainly (not) interactive. +-- Coincindentially, the same nodes as (non-)linkable ones, but since there is a +-- semantic difference, use separate names. +local interactive_node = linkable_node +local non_interactive_node = non_linkable_node + +local function prefer_nodes(prefer_func, reject_func) + return function(cmp_mid_to, cmp_mid_from, mid_node) + local reject_mid = reject_func(mid_node) + local prefer_mid = prefer_func(mid_node) + + -- if we can choose which node to continue in, prefer the one that + -- may be linkable/interactive. + if cmp_mid_to == 0 and reject_mid then + return true, false + elseif cmp_mid_from == 0 and reject_mid then + return false, true + elseif (cmp_mid_to == 0 or cmp_mid_from == 0) and prefer_mid then + return false, false + else + return cmp_mid_to >= 0, cmp_mid_from < 0 + end + end +end + +-- functions for resolving conflicts, if `pos` is on the boundary of two nodes. +-- Return whether to continue behind or before mid (in that order). +-- At most one of those may be true, of course. +local binarysearch_preference = { + outside = function(cmp_mid_to, cmp_mid_from, _) + return cmp_mid_to >= 0, cmp_mid_from <= 0 + end, + inside = function(cmp_mid_to, cmp_mid_from, _) + return cmp_mid_to > 0, cmp_mid_from < 0 + end, + linkable = prefer_nodes(linkable_node, non_linkable_node), + interactive = prefer_nodes(interactive_node, non_interactive_node), +} +-- `nodes` is a list of nodes ordered by their occurrence in the buffer. +-- `pos` is a row-column-tuble, byte-columns, and we return the node the LEFT +-- EDGE(/side) of `pos` is inside. +-- This convention is chosen since a snippet inserted at `pos` will move the +-- character at `pos` to the right. +-- The exact meaning of "inside" can be influenced with `respect_rgravs` and +-- `boundary_resolve_mode`: +-- * if `respect_rgravs` is true, "inside" emulates the shifting-behaviour of +-- extmarks: +-- First of all, we compare the left edge of `pos` with the left/right edges +-- of from/to, depending on rgrav. +-- If the left edge is <= left/right edge of from, and < left/right edge of +-- to, `pos` is inside the node. +-- +-- * if `respect_rgravs` is false, pos has to be fully inside a node to be +-- considered inside it. If pos is on the left endpoint, it is considered to be +-- left of the node, and likewise for the right endpoint. +-- +-- * `boundary_resolve_mode` changes how a position on the boundary of a node +-- is treated: +-- * for `"prefer_linkable/interactive"`, we assume that the nodes in `nodes` are +-- contiguous, and prefer falling into the previous/next node if `pos` is on +-- mid's boundary, and mid is not linkable/interactie. +-- This way, we are more likely to return a node that can handle a new +-- snippet/is interactive. +-- * `"prefer_outside"` makes sense when the nodes are not contiguous, and we'd +-- like to find a position between two nodes. +-- This mode makes sense for finding the snippet a new snippet should be +-- inserted in, since we'd like to prefer inserting before/after a snippet, if +-- the position is ambiguous. +-- +-- In general: +-- These options are useful for making this function more general: When +-- searching in the contiguous nodes of a snippet, we'd like this routine to +-- return any of them (obviously the one pos is inside/or on the border of, and +-- we'd like to prefer returning a node that can be linked), but in no case +-- fail. +-- However! when searching the top-level snippets with the intention of finding +-- the snippet/node a new snippet should be expanded inside, it seems better to +-- shift an existing snippet to the right/left than expand the new snippet +-- inside it (when the expand-point is on the boundary). +local function binarysearch_pos( + nodes, + pos, + respect_rgravs, + boundary_resolve_mode +) + local left = 1 + local right = #nodes + + -- actual search-routine from + -- https://github.com/Roblox/Wiki-Lua-Libraries/blob/master/StandardLibraries/BinarySearch.lua + if #nodes == 0 then + return nil, 1 + end + while true do + local mid = left + math.floor((right - left) / 2) + local mid_mark = nodes[mid].mark + local ok, mid_from, mid_to = pcall(mid_mark.pos_begin_end_raw, mid_mark) + + if not ok then + -- error while running this procedure! + -- return false (because I don't know how to do this with `error` + -- and the offending node). + -- (returning data instead of a message in `error` seems weird..) + return false, mid + end + + if respect_rgravs then + -- if rgrav is set on either endpoint, the node considers its + -- endpoint to be the right, not the left edge. + -- We only want to work with left edges but since the right edge is + -- the left edge of the next column, this is not an issue :) + -- TODO: does this fail with multibyte characters??? + if mid_mark:get_rgrav(-1) then + mid_from[2] = mid_from[2] + 1 + end + if mid_mark:get_rgrav(1) then + mid_to[2] = mid_to[2] + 1 + end + end + + local cmp_mid_to = util.pos_cmp(pos, mid_to) + local cmp_mid_from = util.pos_cmp(pos, mid_from) + + local cont_behind_mid, cont_before_mid = + boundary_resolve_mode(cmp_mid_to, cmp_mid_from, nodes[mid]) + + if cont_behind_mid then + -- make sure right-left becomes smaller. + left = mid + 1 + if left > right then + return nil, mid + 1 + end + elseif cont_before_mid then + -- continue search on left side + right = mid - 1 + if left > right then + return nil, mid + end + else + -- greater-equal than mid_from, smaller or equal to mid_to => left edge + -- of pos is inside nodes[mid] :) + return nodes[mid], mid + end + end +end + +-- a and b have to be in the same snippet, return their first (as seen from +-- them) common parent. +local function first_common_node(a, b) + local a_pos = a.absolute_position + local b_pos = b.absolute_position + + -- last as seen from root. + local i = 0 + local last_common = a.parent.snippet + -- invariant: last_common is parent of both a and b. + while (a_pos[i + 1] ~= nil) and a_pos[i + 1] == b_pos[i + 1] do + last_common = last_common:resolve_position(a_pos[i + 1]) + i = i + 1 + end + + return last_common +end + +-- roots at depth 0, children of root at depth 1, their children at 2, ... +local function snippettree_depth(snippet) + local depth = 0 + while snippet.parent_node ~= nil do + snippet = snippet.parent_node.parent.snippet + depth = depth + 1 + end + return depth +end + +-- find the first common snippet a and b have on their respective unique paths +-- to the snippet-roots. +-- if no common ancestor exists (ie. a and b are roots of their buffers' +-- forest, or just in different trees), return nil. +-- in both cases, the paths themselves are returned as well. +-- The common ancestor is included in the paths, except if there is none. +-- Instead of storing the snippets in the paths, they are represented by the +-- node which contains the next-lower snippet in the path (or `from`/`to`, if it's +-- the first node of the path) +-- This is a bit complicated, but this representation contains more information +-- (or, more easily accessible information) than storing snippets: the +-- immediate parent of the child along the path cannot be easily retrieved if +-- the snippet is stored, but the snippet can be easily retrieved if the child +-- is stored (.parent.snippet). +-- And, so far this is pretty specific to refocus, and thus modeled so there is +-- very little additional work in that method. +-- At most one of a,b may be nil. +local function first_common_snippet_ancestor_path(a, b) + local a_path = {} + local b_path = {} + + -- general idea: we find the depth of a and b, walk upward with the deeper + -- one until we find its first ancestor with the same depth as the less + -- deep snippet, and then follow both paths until they arrive at the same + -- snippet (or at the root of their respective trees). + -- if either is nil, we treat it like it's one of the roots (the code will + -- behave correctly this way, and return an empty path for the nil-node, + -- and the correct path for the non-nil one). + local a_depth = a ~= nil and snippettree_depth(a) or 0 + local b_depth = b ~= nil and snippettree_depth(b) or 0 + + -- bit subtle: both could be 0, but one could be nil. + -- deeper should not be nil! (this allows us to do the whole walk for the + -- non-nil node in the first for-loop, as opposed to needing some special + -- handling). + local deeper, deeper_path, other, other_path + if b == nil or (a ~= nil and a_depth > b_depth) then + deeper = a + other = b + deeper_path = a_path + other_path = b_path + else + -- we land here if `b ~= nil and (a == nil or a_depth >= b_depth)`, so + -- exactly what we want. + deeper = b + other = a + deeper_path = b_path + other_path = a_path + end + + for _ = 1, math.abs(a_depth - b_depth) do + table.insert(deeper_path, deeper.parent_node) + deeper = deeper.parent_node.parent.snippet + end + -- here: deeper and other are at the same depth. + -- If we walk upwards one step at a time, they will meet at the same + -- parent, or hit their respective roots. + + -- deeper can't be nil, if other is, we are done here and can return the + -- paths (and there is no shared node) + if other == nil then + return nil, a_path, b_path + end + -- beyond here, deeper and other are not nil. + + while deeper ~= other do + if deeper.parent_node == nil then + -- deeper is at depth 0 => other as well => both are roots. + return nil, a_path, b_path + end + + table.insert(deeper_path, deeper.parent_node) + table.insert(other_path, other.parent_node) + + -- walk one step towards root. + deeper = deeper.parent_node.parent.snippet + other = other.parent_node.parent.snippet + end + + -- either one will do here. + return deeper, a_path, b_path +end + +-- removes focus from `from` and upwards up to the first common ancestor +-- (node!) of `from` and `to`, and then focuses nodes between that ancestor and +-- `to`. +-- Requires that `from` is currently entered/focused, and that no snippet +-- between `to` and its root is invalid. +local function refocus(from, to) + if from == nil and to == nil then + -- absolutely nothing to do, should not happen. + return + end + -- pass nil if from/to is nil. + -- if either is nil, first_common_node is nil, and the corresponding list empty. + local first_common_snippet, from_snip_path, to_snip_path = + first_common_snippet_ancestor_path( + from and from.parent.snippet, + to and to.parent.snippet + ) + + -- we want leave/enter_path to be s.t. leaving/entering all nodes between + -- each entry and its snippet (and the snippet itself) will leave/enter all + -- nodes between the first common snippet (or the root-snippet) and + -- from/to. + -- Then, the nodes between the first common node and the respective + -- entrypoints (also nodes) into the first common snippet will have to be + -- left/entered, which is handled by final_leave_/first_enter_/common_node. + + -- from, to are not yet in the paths. + table.insert(from_snip_path, 1, from) + table.insert(to_snip_path, 1, to) + + -- determine how far to leave: if there is a common snippet, only up to the + -- first common node of from and to, otherwise leave the one snippet, and + -- enter the other completely. + local final_leave_node, first_enter_node, common_node + if first_common_snippet then + -- there is a common snippet => there is a common node => we have to + -- set final_leave_node, first_enter_node, and common_node. + final_leave_node = from_snip_path[#from_snip_path] + first_enter_node = to_snip_path[#to_snip_path] + common_node = first_common_node(first_enter_node, final_leave_node) + + -- Also remove these last nodes from the lists, their snippet is not + -- supposed to be left entirely. + from_snip_path[#from_snip_path] = nil + to_snip_path[#to_snip_path] = nil + end + + -- now do leave/enter, set no_move on all operations. + -- if one of from/to was nil, there are no leave/enter-operations done for + -- it (from/to_snip_path is {}, final_leave/first_enter_* is nil). + + -- leave_children on all from-nodes except the original from. + if #from_snip_path > 0 then + local ok1, ok2 + if from.type == types.exitNode then + ok1 = pcall(from.input_leave, from, true) + ok2 = true + else + -- we know that the first node is from. + ok1 = pcall(leave_nodes_between, from.parent.snippet, from, true) + -- leave_nodes_between does not affect snippet, so that has to be left + -- here. + -- snippet does not have input_leave_children, so only input_leave + -- needs to be called. + ok2 = pcall( + from.parent.snippet.input_leave, + from.parent.snippet, + true + ) + end + if not ok1 or not ok2 then + from.parent.snippet:remove_from_jumplist() + end + end + for i = 2, #from_snip_path do + local node = from_snip_path[i] + local ok1, ok2, ok3 + ok1 = pcall(node.input_leave_children, node) + + if node.type == types.exitNode then + ok2 = pcall(node.input_leave, node, true) + ok3 = true + else + ok2 = pcall(leave_nodes_between, node.parent.snippet, node, true) + ok3 = pcall( + node.parent.snippet.input_leave, + node.parent.snippet, + true + ) + end + + if not ok1 or not ok2 or not ok3 then + from.parent.snippet:remove_from_jumplist() + end + end + + -- this leave, and the following enters should be safe: the path to `to` + -- was verified via extmarks_valid (precondition). + if common_node and final_leave_node then + -- if the final_leave_node is from, its children are not active (which + -- stems from the requirement that from is the currently active node), + -- and so don't have to be left. + if final_leave_node ~= from then + final_leave_node:input_leave_children() + end + leave_nodes_between(common_node, final_leave_node, true) + end + + if common_node and first_enter_node then + -- In general we assume that common_node is active when we are here. + -- This may not be the case if we are currently inside the i(0) or + -- i(-1), since the snippet might be the common node and in this case, + -- it is inactive. + -- This means that, if we want to enter a non-exitNode, we have to + -- explicitly activate the snippet for all jumps to behave correctly. + -- (if we enter a i(0)/i(-1), this is not necessary, of course). + if + final_leave_node.type == types.exitNode + and first_enter_node.type ~= types.exitNode + then + common_node:input_enter(true) + end + -- symmetrically, entering an i(0)/i(-1) requires leaving the snippet. + if + final_leave_node.type ~= types.exitNode + and first_enter_node.type == types.exitNode + then + common_node:input_leave(true) + end + + enter_nodes_between(common_node, first_enter_node, true) + + -- if the `first_enter_node` is already `to` (occurs if `to` is in the + -- common snippet of to and from), we should not enter its children. + -- (we only want to `input_enter` to.) + if first_enter_node ~= to then + first_enter_node:input_enter_children() + end + end + + -- same here, input_enter_children has to be called manually for the + -- to-nodes of the path we are entering (since enter_nodes_between does not + -- call it for the child-node). + + for i = #to_snip_path, 2, -1 do + local node = to_snip_path[i] + if node.type ~= types.exitNode then + node.parent.snippet:input_enter(true) + enter_nodes_between(node.parent.snippet, node, true) + else + node:input_enter(true) + end + node:input_enter_children() + end + if #to_snip_path > 0 then + if to.type ~= types.exitNode then + to.parent.snippet:input_enter(true) + else + to.parent.snippet:input_leave(true) + end + enter_nodes_between(to.parent.snippet, to, true) + end + + -- it may be that we only leave nodes in this process (happens if to is a + -- parent of from). + -- If that is the case, we will not explicitly focus on to, and it may be + -- that focus is even lost if it was focused previously (leave may trigger + -- update, update may change focus) + -- To prevent this, just call focus here, which is pretty close to a noop + -- if to is already focused. + if to then + to:focus() + end +end + +local function generic_extmarks_valid(node, child) + -- valid if + -- - extmark-extents match. + -- - current choice is valid + local ok1, self_from, self_to = + pcall(node.mark.pos_begin_end_raw, node.mark) + local ok2, child_from, child_to = + pcall(child.mark.pos_begin_end_raw, child.mark) + + if + not ok1 + or not ok2 + or util.pos_cmp(self_from, child_from) ~= 0 + or util.pos_cmp(self_to, child_to) ~= 0 + then + return false + end + return child:extmarks_valid() +end + +-- returns: * the smallest known snippet `pos` is inside. +-- * the list of other snippets inside the snippet of this smallest +-- node +-- * the index this snippet would be at if inserted into that list +-- * the node of this snippet pos is on. +local function snippettree_find_undamaged_node(pos, opts) + local prev_parent, child_indx, found_parent + local prev_parent_children = + session.snippet_roots[vim.api.nvim_get_current_buf()] + + while true do + -- false: don't respect rgravs. + -- Prefer inserting the snippet outside an existing one. + found_parent, child_indx = binarysearch_pos( + prev_parent_children, + pos, + opts.tree_respect_rgravs, + opts.tree_preference + ) + if found_parent == false then + -- if the procedure returns false, there was an error getting the + -- position of a node (in this case, that node is a snippet). + -- The position of the offending snippet is returned in child_indx, + -- and we can remove it here. + prev_parent_children[child_indx]:remove_from_jumplist() + elseif found_parent ~= nil and not found_parent:extmarks_valid() then + -- found snippet damaged (the idea to sidestep the damaged snippet, + -- even if no error occurred _right now_, is to ensure that we can + -- input_enter all the nodes along the insertion-path correctly). + found_parent:remove_from_jumplist() + -- continue again with same parent, but one less snippet in its + -- children => shouldn't cause endless loop. + elseif found_parent == nil then + break + else + prev_parent = found_parent + -- can index prev_parent, since found_parent is not nil, and + -- assigned to prev_parent. + prev_parent_children = prev_parent.child_snippets + end + end + + local node + if prev_parent then + -- if found, find node to insert at, prefer receiving a linkable node. + node = prev_parent:node_at(pos, opts.snippet_mode) + end + + return prev_parent, prev_parent_children, child_indx, node +end + +local function root_path(node) + local path = {} + + while node do + local node_snippet = node.parent.snippet + local snippet_node_path = get_nodes_between(node_snippet, node) + -- get_nodes_between gives parent -> node, but we need + -- node -> parent => insert back to front. + for i = #snippet_node_path, 1, -1 do + table.insert(path, snippet_node_path[i]) + end + -- parent not in get_nodes_between. + table.insert(path, node_snippet) + + node = node_snippet.parent_node + end + + return path +end + +-- adjust rgravs of siblings of the node with indx child_from_indx in nodes. +local function nodelist_adjust_rgravs( + nodes, + child_from_indx, + child_endpoint, + direction, + rgrav, + nodes_adjacent +) + -- only handle siblings, not the node with child_from_indx itself. + local i = child_from_indx + local node = nodes[i] + while node do + local direction_node_endpoint = node.mark:get_endpoint(direction) + if util.pos_equal(direction_node_endpoint, child_endpoint) then + -- both endpoints of node are on top of child_endpoint (we wouldn't + -- be in the loop with `node` if the -direction-endpoint didn't + -- match), so update rgravs of the entire subtree to match rgrav + node:subtree_set_rgrav(rgrav) + else + -- either assume that they are adjacent, or check. + if + nodes_adjacent + or util.pos_equal( + node.mark:get_endpoint(-direction), + child_endpoint + ) + then + -- only the -direction-endpoint matches child_endpoint, adjust its + -- position and break the loop (don't need to look at any other + -- siblings). + node:subtree_set_pos_rgrav(child_endpoint, direction, rgrav) + end + break + end + + i = i + direction + node = nodes[i] + end +end + +return { + subsnip_init_children = subsnip_init_children, + init_child_positions_func = init_child_positions_func, + make_args_absolute = make_args_absolute, + wrap_args = wrap_args, + wrap_context = wrap_context, + get_nodes_between = get_nodes_between, + leave_nodes_between = leave_nodes_between, + enter_nodes_between = enter_nodes_between, + select_node = select_node, + print_dict = print_dict, + init_node_opts = init_node_opts, + snippet_extend_context = snippet_extend_context, + linkable_node = linkable_node, + binarysearch_pos = binarysearch_pos, + binarysearch_preference = binarysearch_preference, + refocus = refocus, + generic_extmarks_valid = generic_extmarks_valid, + snippettree_find_undamaged_node = snippettree_find_undamaged_node, + interactive_node = interactive_node, + root_path = root_path, + nodelist_adjust_rgravs = nodelist_adjust_rgravs, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/util/trig_engines.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/util/trig_engines.lua new file mode 100644 index 00000000..30da7245 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/nodes/util/trig_engines.lua @@ -0,0 +1,132 @@ +local jsregexp_compile_safe = require("luasnip.util.jsregexp") + +-- generate nil-opts-instances here, and save them. +-- This is to prevent generating 100s of the exact same function. +local default_match_pattern, default_match_plain, default_match_vim + +local function apply_common_opts(line_to_cursor, opts) + if opts and opts.max_len then + return line_to_cursor:sub(#line_to_cursor - opts.max_len + 1) + else + return line_to_cursor + end +end + +-- these functions get the line up to the cursor, the trigger, and then +-- determine whether the trigger matches the current line. +-- If the trigger does not match, the functions shall return nil, otherwise +-- the matching substring and the list of captures (empty table if there aren't +-- any). + +local function match_plain(_, opts) + if opts == nil then + return default_match_plain + end + + return function(line_to_cursor, trigger) + line_to_cursor = apply_common_opts(line_to_cursor, opts) + if + line_to_cursor:sub(#line_to_cursor - #trigger + 1, #line_to_cursor) + == trigger + then + -- no captures for plain trigger. + return trigger, {} + else + return nil + end + end +end +default_match_plain = match_plain(nil, {}) + +local function match_pattern(_, opts) + if opts == nil then + return default_match_pattern + end + + return function(line_to_cursor, trigger) + line_to_cursor = apply_common_opts(line_to_cursor, opts) + + -- look for match which ends at the cursor. + -- put all results into a list, there might be many capture-groups. + local find_res = { line_to_cursor:find(trigger .. "$") } + + if #find_res > 0 then + -- if there is a match, determine matching string, and the + -- capture-groups. + local captures = {} + -- find_res[1] is `from`, find_res[2] is `to` (which we already know + -- anyway). + local from = find_res[1] + local match = line_to_cursor:sub(from, #line_to_cursor) + -- collect capture-groups. + for i = 3, #find_res do + captures[i - 2] = find_res[i] + end + return match, captures + else + return nil + end + end +end +default_match_pattern = match_pattern(nil, {}) + +local ecma_engine +if jsregexp_compile_safe then + ecma_engine = function(trig, opts) + local trig_compiled, err_maybe = jsregexp_compile_safe(trig .. "$", "") + if not trig_compiled then + error(("Error while compiling regex: %s"):format(err_maybe)) + end + + return function(line_to_cursor, _) + line_to_cursor = apply_common_opts(line_to_cursor, opts) + + -- get first (very likely only, since we appended the "$") match. + local match = trig_compiled(line_to_cursor)[1] + if match then + -- return full match, and all groups. + return line_to_cursor:sub(match.begin_ind), match.groups + else + return nil + end + end + end +else + ecma_engine = function(x, opts) + return match_plain(x, opts) + end +end + +local function match_vim(_, opts) + if opts == nil then + return default_match_vim + end + + return function(line_to_cursor, trigger) + line_to_cursor = apply_common_opts(line_to_cursor, opts) + + local matchlist = vim.fn.matchlist(line_to_cursor, trigger .. "$") + if #matchlist > 0 then + local groups = {} + for i = 2, 10 do + -- PROBLEM: vim does not differentiate between an empty ("") + -- and a missing capture. + -- Since we need to differentiate between the two (Check `:h + -- luasnip-variables-lsp-variables`), we assume, here, that an + -- empty string is an unmatched group. + groups[i - 1] = matchlist[i] ~= "" and matchlist[i] or nil + end + return matchlist[1], groups + else + return nil + end + end +end +default_match_vim = match_vim(nil, {}) + +return { + plain = match_plain, + pattern = match_pattern, + ecma = ecma_engine, + vim = match_vim, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/enqueueable_operations.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/enqueueable_operations.lua new file mode 100644 index 00000000..7d56691c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/enqueueable_operations.lua @@ -0,0 +1,35 @@ +local snippet_collection = require("luasnip.session.snippet_collection") + +local M = {} + +local refresh_enqueued = false +local next_refresh_fts = {} + +function M.refresh_notify(ft) + next_refresh_fts[ft] = true + + if not refresh_enqueued then + vim.schedule(function() + for enq_ft, _ in pairs(next_refresh_fts) do + snippet_collection.refresh_notify(enq_ft) + end + + next_refresh_fts = {} + refresh_enqueued = false + end) + + refresh_enqueued = true + end +end + +local clean_enqueued = false +function M.clean_invalidated() + if not clean_enqueued then + vim.schedule(function() + snippet_collection.clean_invalidated({ inv_limit = 100 }) + end) + end + clean_enqueued = true +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/init.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/init.lua new file mode 100644 index 00000000..ad1d6dfa --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/init.lua @@ -0,0 +1,52 @@ +-- used to store values like current nodes or the active node for autocommands. +local M = {} + +M.ft_redirect = {} +setmetatable(M.ft_redirect, { + __index = function(table, key) + -- no entry for this ft(key), set it to avoid calls on each expand for + -- this filetype. + local val = { key } + rawset(table, key, val) + return val + end, +}) + +M.current_nodes = {} +-- roots of snippet-trees, per-buffer. +-- snippet_roots[n] => list of snippet-roots in buffer n. +M.snippet_roots = setmetatable({}, { + -- create missing lists automatically. + __index = function(t, k) + local new_t = {} + rawset(t, k, new_t) + return new_t + end, +}) +M.ns_id = vim.api.nvim_create_namespace("Luasnip") +M.active_choice_nodes = {} + +-- only here for overview. +M.latest_load_ft = nil + +M.last_expand_snip = nil +M.last_expand_opts = nil + +-- jump_active is set while luasnip moves the cursor, prevents +-- (for example) updating dependents or deleting a snippet via +-- exit_out_of_region while jumping. +-- init with false, it will be set by (eg.) ls.jump(). +M.jump_active = false + +-- initial value, might be overwritten immediately. +-- No danger of overwriting user-config, since this has to be loaded to allow +-- overwriting. +M.config = require("luasnip.default_config") + +M.loaded_fts = {} + +function M.get_snip_env() + return M.config.snip_env +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/snippet_collection/init.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/snippet_collection/init.lua new file mode 100644 index 00000000..33f05c04 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/snippet_collection/init.lua @@ -0,0 +1,328 @@ +local source = require("luasnip.session.snippet_collection.source") +local u_table = require("luasnip.util.table") +local auto_creating_tables = + require("luasnip.util.auto_table").warn_depth_autotable +local session = require("luasnip.session") + +-- store snippets by some key. +-- also ordered by filetype, eg. +-- { +-- key = { +-- ft1 = {...}, +-- ft2 = {...} +-- } +-- } +local M = { + invalidated_count = 0, +} + +local by_key = {} + +-- stores snippets/autosnippets by priority. +local by_prio = { + snippets = { + -- stores sorted keys, eg 1=1000, 2=1010, 3=1020,..., used for + -- quick iterating. + order = {}, + }, + autosnippets = { + order = {}, + }, +} + +-- this isn't in util/util.lua due to circular dependencies. Would be cleaner +-- to include it there, but it's alright to keep here for now. +-- +-- this is linear, binary search would certainly be nicer, but for our +-- applications this should easily be enough. +local function insert_sorted_unique(t, k) + local tbl_len = #t + + local i = 1 + -- k does not yet exist in table, find first i so t[i] > k. + for _ = 1, tbl_len do + if t[i] > k then + break + end + i = i + 1 + end + + -- shift all t[j] with j > i back by one. + for j = tbl_len, i, -1 do + t[j + 1] = t[j] + end + + t[i] = k +end + +local by_prio_snippets_mt = { + __index = function(s, k) + -- make new tables as they are indexed + return auto_creating_tables(s, k, 3) + end, + __newindex = function(t, k, v) + -- update priority-order as well. + insert_sorted_unique(t.order, k) + rawset(t, k, v) + end, +} +-- metatable for the by_prio table used when by_prio.type[prio] is reset +-- create here so that it can be shared and only has to be created once +local prio_mt2 = { + __index = function(s, k) + -- make new tables as they are indexed + return auto_creating_tables(s, k, 2) + end, +} + +setmetatable(by_prio.snippets, by_prio_snippets_mt) +setmetatable(by_prio.autosnippets, by_prio_snippets_mt) + +-- iterate priorities, high to low. +local function prio_iter(type) + local order = by_prio[type].order + local i = #order + 1 + + return function() + i = i - 1 + if i > 0 then + return by_prio[type][order[i]] + end + return nil + end +end + +local by_ft = { + snippets = {}, + autosnippets = {}, +} + +local by_ft_snippets_mt = { + __index = function(s, k) + return auto_creating_tables(s, k, 2) + end, +} +setmetatable(by_ft.snippets, by_ft_snippets_mt) +setmetatable(by_ft.autosnippets, by_ft_snippets_mt) + +local by_id = setmetatable({}, { + -- make by_id-table weak (v). + -- this means it won't be necessary to explicitly nil values (snippets) in + -- this table. + __mode = "v", +}) + +-- ft: any filetype, optional. +function M.clear_snippets(ft) + if ft then + -- remove all ft-(auto)snippets for all priorities. + -- set to empty table so we won't need to rebuild/clear the order-table. + for _, prio in ipairs(by_prio.snippets.order) do + by_prio.snippets[prio][ft] = {} + end + for _, prio in ipairs(by_prio.autosnippets.order) do + by_prio.autosnippets[prio][ft] = {} + end + + by_ft.snippets[ft] = nil + by_ft.autosnippets[ft] = nil + + for key, _ in pairs(by_key) do + by_key[key][ft] = nil + end + else + -- remove all (auto)snippets for all priorities. + for _, prio in ipairs(by_prio.snippets.order) do + by_prio.snippets[prio] = {} + setmetatable(by_prio.snippets[prio], prio_mt2) + end + for _, prio in ipairs(by_prio.autosnippets.order) do + by_prio.autosnippets[prio] = {} + setmetatable(by_prio.autosnippets[prio], prio_mt2) + end + + by_key = {} + by_ft.snippets = {} + setmetatable(by_ft.snippets, by_ft_snippets_mt) + by_ft.autosnippets = {} + setmetatable(by_ft.autosnippets, by_ft_snippets_mt) + end +end + +function M.match_snippet(line, fts, type) + local expand_params + + for prio_by_ft in prio_iter(type) do + for _, ft in ipairs(fts) do + for _, snip in ipairs(prio_by_ft[ft] or {}) do + expand_params = snip:matches(line) + if expand_params then + -- return matching snippet and table with expand-parameters. + return snip, expand_params + end + end + end + end + + return nil +end + +local function without_invalidated(snippets_by_ft) + local new_snippets = {} + + for ft, ft_snippets in pairs(snippets_by_ft) do + new_snippets[ft] = {} + for _, snippet in ipairs(ft_snippets) do + if not snippet.invalidated then + table.insert(new_snippets[ft], snippet) + end + end + end + + return new_snippets +end + +function M.clean_invalidated(opts) + if opts.inv_limit then + if M.invalidated_count <= opts.inv_limit then + return + end + end + + -- remove invalidated snippets from all tables. + for _, type_snippets in pairs(by_prio) do + for key, prio_snippets in pairs(type_snippets) do + if key ~= "order" then + type_snippets[key] = without_invalidated(prio_snippets) + setmetatable(type_snippets[key], prio_mt2) + end + end + end + + for type, type_snippets in pairs(by_ft) do + by_ft[type] = without_invalidated(type_snippets) + setmetatable(by_ft[type], by_ft_snippets_mt) + end + + for key, key_snippets in pairs(by_key) do + by_key[key] = without_invalidated(key_snippets) + end + + M.invalidated_count = 0 +end + +local function invalidate_addables(addables_by_ft) + for _, addables in pairs(addables_by_ft) do + for _, addable in ipairs(addables) do + for _, expandable in ipairs(addable:retrieve_all()) do + expandable:invalidate() + end + end + end + M.clean_invalidated({ inv_limit = 100 }) +end + +local current_id = 0 +-- snippets like {ft1={}, ft2={}}, opts should be properly +-- initialized with default values. +function M.add_snippets(snippets, opts) + for ft, ft_snippets in pairs(snippets) do + for _, addable in ipairs(ft_snippets) do + for _, snip in ipairs(addable:retrieve_all()) do + local snip_prio = opts.override_priority + or (snip.priority and snip.priority) + or opts.default_priority + or 1000 + + -- if snippetType undefined by snippet, take default value from opts + local snip_type = snip.snippetType ~= nil and snip.snippetType + or opts.type + assert( + snip_type == "autosnippets" or snip_type == "snippets", + "snippetType must be either 'autosnippets' or 'snippets', was " + .. vim.inspect(snip_type) + ) + + local snip_ft = snip.filetype or ft + + snip.id = current_id + current_id = current_id + 1 + + -- do the insertion + table.insert(by_prio[snip_type][snip_prio][snip_ft], snip) + table.insert(by_ft[snip_type][snip_ft], snip) + by_id[snip.id] = snip + + -- set source if it is available. + if snip._source then + source.set(snip, snip._source) + end + end + end + end + + if opts.key then + if by_key[opts.key] then + invalidate_addables(by_key[opts.key]) + end + by_key[opts.key] = snippets + end +end + +-- specialized copy functions to not loose performance on ifs when copying +-- and to be able to specify when pairs or ipairs is used +local function copy_by_ft_type_ft(tab) + local r = {} + for k, v in ipairs(tab) do + r[k] = v + end + return r +end +local function copy_by_ft_type(tab) + local r = {} + for k, v in pairs(tab) do + r[k] = copy_by_ft_type_ft(v) + end + return r +end + +-- ft may be nil, type not. +function M.get_snippets(ft, type) + if ft then + return copy_by_ft_type_ft(by_ft[type][ft]) + else + return copy_by_ft_type(by_ft[type]) + end +end + +function M.get_id_snippet(id) + return by_id[id] +end + +local function get_all_snippet_fts() + local ft_set = {} + for ft, _ in pairs(by_ft.snippets) do + ft_set[ft] = true + end + for ft, _ in pairs(by_ft.autosnippets) do + ft_set[ft] = true + end + + return u_table.set_to_list(ft_set) +end + +-- modules that want to call refresh_notify probably also want to notify others +-- of adding those snippets => put those functions into the same module. +function M.refresh_notify(ft_or_nil) + local fts = ft_or_nil and { ft_or_nil } or get_all_snippet_fts() + + for _, ft in ipairs(fts) do + session.latest_load_ft = ft + vim.api.nvim_exec_autocmds( + "User", + { pattern = "LuasnipSnippetsAdded", modeline = false } + ) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/snippet_collection/source.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/snippet_collection/source.lua new file mode 100644 index 00000000..9f3319ef --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/session/snippet_collection/source.lua @@ -0,0 +1,38 @@ +local id_to_source = {} + +local M = {} + +function M.from_debuginfo(debuginfo) + assert(debuginfo.source, "debuginfo contains source") + assert( + debuginfo.source:match("^@"), + "debuginfo-source is a file: " .. debuginfo.source + ) + + return { + -- omit leading '@'. + file = debuginfo.source:sub(2), + line = debuginfo.currentline, + } +end + +function M.from_location(file, opts) + assert(file, "source needs file") + opts = opts or {} + + return { file = file, line = opts.line, line_end = opts.line_end } +end + +function M.set(snippet, source) + -- snippets only get their id after being added, make sure this is the + -- case. + assert(snippet.id, "snippet has an id") + + id_to_source[snippet.id] = source +end + +function M.get(snippet) + return id_to_source[snippet.id] +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/snippets.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/snippets.lua new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/_builtin_vars.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/_builtin_vars.lua new file mode 100644 index 00000000..8287182f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/_builtin_vars.lua @@ -0,0 +1,208 @@ +local util = require("luasnip.util.util") +local select_util = require("luasnip.util.select") +local time_util = require("luasnip.util.time") +local lazy_vars = {} + +-- Variables defined in https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variables + +-- Inherited from TextMate +function lazy_vars.TM_FILENAME() + return vim.fn.expand("%:t") +end + +function lazy_vars.TM_FILENAME_BASE() + return vim.fn.expand("%:t:s?\\.[^\\.]\\+$??") +end + +function lazy_vars.TM_DIRECTORY() + return vim.fn.expand("%:p:h") +end + +function lazy_vars.TM_FILEPATH() + return vim.fn.expand("%:p") +end + +-- Vscode only + +function lazy_vars.CLIPBOARD() -- The contents of your clipboard + return vim.fn.getreg('"', 1, true) +end + +local function buf_to_ws_part() + local LSP_WORSKPACE_PARTS = "LSP_WORSKPACE_PARTS" + local ok, ws_parts = pcall(vim.api.nvim_buf_get_var, 0, LSP_WORSKPACE_PARTS) + if not ok then + local file_path = vim.fn.expand("%:p") + + for _, ws in pairs(vim.lsp.buf.list_workspace_folders()) do + if file_path:find(ws, 1, true) == 1 then + ws_parts = { ws, file_path:sub(#ws + 2, -1) } + break + end + end + -- If it can't be extracted from lsp, then we use the file path + if not ok and not ws_parts then + ws_parts = { vim.fn.expand("%:p:h"), vim.fn.expand("%:p:t") } + end + vim.api.nvim_buf_set_var(0, LSP_WORSKPACE_PARTS, ws_parts) + end + return ws_parts +end + +function lazy_vars.RELATIVE_FILEPATH() -- The relative (to the opened workspace or folder) file path of the current document + return buf_to_ws_part()[2] +end + +function lazy_vars.WORKSPACE_FOLDER() -- The path of the opened workspace or folder + return buf_to_ws_part()[1] +end + +function lazy_vars.WORKSPACE_NAME() -- The name of the opened workspace or folder + local parts = vim.split(buf_to_ws_part()[1] or "", "[\\/]") + return parts[#parts] +end + +-- DateTime Related +function lazy_vars.CURRENT_YEAR() + return os.date("%Y") +end + +function lazy_vars.CURRENT_YEAR_SHORT() + return os.date("%y") +end + +function lazy_vars.CURRENT_MONTH() + return os.date("%m") +end + +function lazy_vars.CURRENT_MONTH_NAME() + return os.date("%B") +end + +function lazy_vars.CURRENT_MONTH_NAME_SHORT() + return os.date("%b") +end + +function lazy_vars.CURRENT_DATE() + return os.date("%d") +end + +function lazy_vars.CURRENT_DAY_NAME() + return os.date("%A") +end + +function lazy_vars.CURRENT_DAY_NAME_SHORT() + return os.date("%a") +end + +function lazy_vars.CURRENT_HOUR() + return os.date("%H") +end + +function lazy_vars.CURRENT_MINUTE() + return os.date("%M") +end + +function lazy_vars.CURRENT_SECOND() + return os.date("%S") +end + +function lazy_vars.CURRENT_SECONDS_UNIX() + return tostring(os.time()) +end + +function lazy_vars.CURRENT_TIMEZONE_OFFSET() + return time_util + .get_timezone_offset(os.time()) + :gsub("([+-])(%d%d)(%d%d)$", "%1%2:%3") +end + +-- For inserting random values + +math.randomseed(os.time()) + +function lazy_vars.RANDOM() + return string.format("%06d", math.random(999999)) +end + +function lazy_vars.RANDOM_HEX() + return string.format("%06x", math.random(16777216)) --16^6 +end + +function lazy_vars.UUID() + local random = math.random + local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" + local out + local function subs(c) + local v = (((c == "x") and random(0, 15)) or random(8, 11)) + return string.format("%x", v) + end + + out = template:gsub("[xy]", subs) + return out +end + +function lazy_vars.LINE_COMMENT() + return util.buffer_comment_chars()[1] +end + +function lazy_vars.BLOCK_COMMENT_START() + return util.buffer_comment_chars()[2] +end + +function lazy_vars.BLOCK_COMMENT_END() + return util.buffer_comment_chars()[3] +end + +-- These are the vars that have to be populated once the snippet starts to avoid any issue +local function eager_vars(info) + local vars = {} + local pos = info.pos + vars.TM_CURRENT_LINE = + vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false)[1] + vars.TM_CURRENT_WORD = util.word_under_cursor(pos, vars.TM_CURRENT_LINE) + vars.TM_LINE_INDEX = tostring(pos[1]) + vars.TM_LINE_NUMBER = tostring(pos[1] + 1) + vars.LS_SELECT_RAW, vars.LS_SELECT_DEDENT, vars.TM_SELECTED_TEXT = + select_util.retrieve() + -- These are for backward compatibility, for now on all builtins that are not part of TM_ go in LS_ + vars.SELECT_RAW, vars.SELECT_DEDENT = + vars.LS_SELECT_RAW, vars.LS_SELECT_DEDENT + for i, cap in ipairs(info.captures) do + vars["LS_CAPTURE_" .. i] = cap + end + vars.LS_TRIGGER = info.trigger + return vars +end + +local builtin_ns = { SELECT = true, LS = true } + +for name, _ in pairs(lazy_vars) do + local parts = vim.split(name, "_") + if #parts > 1 then + builtin_ns[parts[1]] = true + end +end + +local _is_table = { + TM_SELECTED_TEXT = true, + SELECT_RAW = true, + SELECT_DEDENT = true, +} + +return { + is_table = function(key) + -- variables generated at runtime by treesitter-postfix. + if + key:match("LS_TSCAPTURE_.*") + or key == "LS_TSPOSTFIX_MATCH" + or key == "LS_TSPOSTFIX_DATA" + then + return true + end + return _is_table[key] or false + end, + vars = lazy_vars, + init = eager_vars, + builtin_ns = builtin_ns, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/auto_table.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/auto_table.lua new file mode 100644 index 00000000..3a9858c0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/auto_table.lua @@ -0,0 +1,54 @@ +-- depth specifies how many levels under this table should be allowed to index +-- throug this metamethod +-- set depth to 0 to disable checking +-- Acknowledgment: This is (maybe more than) inspired by +-- https://lua-users.org/wiki/AutomagicTables so special thanks to +-- Thomas Wrensch and Rici Lake for sharing their ideas on this topic. +local function auto_creating_tables_warn_depth(self, key, depth) + local t = {} + assert(depth ~= 1, "don't index at that level") + setmetatable(t, { + -- creating a new function on each time (could be shared) isn't that + -- nice. Nonetheless this shouldn't be too bad, as these are only + -- created twice (auto+snippet) per ft and twice for each prio,ft + -- combination + __index = function(s, k) + return auto_creating_tables_warn_depth(s, k, depth - 1) + end, + }) + self[key] = t + return t +end + +local function auto_creating_tables(self, key, depth) + local t = {} + if depth ~= 1 then + setmetatable(t, { + __index = function(s, k) + return auto_creating_tables(s, k, depth - 1) + end, + }) + end + self[key] = t + return t +end + +local M = {} + +function M.autotable(max_depth, opts) + opts = opts or {} + local warn = vim.F.if_nil(opts.warn, false) + + local auto_table_func = warn and auto_creating_tables_warn_depth + or auto_creating_tables + + return setmetatable({}, { + __index = function(s, k) + return auto_table_func(s, k, max_depth - 1) + end, + }) +end + +M.warn_depth_autotable = auto_creating_tables_warn_depth + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/dict.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/dict.lua new file mode 100644 index 00000000..3ce60ab6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/dict.lua @@ -0,0 +1,63 @@ +local Dictionary = {} + +local function new(o) + return setmetatable(o or {}, { + __index = Dictionary, + }) +end + +function Dictionary:set(path, value) + -- Insp(path) + -- print("val: ", value) + local current_table = self + for i = 1, #path - 1 do + local crt_key = path[i] + if not current_table[crt_key] then + current_table[crt_key] = {} + end + current_table = current_table[crt_key] + end + current_table[path[#path]] = value +end + +function Dictionary:get(path) + local current_table = self + for _, v in ipairs(path) do + if not current_table[v] then + return nil + end + current_table = current_table[v] + end + -- may not be a table. + return current_table +end + +function Dictionary:find_all(path, key) + local res = {} + local to_search = { self:get(path) } + if not to_search[1] then + return nil + end + + -- weird hybrid of depth- and breadth-first search for key, collect values in res. + local search_index = 1 + local search_size = 1 + while search_size > 0 do + for k, v in pairs(to_search[search_index]) do + if k == key then + res[#res + 1] = v + else + to_search[search_index + search_size] = v + search_size = search_size + 1 + end + end + search_index = search_index + 1 + search_size = search_size - 1 + end + + return res +end + +return { + new = new, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/directed_graph.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/directed_graph.lua new file mode 100644 index 00000000..4c906328 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/directed_graph.lua @@ -0,0 +1,263 @@ +local tbl_util = require("luasnip.util.table") +local autotable = require("luasnip.util.auto_table").autotable + +local DirectedGraph = {} + +-- set __index directly in DirectedGraph, otherwise each DirectedGraph-object would have its' +-- own metatable (one more table around), which would not be bad, but +-- unnecessary nonetheless. +DirectedGraph.__index = DirectedGraph + +local Vertex = {} +Vertex.__index = Vertex + +local function new_graph() + return setmetatable({ + -- all vertices of this graph. + vertices = {}, + }, DirectedGraph) +end +local function new_vertex() + return setmetatable({ + -- vertices this vertex has an edge from/to. + -- map[vert -> bool] + incoming_edge_verts = {}, + outgoing_edge_verts = {}, + }, Vertex) +end + +---Add new vertex to the DirectedGraph +---@return table: the generated vertex, to be used in `set_edge`, for example. +function DirectedGraph:add_vertex() + local vert = new_vertex() + table.insert(self.vertices, vert) + return vert +end + +---Remove vertex and its edges from DirectedGraph. +---@param v table: the vertex. +function DirectedGraph:clear_vertex(v) + if not vim.tbl_contains(self.vertices, v) then + -- vertex does not belong to this graph. Maybe throw error/make + -- condition known? + return + end + -- remove outgoing.. + for outgoing_edge_vert, _ in pairs(v.outgoing_edge_verts) do + self:clear_edge(v, outgoing_edge_vert) + end + -- ..and incoming edges with v from the graph. + for incoming_edge_vert, _ in pairs(v.incoming_edge_verts) do + self:clear_edge(incoming_edge_vert, v) + end +end + +---Add edge from v1 to v2 +---@param v1 table: vertex in the graph. +---@param v2 table: vertex in the graph. +function DirectedGraph:set_edge(v1, v2) + if v1.outgoing_edge_verts[v2] then + -- the edge already exists. Don't return an error, for now. + return + end + -- link vertices. + v1.outgoing_edge_verts[v2] = true + v2.incoming_edge_verts[v1] = true +end + +---Remove edge from v1 to v2 +---@param v1 table: vertex in the graph. +---@param v2 table: vertex in the graph. +function DirectedGraph:clear_edge(v1, v2) + assert(v1.outgoing_edge_verts[v2], "nonexistent edge cannot be removed.") + -- unlink vertices. + v1.outgoing_edge_verts[v2] = nil + v2.incoming_edge_verts[v1] = nil +end + +---Find and return verts with indegree 0. +---@param graph table: graph. +---@return table of vertices. +local function source_verts(graph) + local indegree_0_verts = {} + for _, vert in ipairs(graph.vertices) do + if vim.tbl_count(vert.incoming_edge_verts) == 0 then + table.insert(indegree_0_verts, vert) + end + end + return indegree_0_verts +end + +---Copy graph. +---@param graph table: graph. +---@return table,table: copied graph and table for mapping copied node to +---original node(original_vert[some_vert_from_copy] -> corresponding original +---vert). +local function graph_copy(graph) + local copy = vim.deepcopy(graph) + local original_vert = {} + for i, copy_vert in ipairs(copy.vertices) do + original_vert[copy_vert] = graph.vertices[i] + end + return copy, original_vert +end + +---Generate a (it's not necessarily unique) topological sorting of this graphs +---vertices. +---https://en.wikipedia.org/wiki/Topological_sorting, this uses Kahn's Algorithm. +---@return table|nil: sorted vertices of this graph, nil if there is no +---topological sorting (eg. if the graph has a cycle). +function DirectedGraph:topological_sort() + local sorting = {} + + -- copy self so edges can be removed without affecting the real graph. + local graph, original_vert = graph_copy(self) + + -- find vertices without incoming edges. + -- invariant: at the end of each step, sources contains all vertices + -- without incoming edges. + local sources = source_verts(graph) + while #sources > 0 do + -- pop v from sources. + local v = sources[#sources] + sources[#sources] = nil + + -- v has no incoming edges, it can be next in the sorting. + -- important!! don't insert v, insert the corresponding vertex from the + -- original graph. The copied vertices are not known outside this + -- function (alternative: maybe return indices in graph.vertices?). + table.insert(sorting, original_vert[v]) + + -- find vertices which, if v is removed from graph, have no more incoming edges. + -- Those are sources after v is removed. + for outgoing_edge_vert, _ in pairs(v.outgoing_edge_verts) do + -- there is one edge, it has to be from v. + if vim.tbl_count(outgoing_edge_vert.incoming_edge_verts) == 1 then + table.insert(sources, outgoing_edge_vert) + end + end + + -- finally: remove v from graph and sources. + graph:clear_vertex(v) + end + + if #sorting ~= #self.vertices then + -- error: the sorting does not contain all vertices -> the graph has a cycle. + return nil + end + return sorting +end + +-- return all vertices reachable from this one. +function DirectedGraph:connected_component(vert, edge_direction) + local outgoing_vertices_field = edge_direction == "Backward" + and "incoming_edge_verts" + or "outgoing_edge_verts" + + local visited = {} + local to_visit = { [vert] = true } + + -- get any value in table. + local next_vert, _ = next(to_visit, nil) + while next_vert do + to_visit[next_vert] = nil + visited[next_vert] = true + + for neighbor, _ in pairs(next_vert[outgoing_vertices_field]) do + if not visited[neighbor] then + to_visit[neighbor] = true + end + end + + next_vert, _ = next(to_visit, nil) + end + + return tbl_util.set_to_list(visited) +end + +-- Very useful to have a graph where vertices are associated with some label. +-- This just proxies DirectedGraph and swaps labels and vertices in +-- parameters/return-values. +local LabeledDigraph = {} +LabeledDigraph.__index = LabeledDigraph + +local function new_labeled_graph() + return setmetatable({ + graph = new_graph(), + label_to_vert = {}, + vert_to_label = {}, + -- map label -> origin-vert -> dest-vert + label_to_verts = autotable(3, { warn = false }), + -- map edge (origin,dest) to set of labels. + verts_to_label = autotable(3, { warn = false }), + }, LabeledDigraph) +end + +function LabeledDigraph:set_vertex(label) + if self.label_to_vert[label] then + -- don't add same label again. + return + end + + local vert = self.graph:add_vertex() + self.label_to_vert[label] = vert + self.vert_to_label[vert] = label +end + +function LabeledDigraph:set_edge(lv1, lv2, edge_label) + -- ensure vertices exist. + self:set_vertex(lv1) + self:set_vertex(lv2) + + if self.verts_to_label[lv1][lv2][edge_label] then + -- edge exists, do nothing. + return + end + + -- determine before setting the lv1-lv2-edge. + local other_edge_exists = next(self.verts_to_label[lv1][lv2], nil) ~= nil + + -- store both associations; + self.verts_to_label[lv1][lv2][edge_label] = true + self.label_to_verts[edge_label][lv1][lv2] = true + + if other_edge_exists then + -- there already exists an entry for this edge, no need to add it to + -- the graph. + return + end + self.graph:set_edge(self.label_to_vert[lv1], self.label_to_vert[lv2]) +end + +function LabeledDigraph:clear_edge(lv1, lv2, ledge) + if not self.verts_to_label[lv1][lv2][ledge] then + -- edge does not exist, do nothing. + return + end + + self.verts_to_label[lv1][lv2][ledge] = nil + if next(self.verts_to_label[lv1][lv2]) == nil then + -- removed last edge between v1, v2 -> remove edge in graph. + self.graph:clear_edge(self.label_to_vert[lv1], self.label_to_vert[lv2]) + end +end +function LabeledDigraph:clear_edges(label) + for lv1, lv2 in pairs(self.label_to_verts[label]) do + self:clear_edge(lv1, lv2, label) + end + -- set to nil, not {}, so autotable can work its magic. + self.label_to_verts[label] = nil +end + +function LabeledDigraph:connected_component(lv, edge_direction) + self:set_vertex(lv) + + return vim.tbl_map(function(v) + return self.vert_to_label[v] + end, self.graph:connected_component(self.label_to_vert[lv], edge_direction)) +end + +return { + new = new_graph, + new_labeled = new_labeled_graph, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/environ.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/environ.lua new file mode 100644 index 00000000..bd73d1a2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/environ.lua @@ -0,0 +1,188 @@ +local builtin_namespace = require("luasnip.util._builtin_vars") + +local function tbl_to_lazy_env(tbl) + local function wrapper(varname) + local val_ = tbl[varname] + if type(val_) == "function" then + return val_() + end + return val_ + end + + return wrapper +end + +local namespaces = {} + +-- Namespaces allow users to define their own environmet variables +local function _resolve_namespace_var(full_varname) + local parts = vim.split(full_varname, "_") + local nmsp = namespaces[parts[1]] + + local varname + -- Is safe to fallback to the buitin-unnamed namespace as the checks in _env_namespace + -- don't allow overriding those vars + if nmsp then + varname = full_varname:sub(#parts[1] + 2) + else + nmsp = namespaces[""] + varname = full_varname + end + return nmsp, varname +end + +local Environ = {} + +function Environ.is_table(var_fullname) + local nmsp, varname = _resolve_namespace_var(var_fullname) + ---@diagnostic disable-next-line: need-check-nil + return nmsp.is_table(varname) +end + +function Environ:new(info, o) + o = o or {} + setmetatable(o, self) + vim.list_extend(info, info.pos) -- For compatibility with old user defined namespaces + + for ns_name, ns in pairs(namespaces) do + local eager_vars = {} + if ns.init then + eager_vars = ns.init(info) + end + for _, eager in ipairs(ns.eager) do + if not eager_vars[eager] then + eager_vars[eager] = ns.vars(eager) + end + end + + local prefix = "" + if ns_name ~= "" then + prefix = ns_name .. "_" + end + + for name, val in pairs(eager_vars) do + name = prefix .. name + rawset(o, name, val) + end + end + return o +end + +local builtin_ns_names = vim.inspect(vim.tbl_keys(builtin_namespace.builtin_ns)) + +local function _env_namespace(name, opts) + assert( + opts and type(opts) == "table", + ("Your opts for '%s' has to be a table"):format(name) + ) + assert( + opts.init or opts.vars, + ("Your opts for '%s' needs init or vars"):format(name) + ) + + -- namespace.eager → ns.vars + assert( + not opts.eager or opts.vars, + ("Your opts for %s can't set a `eager` field without the `vars` one"):format( + name + ) + ) + + opts.eager = opts.eager or {} + local multiline_vars = opts.multiline_vars or false + + local type_of_it = type(multiline_vars) + + assert( + type_of_it == "table" + or type_of_it == "boolean" + or type_of_it == "function", + ("Your opts for %s can't have `multiline_vars` of type %s"):format( + name, + type_of_it + ) + ) + + -- If type is function we don't have to override it + if type_of_it == "table" then + local is_table_set = {} + + for _, key in ipairs(multiline_vars) do + is_table_set[key] = true + end + + opts.is_table = function(key) + return is_table_set[key] or false + end + elseif type_of_it == "boolean" then + opts.is_table = function(_) + return multiline_vars + end + else -- is a function + opts.is_table = multiline_vars + end + + if opts.vars and type(opts.vars) == "table" then + opts.vars = tbl_to_lazy_env(opts.vars) + end + + namespaces[name] = opts +end + +_env_namespace("", builtin_namespace) + +-- The exposed api checks for the names to avoid accidental overrides +function Environ.env_namespace(name, opts) + assert( + name:match("^[a-zA-Z][a-zA-Z0-9]*$"), + ("You can't create a namespace with name '%s' it has to contain only and at least a non alpha-numeric character"):format( + name + ) + ) + assert( + not builtin_namespace.builtin_ns[name], + ("You can't create a namespace with name '%s' because is one one of %s"):format( + name, + builtin_ns_names + ) + ) + + _env_namespace(name, opts) +end + +function Environ:__index(key) + local nmsp, varname = _resolve_namespace_var(key) + ---@diagnostic disable-next-line: need-check-nil + local val = nmsp.vars(varname) + + rawset(self, key, val) + + return val +end + +function Environ:override(env, new_env) + for k, v in pairs(new_env) do + env[k] = v + end +end + +local fake_env = { + __index = function(tbl, key) + local var + if Environ.is_table(key) then + var = { "$" .. key } + else + var = "$" .. key + end + rawset(tbl, key, var) + return var + end, +} + +function Environ.fake() + local o = {} + setmetatable(o, fake_env) + return o +end + +return Environ diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/events.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/events.lua new file mode 100644 index 00000000..a7ba52d4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/events.lua @@ -0,0 +1,18 @@ +local node_names = require("luasnip.util.types").names_pascal_case + +return { + enter = 1, + leave = 2, + change_choice = 3, + pre_expand = 4, + to_string = function(node_type, event_id) + if event_id == 3 then + return "ChangeChoice" + elseif event_id == 4 then + return "PreExpand" + else + return node_names[node_type] + .. (event_id == 1 and "Enter" or "Leave") + end + end, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/ext_opts.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/ext_opts.lua new file mode 100644 index 00000000..109bd6fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/ext_opts.lua @@ -0,0 +1,159 @@ +-- eventually turn ext_opts into proper objects, mainly for +-- default-construction eg. assured `complete`. +-- +-- child_*-functions perform the same operation as theiry non-child +-- counterparts, but on a collection (eg. +-- `{[types.insertNode={...}, [types.textNode]= {...}]}`) of ext_opts. + +local types = require("luasnip.util.types") + +-- vim.tbl_extend always creates a new table, but doesn't accept nil, so we +-- always pass this empty table, which will (has to!) stay empty. +local shared_empty_table = {} + +local states = { + "active", + "passive", + "snippet_passive", + "visited", + "unvisited", +} + +-- opts: child_ext_opts, have to have hl_group set for all combinations of +-- node-type and active,passive,snippet_passive,visited,unvisited. +local function clear_invalid(opts) + --stylua: ignore start + for _, node_type in pairs(types.node_types) do + for _, state in ipairs(states) do + local state_hl_group = opts[node_type][state].hl_group + + opts[node_type][state].hl_group = + vim.fn.hlexists(state_hl_group) == 1 and state_hl_group + or nil + end + end + --stylua: ignore end +end + +local function _complete_ext_opts(ext_opts) + if not ext_opts then + ext_opts = {} + end + ext_opts.snippet_passive = ext_opts.snippet_passive or {} + ext_opts.passive = vim.tbl_extend( + "keep", + ext_opts.passive or shared_empty_table, + ext_opts.snippet_passive or shared_empty_table + ) + -- both unvisited and visited inherit from passive. + ext_opts.unvisited = vim.tbl_extend( + "keep", + ext_opts.unvisited or shared_empty_table, + ext_opts.passive or shared_empty_table + ) + ext_opts.visited = vim.tbl_extend( + "keep", + ext_opts.visited or shared_empty_table, + ext_opts.passive or shared_empty_table + ) + -- active inherits from visited. + ext_opts.active = vim.tbl_extend( + "keep", + ext_opts.active or shared_empty_table, + ext_opts.visited or shared_empty_table + ) + + for _, state in ipairs(states) do + --stylua: ignore start + if ext_opts[state].hl_group and not + ext_opts[state].priority then + ext_opts[state].priority = 0 + end + --stylua: ignore end + end + + return ext_opts +end + +-- active inherits unset values from passive, which in turn inherits from +-- snippet_passive. +-- Also make sure that all keys have a table, and are not nil! +local function child_complete(ext_opts) + for _, node_type in pairs(types.node_types) do + ext_opts[node_type] = _complete_ext_opts(ext_opts[node_type]) + end + ext_opts.base_prio = 0 + + return ext_opts +end + +local function complete(ext_opts) + _complete_ext_opts(ext_opts) + ext_opts.base_prio = 0 + + return ext_opts +end + +-- in-place adds opts of b to a, doesn't override. +-- a/b: completed ext_opts, not nil. +local function extend(opts_a, opts_b) + for _, state in ipairs(states) do + opts_a[state] = vim.tbl_extend("keep", opts_a[state], opts_b[state]) + end + + return opts_a +end + +-- in-place adds opts of b to a, doesn't override. +-- a/b: completed child_ext_opts, not nil. +local function child_extend(opts_a, opts_b) + for _, node_type in ipairs(types.node_types) do + extend(opts_a[node_type], opts_b[node_type]) + end + + return opts_a +end + +local function increase_prio(opts, inc) + -- increase only if there is a priority. + for _, state in ipairs(states) do + opts[state].priority = opts[state].priority + and (opts[state].priority + inc) + end +end + +-- ext_opts-priorities are defined relative to some base-priority. +-- As nvim_api_buf_set_extmark takes absolute values only, we have to +-- set the absolute priorities, which can vary depending on nesting-level +-- of a given snippet, during runtime, by increasing the relative priorities by +-- either the conf.base_prio or the base-prio used in the previous nesting-level. +local function set_abs_prio(opts, new_base_prio) + -- undo previous increase. + -- base_prio is initialized with 0. + local prio_offset = new_base_prio - opts.base_prio + opts.base_prio = new_base_prio + increase_prio(opts, prio_offset) + + return opts +end + +local function child_set_abs_prio(opts, new_base_prio) + -- undo previous increase. + -- base_prio is initialized with 0. + local prio_offset = new_base_prio - opts.base_prio + opts.base_prio = new_base_prio + for _, node_type in ipairs(types.node_types) do + increase_prio(opts[node_type], prio_offset) + end + return opts +end + +return { + clear_invalid = clear_invalid, + complete = complete, + child_complete = child_complete, + extend = extend, + child_extend = child_extend, + set_abs_prio = set_abs_prio, + child_set_abs_prio = child_set_abs_prio, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/extend_decorator.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/extend_decorator.lua new file mode 100644 index 00000000..f0723819 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/extend_decorator.lua @@ -0,0 +1,82 @@ +local M = {} + +-- map fn -> {arg_indx = int, extend = fn}[] +local function_properties = setmetatable({}, { __mode = "k" }) + +local function default_extend(arg, extend) + return vim.tbl_extend("keep", arg or {}, extend or {}) +end + +---Create a new decorated version of `fn`. +---@param fn The function to create a decorator for. +---@vararg The values to extend with. These should match the descriptions passed +---in `register`: +---```lua +---local function somefn(arg1, arg2, opts1, opts2) +---... +---end +---register(somefn, {arg_indx=4}, {arg_indx=3}) +---apply(somefn, +--- {key = "opts2 is extended with this"}, +--- {key = "and opts1 with this"}) +---``` +---@return function: The decorated function. +function M.apply(fn, ...) + local extend_properties = function_properties[fn] + assert( + extend_properties, + "Cannot extend this function, it was not registered! Check :h luasnip-extend_decorator for more infos." + ) + + local extend_values = { ... } + + local decorated_fn = function(...) + local direct_args = { ... } + + -- override values of direct argument. + for i, ep in ipairs(extend_properties) do + local arg_indx = ep.arg_indx + + -- still allow overriding with directly-passed keys. + direct_args[arg_indx] = + ep.extend(direct_args[arg_indx], extend_values[i]) + end + + -- important: http://www.lua.org/manual/5.3/manual.html#3.4 + -- Passing arguments after the results from `unpack` would mess all this + -- up. + return fn(unpack(direct_args)) + end + + -- we know how to extend the decorated function! + function_properties[decorated_fn] = extend_properties + + return decorated_fn +end + +---Prepare a function for usage with extend_decorator. +---To create a decorated function which extends `opts`-style tables passed to it, we need to know +--- 1. which parameter-position the opts are in and +--- 2. how to extend them. +---@param fn function: the function that should be registered. +---@vararg tables. Each describes how to extend one parameter to `fn`. +---The tables accept the following keys: +--- - arg_indx, number (required): the position of the parameter to override. +--- - extend, fn(arg, extend_value) -> effective_arg (optional): this function +--- is used to extend the args passed to the decorated function. +--- It defaults to a function which just extends the the arg-table with the +--- extend-table. +--- This extend-behaviour is adaptable to accomodate `s`, where the first +--- argument may be string or table. +function M.register(fn, ...) + local fn_eps = { ... } + + -- make sure ep.extend is set. + for _, ep in ipairs(fn_eps) do + ep.extend = ep.extend or default_extend + end + + function_properties[fn] = fn_eps +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/functions.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/functions.lua new file mode 100644 index 00000000..3fc1ad7d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/functions.lua @@ -0,0 +1,49 @@ +local sn = require("luasnip.nodes.snippet").SN +local t = require("luasnip.nodes.textNode").T + +return { + var = function(_, _, node, text) + local v = node.parent.snippet.env[text] + if type(v) == "table" then + -- Avoid issues with empty vars + if #v > 0 then + return v + else + return { "" } + end + else + return { v } + end + end, + better_var = function(varname) + return function(_, parent) + local v = parent.snippet.env[varname] + if type(v) == "table" then + -- Avoid issues with empty vars + if #v > 0 then + return v + else + return { "" } + end + else + return { v } + end + end + end, + eval_vim_dynamic = function(vimstring) + return function() + -- 'echo'd string is returned to lua. + return sn(nil, { + t( + vim.split( + vim.api.nvim_exec("echo " .. vimstring, true), + "\n" + ) + ), + }) + end + end, + copy = function(args) + return args[1] + end, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/jsonc.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/jsonc.lua new file mode 100644 index 00000000..d72dad5a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/jsonc.lua @@ -0,0 +1,469 @@ +-- Taken from https://github.com/actboy168/json.lua + +-- MIT License +-- +-- Copyright (c) 2020 actboy168 +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. + +local type = type +local next = next +local error = error +local tonumber = tonumber +local string_char = string.char +local string_byte = string.byte +local string_find = string.find +local string_match = string.match +local string_gsub = string.gsub +local string_sub = string.sub +local string_format = string.format + +local utf8_char + +if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.2" then + local math_floor = math.floor + function utf8_char(c) + if c <= 0x7f then + return string_char(c) + elseif c <= 0x7ff then + return string_char(math_floor(c / 64) + 192, c % 64 + 128) + elseif c <= 0xffff then + return string_char( + math_floor(c / 4096) + 224, + math_floor(c % 4096 / 64) + 128, + c % 64 + 128 + ) + elseif c <= 0x10ffff then + return string_char( + math_floor(c / 262144) + 240, + math_floor(c % 262144 / 4096) + 128, + math_floor(c % 4096 / 64) + 128, + c % 64 + 128 + ) + end + error(string_format("invalid UTF-8 code '%x'", c)) + end +else + utf8_char = utf8.char +end + +local encode_escape_map = { + ['"'] = '\\"', + ["\\"] = "\\\\", + ["/"] = "\\/", + ["\b"] = "\\b", + ["\f"] = "\\f", + ["\n"] = "\\n", + ["\r"] = "\\r", + ["\t"] = "\\t", +} + +local decode_escape_set = {} +local decode_escape_map = {} +for k, v in next, encode_escape_map do + decode_escape_map[v] = k + decode_escape_set[string_byte(v, 2)] = true +end + +local statusBuf +local statusPos +local statusTop +local statusAry = {} +local statusRef = {} + +local function find_line() + local line = 1 + local pos = 1 + while true do + local f, _, nl1, nl2 = string_find(statusBuf, "([\n\r])([\n\r]?)", pos) + if not f then + return line, statusPos - pos + 1 + end + local newpos = f + ((nl1 == nl2 or nl2 == "") and 1 or 2) + if newpos > statusPos then + return line, statusPos - pos + 1 + end + pos = newpos + line = line + 1 + end +end + +local function decode_error(msg) + error(string_format("ERROR: %s at line %d col %d", msg, find_line()), 2) +end + +local function get_word() + return string_match(statusBuf, "^[^ \t\r\n%]},]*", statusPos) +end + +local function skip_comment(b) + if + b ~= 47 --[[ '/' ]] + then + return + end + local c = string_byte(statusBuf, statusPos + 1) + if + c == 42 --[[ '*' ]] + then + -- block comment + local pos = string_find(statusBuf, "*/", statusPos) + if pos then + statusPos = pos + 2 + else + statusPos = #statusBuf + 1 + end + return true + elseif + c == 47 --[[ '/' ]] + then + -- line comment + local pos = string_find(statusBuf, "[\r\n]", statusPos) + if pos then + statusPos = pos + else + statusPos = #statusBuf + 1 + end + return true + end +end + +local function next_byte() + local pos = string_find(statusBuf, "[^ \t\r\n]", statusPos) + if pos then + statusPos = pos + local b = string_byte(statusBuf, pos) + if not skip_comment(b) then + return b + end + return next_byte() + end + return -1 +end + +local function decode_unicode_surrogate(s1, s2) + return utf8_char( + 0x10000 + + (tonumber(s1, 16) - 0xd800) * 0x400 + + (tonumber(s2, 16) - 0xdc00) + ) +end + +local function decode_unicode_escape(s) + return utf8_char(tonumber(s, 16)) +end + +local function decode_string() + local has_unicode_escape = false + local has_escape = false + local i = statusPos + 1 + while true do + i = string_find(statusBuf, '[%z\1-\31\\"]', i) + if not i then + decode_error("expected closing quote for string") + end + local x = string_byte(statusBuf, i) + if x < 32 then + statusPos = i + decode_error("control character in string") + end + if + x == 34 --[[ '"' ]] + then + local s = string_sub(statusBuf, statusPos + 1, i - 1) + if has_unicode_escape then + s = string_gsub( + string_gsub( + s, + "\\u([dD][89aAbB]%x%x)\\u([dD][c-fC-F]%x%x)", + decode_unicode_surrogate + ), + "\\u(%x%x%x%x)", + decode_unicode_escape + ) + end + if has_escape then + s = string_gsub(s, "\\.", decode_escape_map) + end + statusPos = i + 1 + return s + end + --assert(x == 92 --[[ "\\" ]]) + local nx = string_byte(statusBuf, i + 1) + if + nx == 117 --[[ "u" ]] + then + if not string_match(statusBuf, "^%x%x%x%x", i + 2) then + statusPos = i + decode_error("invalid unicode escape in string") + end + has_unicode_escape = true + i = i + 6 + else + if not decode_escape_set[nx] then + statusPos = i + decode_error( + "invalid escape char '" + .. (nx and string_char(nx) or "") + .. "' in string" + ) + end + has_escape = true + i = i + 2 + end + end +end + +local function decode_number() + local num, c = + string_match(statusBuf, "^([0-9]+%.?[0-9]*)([eE]?)", statusPos) + if + not num or string_byte(num, -1) == 0x2E --[[ "." ]] + then + decode_error("invalid number '" .. get_word() .. "'") + end + if c ~= "" then + num = string_match( + statusBuf, + "^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},/]", + statusPos + ) + if not num then + decode_error("invalid number '" .. get_word() .. "'") + end + end + statusPos = statusPos + #num + return tonumber(num) +end + +local function decode_number_zero() + local num, c = string_match(statusBuf, "^(.%.?[0-9]*)([eE]?)", statusPos) + if + not num + or string_byte(num, -1) == 0x2E --[[ "." ]] + or string_match(statusBuf, "^.[0-9]+", statusPos) + then + decode_error("invalid number '" .. get_word() .. "'") + end + if c ~= "" then + num = string_match( + statusBuf, + "^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},/]", + statusPos + ) + if not num then + decode_error("invalid number '" .. get_word() .. "'") + end + end + statusPos = statusPos + #num + return tonumber(num) +end + +local function decode_number_negative() + statusPos = statusPos + 1 + local c = string_byte(statusBuf, statusPos) + if c then + if c == 0x30 then + return -decode_number_zero() + elseif c > 0x30 and c < 0x3A then + return -decode_number() + end + end + decode_error("invalid number '" .. get_word() .. "'") +end + +local function decode_true() + if string_sub(statusBuf, statusPos, statusPos + 3) ~= "true" then + decode_error("invalid literal '" .. get_word() .. "'") + end + statusPos = statusPos + 4 + return true +end + +local function decode_false() + if string_sub(statusBuf, statusPos, statusPos + 4) ~= "false" then + decode_error("invalid literal '" .. get_word() .. "'") + end + statusPos = statusPos + 5 + return false +end + +local function decode_null() + if string_sub(statusBuf, statusPos, statusPos + 3) ~= "null" then + decode_error("invalid literal '" .. get_word() .. "'") + end + statusPos = statusPos + 4 + return vim.NIL +end + +local function decode_array() + statusPos = statusPos + 1 + local res = {} + local chr = next_byte() + if + chr == 93 --[[ ']' ]] + then + statusPos = statusPos + 1 + return res + end + statusTop = statusTop + 1 + statusAry[statusTop] = true + statusRef[statusTop] = res + return res +end + +local function decode_object() + statusPos = statusPos + 1 + local res = {} + local chr = next_byte() + if + chr == 125 --[[ ']' ]] + then + statusPos = statusPos + 1 + return vim.empty_dict() + end + statusTop = statusTop + 1 + statusAry[statusTop] = false + statusRef[statusTop] = res + return res +end + +local decode_uncompleted_map = { + [string_byte('"')] = decode_string, + [string_byte("0")] = decode_number_zero, + [string_byte("1")] = decode_number, + [string_byte("2")] = decode_number, + [string_byte("3")] = decode_number, + [string_byte("4")] = decode_number, + [string_byte("5")] = decode_number, + [string_byte("6")] = decode_number, + [string_byte("7")] = decode_number, + [string_byte("8")] = decode_number, + [string_byte("9")] = decode_number, + [string_byte("-")] = decode_number_negative, + [string_byte("t")] = decode_true, + [string_byte("f")] = decode_false, + [string_byte("n")] = decode_null, + [string_byte("[")] = decode_array, + [string_byte("{")] = decode_object, +} +local function unexpected_character() + decode_error( + "unexpected character '" + .. string_sub(statusBuf, statusPos, statusPos) + .. "'" + ) +end +local function unexpected_eol() + decode_error("unexpected character ''") +end + +local decode_map = {} +for i = 0, 255 do + decode_map[i] = decode_uncompleted_map[i] or unexpected_character +end +decode_map[-1] = unexpected_eol + +local function decode() + return decode_map[next_byte()]() +end + +local function decode_item() + local top = statusTop + local ref = statusRef[top] + if statusAry[top] then + ref[#ref + 1] = decode() + else + local key = decode_string() + if + next_byte() ~= 58 --[[ ':' ]] + then + decode_error("expected ':'") + end + statusPos = statusPos + 1 + ref[key] = decode() + end + if top == statusTop then + repeat + local chr = next_byte() + statusPos = statusPos + 1 + if + chr == 44 --[[ "," ]] + then + local c = next_byte() + if statusAry[statusTop] then + if + c ~= 93 --[[ "]" ]] + then + return + end + else + if + c ~= 125 --[[ "}" ]] + then + return + end + end + statusPos = statusPos + 1 + else + if statusAry[statusTop] then + if + chr ~= 93 --[[ "]" ]] + then + decode_error("expected ']' or ','") + end + else + if + chr ~= 125 --[[ "}" ]] + then + decode_error("expected '}' or ','") + end + end + end + statusTop = statusTop - 1 + until statusTop == 0 + end +end + +local M = {} + +function M.decode(str) + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + if str == "" then + error("attempted to decode an empty string") + end + statusBuf = str + statusPos = 1 + statusTop = 0 + if next_byte() == -1 then + return vim.NIL + end + local res = decode() + while statusTop > 0 do + decode_item() + end + if string_find(statusBuf, "[^ \t\r\n]", statusPos) then + decode_error("trailing garbage") + end + return res +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/jsregexp.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/jsregexp.lua new file mode 100644 index 00000000..54be37ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/jsregexp.lua @@ -0,0 +1,41 @@ +local Path = require("luasnip.util.path") + +-- neovim-loader does not handle module-names with dots correctly, so for +-- jsregexp-0.0.6, the call to require("jsregexp.core") in jsregexp.lua errors +-- even if the library is in rtp. +-- Resolve path to jsregexp.so manually, and loadlib it in preload (and remove +-- preload after requires are done and have failed/worked). + +-- omit "@". +local this_file = debug.getinfo(1).source:sub(2) +local repo_dir = vim.fn.fnamemodify(this_file, ":h:h:h:h") +local jsregexp_core_path = Path.join(repo_dir, "deps", "luasnip-jsregexp.so") + +-- rather gracefully, if the path does not exist, or loadlib can't do its job +-- for some other reason, the preload will be set to nil, ie not be set. +-- +-- This means we don't hinder a regularly installed 0.0.6-jsregexp-library, +-- since its `require("jsregexp.core")` will be unaffected. +package.preload["jsregexp.core"] = + package.loadlib(jsregexp_core_path, "luaopen_jsregexp_core") + +-- jsregexp: first try loading the version installed by luasnip, then global ones. +local jsregexp_ok, jsregexp = pcall(require, "luasnip-jsregexp") +if not jsregexp_ok then + jsregexp_ok, jsregexp = pcall(require, "jsregexp") +end + +-- don't want to affect other requires. +package.preload["jsregexp.core"] = nil + +if not jsregexp_ok then + return false +end + +-- detect version, and return compile-function. +-- 0.0.6-compile_safe and 0.0.5-compile behave the same, ie. nil, err on error. +if jsregexp.compile_safe then + return jsregexp.compile_safe +else + return jsregexp.compile +end diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/lazy_table.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/lazy_table.lua new file mode 100644 index 00000000..6c5e4821 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/lazy_table.lua @@ -0,0 +1,13 @@ +return function(lazy_t, lazy_defs) + return setmetatable(lazy_t, { + __index = function(t, k) + local v = lazy_defs[k] + if v then + local v_resolved = v() + rawset(t, k, v_resolved) + return v_resolved + end + return nil + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/log.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/log.lua new file mode 100644 index 00000000..ad4045da --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/log.lua @@ -0,0 +1,123 @@ +local util = require("luasnip.util.util") + +-- older neovim-versions (even 0.7.2) do not have stdpath("log"). +local logpath_ok, logpath = pcall(vim.fn.stdpath, "log") +if not logpath_ok then + logpath = vim.fn.stdpath("cache") +end + +-- just to be sure this dir exists. +-- 448 = 0700 +vim.loop.fs_mkdir(logpath, 448) + +local log_location = logpath .. "/luasnip.log" +local log_old_location = logpath .. "/luasnip.log.old" + +local luasnip_log_fd = vim.loop.fs_open( + log_location, + -- only append. + "a", + -- 420 = 0644 + 420 +) + +local function log_line_append(msg) + msg = msg:gsub("\n", "\n | ") + vim.loop.fs_write(luasnip_log_fd, msg .. "\n") +end + +if not luasnip_log_fd then + -- print a warning + print( + ("LuaSnip: could not open log at %s. Not logging for this session."):format( + log_location + ) + ) + -- make log_line_append do nothing. + log_line_append = util.nop +else + -- if log_fd found, check if log should be rotated. + local logsize = vim.loop.fs_fstat(luasnip_log_fd).size + if logsize > 10 * 2 ^ 20 then + -- logsize > 10MiB: + -- move log -> old log, start new log. + vim.loop.fs_rename(log_location, log_old_location) + luasnip_log_fd = vim.loop.fs_open( + log_location, + -- only append. + "a", + -- 420 = 0644 + 420 + ) + end +end + +local M = {} + +local log = { + error = function(msg) + log_line_append("ERROR | " .. msg) + end, + warn = function(msg) + log_line_append("WARN | " .. msg) + end, + info = function(msg) + log_line_append("INFO | " .. msg) + end, + debug = function(msg) + log_line_append("DEBUG | " .. msg) + end, +} + +-- functions copied directly by deepcopy. +-- will be initialized later on, by set_loglevel. +local effective_log + +-- levels sorted by importance, descending. +local loglevels = { "error", "warn", "info", "debug" } + +-- special key none disable all logging. +function M.set_loglevel(target_level) + local target_level_indx = util.indx_of(loglevels, target_level) + if target_level == "none" then + target_level_indx = 0 + end + + assert(target_level_indx ~= nil, "invalid level!") + + -- reset effective loglevels, set those with importance higher than + -- target_level, disable (nop) those with lower. + effective_log = {} + for i = 1, target_level_indx do + effective_log[loglevels[i]] = log[loglevels[i]] + end + for i = target_level_indx + 1, #loglevels do + effective_log[loglevels[i]] = util.nop + end +end + +function M.new(module_name) + local module_log = {} + for name, _ in pairs(log) do + module_log[name] = function(msg, ...) + -- don't immediately get the referenced function, we'd like to + -- allow changing the loglevel on-the-fly. + effective_log[name](module_name .. ": " .. msg:format(...)) + end + end + return module_log +end + +function M.open() + vim.cmd(("tabnew %s"):format(log_location)) +end + +-- to verify log is working. +function M.ping() + log_line_append(("PONG | pong! (%s)"):format(os.date())) +end + +-- set default-loglevel. +M.set_loglevel("warn") + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/mark.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/mark.lua new file mode 100644 index 00000000..daf9b074 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/mark.lua @@ -0,0 +1,208 @@ +local session = require("luasnip.session") + +local Mark = {} + +function Mark:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o +end + +-- opts just like in nvim_buf_set_extmark. +local function mark(pos_begin, pos_end, opts) + return Mark:new({ + id = vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + pos_begin[1], + pos_begin[2], + -- override end_* in opts. + vim.tbl_extend( + "force", + opts, + { end_line = pos_end[1], end_col = pos_end[2] } + ) + ), + -- store opts here, can't be queried using nvim_buf_get_extmark_by_id. + opts = opts, + }) +end + +local function bytecol_to_utfcol(pos) + local line = vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false) + -- line[1]: get_lines returns table. + -- use utf16-index. + local utf16_indx, _ = vim.str_utfindex(line[1] or "", pos[2]) + return { pos[1], utf16_indx } +end + +function Mark:pos_begin_end() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = true } + ) + + return bytecol_to_utfcol({ mark_info[1], mark_info[2] }), + bytecol_to_utfcol({ mark_info[3].end_row, mark_info[3].end_col }) +end + +function Mark:pos_begin() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = false } + ) + + return bytecol_to_utfcol({ mark_info[1], mark_info[2] }) +end + +function Mark:pos_end() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = true } + ) + + return bytecol_to_utfcol({ mark_info[3].end_row, mark_info[3].end_col }) +end + +function Mark:pos_begin_end_raw() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = true } + ) + return { mark_info[1], mark_info[2] }, { + mark_info[3].end_row, + mark_info[3].end_col, + } +end + +function Mark:pos_begin_raw() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = false } + ) + return { mark_info[1], mark_info[2] } +end + +function Mark:copy_pos_gravs(opts) + local pos_beg, pos_end = self:pos_begin_end_raw() + opts.right_gravity = self.opts.right_gravity + opts.end_right_gravity = self.opts.end_right_gravity + return mark(pos_beg, pos_end, opts) +end + +-- opts just like in nvim_buf_set_extmark. +-- opts as first arg bcs. pos are pretty likely to stay the same. +function Mark:update(opts, pos_begin, pos_end) + -- if one is changed, the other is likely as well. + if not pos_begin then + pos_begin = old_pos_begin + if not pos_end then + pos_end = old_pos_end + end + end + -- override with new. + self.opts = vim.tbl_extend("force", self.opts, opts) + vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + pos_begin[1], + pos_begin[2], + vim.tbl_extend( + "force", + self.opts, + { id = self.id, end_line = pos_end[1], end_col = pos_end[2] } + ) + ) +end + +function Mark:set_opts(opts) + local pos_begin, pos_end = self:pos_begin_end_raw() + vim.api.nvim_buf_del_extmark(0, session.ns_id, self.id) + + self.opts = opts + -- set new extmark, current behaviour for updating seems inconsistent, + -- eg. gravs are reset, deco is kept. + self.id = vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + pos_begin[1], + pos_begin[2], + vim.tbl_extend( + "force", + opts, + { end_line = pos_end[1], end_col = pos_end[2] } + ) + ) +end + +function Mark:set_rgravs(rgrav_left, rgrav_right) + -- don't update if nothing would change. + if + self.opts.right_gravity ~= rgrav_left + or self.opts.end_right_gravity ~= rgrav_right + then + self.opts.right_gravity = rgrav_left + self.opts.end_right_gravity = rgrav_right + self:set_opts(self.opts) + end +end + +function Mark:get_rgrav(which) + if which == -1 then + return self.opts.right_gravity + else + return self.opts.end_right_gravity + end +end + +function Mark:set_rgrav(which, rgrav) + if which == -1 then + if self.opts.right_gravity == rgrav then + return + end + self.opts.right_gravity = rgrav + else + if self.opts.end_right_gravity == rgrav then + return + end + self.opts.end_right_gravity = rgrav + end + self:set_opts(self.opts) +end + +function Mark:get_endpoint(which) + -- simpler for now, look into perf here later. + local l, r = self:pos_begin_end_raw() + if which == -1 then + return l + else + return r + end +end + +-- change all opts except rgravs. +function Mark:update_opts(opts) + local opts_cp = vim.deepcopy(opts) + opts_cp.right_gravity = self.opts.right_gravity + opts_cp.end_right_gravity = self.opts.end_right_gravity + self:set_opts(opts_cp) +end + +function Mark:clear() + vim.api.nvim_buf_del_extmark(0, session.ns_id, self.id) +end + +return { + mark = mark, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/ast_parser.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/ast_parser.lua new file mode 100644 index 00000000..20f056a3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/ast_parser.lua @@ -0,0 +1,350 @@ +local ast_utils = require("luasnip.util.parser.ast_utils") +local Ast = require("luasnip.util.parser.neovim_ast") +local tNode = require("luasnip.nodes.textNode") +local iNode = require("luasnip.nodes.insertNode") +local fNode = require("luasnip.nodes.functionNode") +local cNode = require("luasnip.nodes.choiceNode") +local dNode = require("luasnip.nodes.dynamicNode") +local sNode = require("luasnip.nodes.snippet") +local functions = require("luasnip.util.functions") +local Environ = require("luasnip.util.environ") +local session = require("luasnip.session") +local util = require("luasnip.util.util") + +local M = {} + +local _split = function(s) + return vim.split(s, "\n", { plain = true }) +end + +local types = ast_utils.types + +local to_node + +local function fix_node_indices(nodes) + local used_nodes = {} + for _, node in ipairs(nodes) do + if node.pos and node.pos > 0 then + used_nodes[node.pos] = node + end + end + + for _, v, i in util.key_sorted_pairs(used_nodes) do + v.pos = i + end + return nodes +end + +local function ast2luasnip_nodes(ast_nodes) + local nodes = {} + for i, ast_node in ipairs(ast_nodes) do + nodes[i] = ast_node.parsed + end + + return fix_node_indices(nodes) +end + +local function var_func(ast) + local varname = ast.name + + local transform_func + if ast.transform then + transform_func = ast_utils.apply_transform(ast.transform) + else + transform_func = util.id + end + + return function(_, parent, _, variable_default) + local v = parent.snippet.env[varname] + local lines + if type(v) == "table" then + -- Avoid issues with empty vars + if #v > 0 then + lines = v + else + lines = { "" } + end + else + lines = { v } + end + + -- quicker than checking `lines` in some way. + if not v then + -- the variable is not defined: + -- insert the variable's name as a placeholder. + return sNode.SN(nil, { iNode.I(1, varname) }) + end + if #lines == 0 or (#lines == 1 and #lines[1] == 0) then + -- The variable is empty. + + -- default passed as user_arg, rationale described in + -- types.VARIABLE-to_node_func. + if variable_default then + return variable_default + else + -- lines might still just be {} (#lines == 0). + lines = { "" } + end + end + + -- v exists and has no default, return the (maybe modified) lines. + return sNode.SN(nil, { tNode.T(transform_func(lines)) }) + end +end + +local function copy_func(tabstop) + local transform_func + if tabstop.transform then + transform_func = ast_utils.apply_transform(tabstop.transform) + else + transform_func = util.id + end + return function(args) + return transform_func(args[1]) + end +end + +local function placeholder_func(_, parent, _, placeholder_snip) + local env = parent.snippet.env + -- is_interactive needs env to determine interactiveness. + -- env is passed through to all following is_interactive calls. + if not placeholder_snip:is_interactive(env) then + -- this placeholder only contains text or (transformed) + -- variables, so an insertNode can be generated from its + -- contents. + -- create new snippet that only contains the parsed snippetNode, so we + -- can `fake_expand` and `get_static_text()` it. + local snippet = sNode.S("", placeholder_snip) + + -- get active env from snippet. + snippet:fake_expand({ env = env }) + local iText = snippet:get_static_text() + + -- no need to un-escape iText, that was already done. + return sNode.SN(nil, iNode.I(1, iText)) + end + + return sNode.SN( + nil, + session.config.parser_nested_assembler(1, placeholder_snip) + ) +end + +---If this tabstop-node (CHOICE, TABSTOP or PLACEHOLDER) is a copy of another, +---set that up and return, otherwise return false. +---@param ast table: ast-node. +---@return boolean: whether the node is now parsed. +local function tabstop_node_copy_inst(ast) + local existing_tabstop_ast_node = ast.copies + if existing_tabstop_ast_node then + -- this tabstop is a mirror of an already-parsed tabstop/placeholder. + ast.parsed = + fNode.F(copy_func(ast), { existing_tabstop_ast_node.parsed }) + return true + end + return false +end +-- these actually create nodes from any AST. +local to_node_funcs = { + -- careful! this parses the snippet into a list of nodes, not a full snippet! + -- The table can then be passed to the regular snippet-constructors. + [types.SNIPPET] = function(ast, _) + ast.parsed = ast2luasnip_nodes(ast.children) + end, + [types.TEXT] = function(ast, _) + local text = _split(ast.esc) + ast.parsed = tNode.T(text) + end, + [types.CHOICE] = function(ast) + -- even choices may be copies. + if tabstop_node_copy_inst(ast) then + return + end + + local choices = {} + for i, choice in ipairs(ast.items) do + choices[i] = tNode.T(_split(choice)) + end + + ast.parsed = cNode.C(ast.tabstop, choices) + end, + [types.TABSTOP] = function(ast) + if tabstop_node_copy_inst(ast) then + return + end + -- tabstops don't have placeholder-text. + ast.parsed = iNode.I(ast.tabstop) + end, + [types.PLACEHOLDER] = function(ast, state) + if tabstop_node_copy_inst(ast) then + return + end + + local node + if #ast.children == 1 and ast.children[1].type == types.TEXT then + -- we cannot place a dynamicNode as $0. + -- But all valid ${0:some nodes here} contain just text inside + -- them, so this works :) + node = iNode.I(ast.tabstop, _split(ast.children[1].esc)) + else + local snip = sNode.SN(1, ast2luasnip_nodes(ast.children)) + node = dNode.D(ast.tabstop, placeholder_func, {}, { + -- pass snip here, again to preserve references to other tables. + user_args = { snip }, + }) + end + + ast.parsed = node + end, + [types.VARIABLE] = function(ast, state) + local var = ast.name + + local default + if ast.children then + default = sNode.SN(nil, ast2luasnip_nodes(ast.children)) + end + + local fn + local is_interactive_fn + if state.var_functions[var] then + fn, is_interactive_fn = unpack(state.var_functions[var]) + else + fn = var_func(ast) + -- override the regular `is_interactive` to accurately determine + -- whether the snippet produced by the dynamicNode is interactive + -- or not. This is important when a variable is wrapped inside a + -- placeholder: ${1:$TM_SELECTED_TEXT} + -- With variable-environments we cannot tell at parse-time whether + -- the dynamicNode will be just text, an insertNode or some other + -- nodes(the default), so that has to happen at runtime now. + is_interactive_fn = function(_, env) + local var_value = env[var] + + if not var_value then + -- inserts insertNode. + return true + end + + -- just wrap it for more uniformity. + if type(var_value) == "string" then + var_value = { var_value } + end + + if + (#var_value == 1 and #var_value[1] == 0) + or #var_value == 0 + then + -- var is empty, default is inserted. + -- if no default, it's not interactive (an empty string is inserted). + return default and default:is_interactive() + end + + -- variable is just inserted, not interactive. + return false + end + end + + local d = dNode.D(ast.potential_tabstop, fn, {}, { + -- TRICKY!!!! + -- Problem: if the default is passed to the dynamicNode-function via lambda-capture, the + -- copy-routine, which will run on expansion, cannot associate these + -- nodes inside the passed nodes with the ones that are inside the + -- snippet. + -- For example, if `default` contains a functionNode which relies on + -- an insertNode within the snippet, it has the insertNode as an + -- argnode stored inside it. During copy, the copied insertNode (eg + -- a pointer to it) has to be inserted at this position as well, + -- otherwise there might be bugs (the snippet thinks the argnode is + -- present, but it isn't). + -- + -- This means that these nodes may not be passed as a simple + -- lambda-capture (!!). + -- I don't really like this, it can lead to very subtle errors (not + -- in this instance, but needing to do this in general). + -- + -- TODO: think about ways to avoid this. OTOH, this is almost okay, + -- just needs to be documented a bit. + -- + -- `default` is potentially nil. + user_args = { default }, + }) + d.is_interactive = is_interactive_fn + + -- if the variable is preceded by \n, the indent is applied to + -- all lines of the variable (important for eg. TM_SELECTED_TEXT). + if ast.previous_text ~= nil and #ast.previous_text > 1 then + local last_line_indent = + ast.previous_text[#ast.previous_text]:match("^%s+$") + if last_line_indent then + -- TM_SELECTED_TEXT contains the indent of the selected + -- snippets, which leads to correct indentation if the + -- snippet is expanded at the position the text was removed + -- from. + -- This seems pretty stupid, but TM_SELECTED_TEXT is + -- desigend to be compatible with vscode. + -- Use SELECT_DEDENT insted. + -- stylua: ignore + local indentstring = var ~= "TM_SELECTED_TEXT" + and "$PARENT_INDENT" .. last_line_indent + or last_line_indent + + -- adjust current d's jump-position.. + d.pos = 1 + -- ..so it has the correct position when wrapped inside a + -- snippetNode. + d = sNode.ISN(ast.potential_tabstop, { d }, indentstring) + end + end + + ast.parsed = d + end, +} + +--- Converts any ast into luasnip-nodes. +--- Snippets return a table of nodes, those can be used like the return-value of `fmt`. +---@param ast table: AST, as generated by `require("vim.lsp._snippet").parse` +---@param state table: +--- - `var_functions`: table, string -> {dNode-fn, is_interactive_fn} +--- For now, only used when parsing snipmate-snippets. +---@return table: node corresponding to `ast`. +function to_node(ast, state) + if not Ast.is_node(ast) then + -- ast is not an ast (probably a luasnip-node), return it as-is. + return ast + end + return to_node_funcs[ast.type](ast, state) +end + +--- Converts any ast into usable nodes. +---@param ast table: AST, as generated by `require("vim.lsp._snippet").parse` +---@param state table: +--- - `var_functions`: table, string -> {dNode-fn, is_interactive_fn} +--- For now, only used when parsing snipmate-snippets. +---@return table: list of luasnip-nodes. +function M.to_luasnip_nodes(ast, state) + state = state or {} + state.var_functions = state.var_functions or {} + + ast_utils.give_vars_potential_tabstop(ast) + + -- fix disallowed $0 in snippet. + -- TODO(logging): report changes here. + ast_utils.fix_zero(ast) + + -- Variables need the text just in front of them to determine whether to + -- indent all lines of the Variable. + ast_utils.give_vars_previous_text(ast) + + local ast_nodes_topsort = ast_utils.parse_order(ast) + assert( + ast_nodes_topsort, + "cannot represent snippet: contains circular dependencies" + ) + for _, node in ipairs(ast_nodes_topsort) do + to_node(node, state) + end + + return ast.parsed +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/ast_utils.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/ast_utils.lua new file mode 100644 index 00000000..42e614ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/ast_utils.lua @@ -0,0 +1,461 @@ +local Ast = require("luasnip.util.parser.neovim_ast") +local types = Ast.node_type +local util = require("luasnip.util.util") +local Str = require("luasnip.util.str") +local log = require("luasnip.util.log").new("parser") +local jsregexp_compile_safe = require("luasnip.util.jsregexp") + +local directed_graph = require("luasnip.util.directed_graph") + +local M = {} + +---Walks ast pre-order, from left to right, applying predicate fn. +---The walk is aborted as soon as fn matches (eg. returns true). +---The walk does not recurse into Transform or choice, eg. it only covers nodes +---that can be jumped (in)to. +---@param ast table: the tree. +---@param fn function: the predicate. +---@return boolean: whether the predicate matched. +local function predicate_ltr_nodes(ast, fn) + if fn(ast) then + return true + end + for _, node in ipairs(ast.children or {}) do + if predicate_ltr_nodes(node, fn) then + return true + end + end + + return false +end + +-- tested in vscode: +-- in "${1|b,c|} ${1:aa}" ${1:aa} is the copy, +-- in "${1:aa}, ${1|b,c|}" ${1|b,c} is the copy => with these two the position +-- determines which is the real tabstop => they have the same priority. +-- in "$1 ${1:aa}", $1 is the copy, so it has to have a lower priority. +local function type_real_tabstop_prio(node) + local _type_real_tabstop_prio = { + [types.TABSTOP] = 1, + [types.PLACEHOLDER] = 2, + [types.CHOICE] = 2, + } + if node.transform then + return 0 + end + return _type_real_tabstop_prio[node.type] +end + +---The name of this function is horrible, but I can't come up with something +---more succinct. +---The idea here is to find which of two nodes is "smaller" in a +---"real-tabstop"-ordering relation on all the nodes of a snippet. +---REQUIREMENT!!! The nodes have to be passed in the order they appear in in +---the snippet, eg. prev_node has to appear earlier in the text (or be a parent +---of) current_node. +---@param prev_node table: the ast node earlier in the text. +---@param current_node table: the other ast node. +---@return boolean: true if prev_node is less than (according to the +---"real-tabstop"-ordering described above and in the docstring of +---`add_dependents`), false otherwise. +local function real_tabstop_order_less(prev_node, current_node) + local prio_prev = type_real_tabstop_prio(prev_node) + local prio_current = type_real_tabstop_prio(current_node) + -- if type-prio is the same, the one that appeared earlier is the real tabstop. + return prio_prev == prio_current and false or prio_prev < prio_current +end + +---Find the real (eg. the one that is not a copy) $0. +---@param ast table: ast +---@return number, number, boolean: first, the type of the node with position 0, then +--- the child of `ast` containing it and last whether the real $0 is copied. +local function real_zero_node(ast) + local real_zero = nil + local real_zero_indx = nil + local is_copied = false + + local _search_zero + _search_zero = function(node) + local had_zero = false + -- find placeholder/tabstop/choice with position 0 + if node.tabstop == 0 then + if not real_zero then + real_zero = node + had_zero = true + else + if real_tabstop_order_less(real_zero, node) then + -- node has a higher prio than the current real_zero. + real_zero = node + had_zero = true + end + -- we already encountered a zero-node, since i(0) cannot be + -- copied this has to be reported to the caller. + is_copied = true + end + end + for indx, child in ipairs(node.children or {}) do + local zn, _ = _search_zero(child) + -- due to recursion, this will be called last in the loop of the + -- outermost snippet. + -- real_zero_indx will be the position of the child of snippet, in + -- which the real $0 is located. + if zn then + real_zero_indx = indx + had_zero = true + end + end + + return had_zero + end + _search_zero(ast) + + return real_zero, real_zero_indx, is_copied +end + +local function count_tabstop(ast, tabstop_indx) + local count = 0 + + predicate_ltr_nodes(ast, function(node) + if node.tabstop == tabstop_indx then + count = count + 1 + end + -- only stop once all nodes were looked at. + return false + end) + + return count +end + +local function text_only_placeholder(placeholder) + local only_text = true + + predicate_ltr_nodes(placeholder, function(node) + if node == placeholder then + -- ignore placeholder. + return false + end + if node.type ~= types.TEXT then + only_text = false + -- we found non-text, no need to search more. + return true + end + end) + + return only_text +end + +local function max_position(ast) + local max = 0 + predicate_ltr_nodes(ast, function(node) + local new_max = node.tabstop or 0 + if new_max > max then + max = new_max + end + -- don't stop early. + return false + end) + + return max +end + +local function replace_position(ast, p1, p2) + predicate_ltr_nodes(ast, function(node) + if node.tabstop == p1 then + node.tabstop = p2 + end + -- look at all nodes. + return false + end) +end + +function M.fix_zero(ast) + local zn, ast_child_with_0_indx, is_copied = real_zero_node(ast) + -- if zn exists, is a tabstop, an immediate child of `ast`, and does not + -- have to be copied, the snippet can be accurately represented by luasnip. + -- (also if zn just does not exist, ofc). + -- + -- If the snippet can't be represented as-is, the ast needs to be modified + -- as described below. + if + not zn + or ( + zn + and not is_copied + and (zn.type == types.TABSTOP or (zn.type == types.PLACEHOLDER and text_only_placeholder( + zn + ))) + and ast.children[ast_child_with_0_indx] == zn + ) + then + return + end + + -- bad, a choice or placeholder is at position 0. + -- replace all ${0:...} with ${n+1:...} (n highest position) + -- max_position is at least 0, all's good. + local max_pos = max_position(ast) + replace_position(ast, 0, max_pos + 1) + + -- insert $0 as a direct child to snippet, just behind the original $0/the + -- node containing it. + table.insert(ast.children, ast_child_with_0_indx + 1, Ast.tabstop(0)) +end + +---This function identifies which tabstops/placeholder/choices are copies, and +---which are "real tabstops"(/choices/placeholders). The real tabstops are +---extended with a list of their dependents (tabstop.dependents), the copies +---with their real tabstop (copy.copies) +--- +---Rules for which node of any two nodes with the same tabstop-index is the +---real tabstop: +--- - if one is a tabstop and the other a placeholder/choice, the +--- placeholder/choice is the real tabstop. +--- - if they are both tabstop or both placeholder/choice, the one which +--- appears earlier in the snippet is the real tabstop. +--- (in "${1: ${1:lel}}" the outer ${1:...} appears earlier). +--- +---@param ast table: the AST. +function M.add_dependents(ast) + -- all nodes that have a tabstop. + -- map tabstop-index (number) -> node. + local tabstops = {} + + -- nodes which copy some tabstop. + -- map tabstop-index (number) -> node[] (since there could be multiple copies of that one snippet). + local copies = {} + + predicate_ltr_nodes(ast, function(node) + if not node.tabstop then + -- not a tabstop-node -> continue. + return false + end + + if not tabstops[node.tabstop] then + tabstops[node.tabstop] = node + -- continue, we want to find all dependencies. + return false + end + if not copies[node.tabstop] then + copies[node.tabstop] = {} + end + if real_tabstop_order_less(tabstops[node.tabstop], node) then + table.insert(copies[node.tabstop], tabstops[node.tabstop]) + tabstops[node.tabstop] = node + else + table.insert(copies[node.tabstop], node) + end + -- continue. + return false + end) + + -- associate real tabstop with its copies (by storing the copies in the real tabstop). + for i, real_tabstop in pairs(tabstops) do + real_tabstop.dependents = {} + for _, copy in ipairs(copies[i] or {}) do + table.insert(real_tabstop.dependents, copy) + copy.copies = real_tabstop + end + end +end + +local function apply_modifier(text, modifier) + local mod_fn = Str.vscode_string_modifiers[modifier] + if mod_fn then + return mod_fn(text) + else + -- this can't really be reached, since only correct and available + -- modifiers are parsed successfully + -- (https://github.com/L3MON4D3/LuaSnip/blob/5fbebf6409f86bc4b7b699c2c80745e1ed190c16/lua/luasnip/util/parser/neovim_parser.lua#L239-L245). + log.warn( + "Tried to apply unknown modifier `%s` while parsing snippet, recovering by applying identity instead.", + modifier + ) + return text + end +end + +local function apply_transform_format(nodes, captures) + local transformed = "" + for _, node in ipairs(nodes) do + if node.type == types.TEXT then + transformed = transformed .. node.esc + else + local capture = captures[node.capture_index] + -- capture exists if it ..exists.. and is nonempty. + if capture and #capture > 0 then + if node.if_text then + transformed = transformed .. node.if_text + elseif node.modifier then + transformed = transformed + .. apply_modifier(capture, node.modifier) + else + transformed = transformed .. capture + end + else + if node.else_text then + transformed = transformed .. node.else_text + end + end + end + end + + return transformed +end + +function M.apply_transform(transform) + if jsregexp_compile_safe then + local reg_compiled, err = + jsregexp_compile_safe(transform.pattern, transform.option) + + if reg_compiled then + -- can be passed to functionNode! + return function(lines) + -- luasnip expects+passes lines as list, but regex needs one string. + lines = table.concat(lines, "\n") + local matches = reg_compiled(lines) + + local transformed = "" + -- index one past the end of previous match. + -- This is used to append unmatched characters to `transformed`, so + -- it's initialized such that the first append is from 1. + local prev_match_end = 0 + for _, match in ipairs(matches) do + -- begin_ind and end_ind are inclusive. + transformed = transformed + .. lines:sub(prev_match_end + 1, match.begin_ind - 1) + .. apply_transform_format( + transform.format, + match.groups + ) + + -- end-inclusive + prev_match_end = match.end_ind + end + transformed = transformed + .. lines:sub(prev_match_end + 1, #lines) + + return vim.split(transformed, "\n") + end + else + log.error( + "Failed parsing regex `%s` with options `%s`: %s", + transform.pattern, + transform.option, + err + ) + -- fall through to returning identity. + end + end + + -- without jsregexp, or without a valid regex, we cannot properly transform + -- whatever is supposed to be transformed here. + -- Just return a function that returns the to-be-transformed string + -- unmodified. + return util.id +end + +---Variables need the text which is in front of them to determine whether they +---have to be indented ("asdf\n\t$TM_SELECTED_TEXT": vscode indents all lines +---of TM_SELECTED_TEXT). +--- +---The text is accessible as ast_node.previous_text, a string[]. +---@param ast table: the AST. +function M.give_vars_previous_text(ast) + local last_text = { "" } + -- important: predicate_ltr_nodes visits the node in the order they appear, + -- textually, in the snippet. + -- This is necessary to actually ensure the variables actually get the text just in front of them. + predicate_ltr_nodes(ast, function(node) + if node.children then + -- continue if this node is not a leaf. + -- Since predicate_ltr_nodes runs fn first for the placeholder, and + -- then for its' children, `last_text` would be reset wrongfully + -- (example: "asdf\n\t${1:$TM_SELECTED_TEXT}". Here the placeholder + -- is encountered before the variable -> no indentation). + -- + -- ignoring non-leaf-nodes makes it so that only the nodes which + -- actually contribute text (placeholders are "invisible" in that + -- they don't add text themselves, they do it through their + -- children) are considered. + return false + end + if node.type == types.TEXT then + last_text = vim.split(node.esc, "\n") + elseif node.type == types.VARIABLE then + node.previous_text = last_text + else + -- reset last_text when a different node is encountered. + last_text = { "" } + end + -- continue.. + return false + end) +end + +---Variables are turned into placeholders if the Variable is undefined or not set. +---Since in luasnip, variables can be added at runtime, the decision whether a +---variable is just some text, inserts its default, or its variable-name has to +---be deferred to runtime. +---So, each variable is a dynamicNode, and needs a tabstop. +---In vscode the variables are visited +--- 1) after all other tabstops/placeholders/choices and +--- 2) in the order they appear in the snippet-body. +---We mimic this behaviour. +---@param ast table: The AST. +function M.give_vars_potential_tabstop(ast) + local last_tabstop = max_position(ast) + + predicate_ltr_nodes(ast, function(node) + if node.type == types.VARIABLE then + last_tabstop = last_tabstop + 1 + node.potential_tabstop = last_tabstop + end + end) +end + +function M.parse_order(ast) + M.add_dependents(ast) + -- build Directed Graph from ast-nodes. + -- vertices are ast-nodes, edges define has-to-be-parsed-before-relations + -- (a child of some placeholder would have an edge to it, real tabstops + -- have edges to their copies). + local g = directed_graph.new() + -- map node -> vertex. + local to_vert = {} + + -- add one vertex for each node + create map node->vert. + predicate_ltr_nodes(ast, function(node) + to_vert[node] = g:add_vertex() + end) + + predicate_ltr_nodes(ast, function(node) + if node.dependents then + -- if the node has dependents, it has to be parsed before they are. + for _, dep in ipairs(node.dependents) do + g:set_edge(to_vert[node], to_vert[dep]) + end + end + if node.children then + -- if the node has children, they have to be parsed before it can + -- be parsed. + for _, child in ipairs(node.children) do + g:set_edge(to_vert[child], to_vert[node]) + end + end + end) + + local topsort = g:topological_sort() + if not topsort then + -- ast (with additional dependencies) contains circle. + return nil + end + + local to_node = util.reverse_lookup(to_vert) + return vim.tbl_map(function(vertex) + return to_node[vertex] + end, topsort) +end + +M.types = types + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/init.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/init.lua new file mode 100644 index 00000000..31320faa --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/init.lua @@ -0,0 +1,141 @@ +local sNode = require("luasnip.nodes.snippet") +local ast_parser = require("luasnip.util.parser.ast_parser") +local parse = require("luasnip.util.parser.neovim_parser").parse +local Ast = require("luasnip.util.parser.neovim_ast") +local Str = require("luasnip.util.str") +local functions = require("luasnip.util.functions") +local util = require("luasnip.util.util") +local extend_decorator = require("luasnip.util.extend_decorator") + +local M = {} + +---Parse snippet represented by `body`. +---@param context (table|string|number|nil): +--- - table|string: treated like the first argument to `ls.snippet`, +--- returns a snippet. +--- - number: Returns a snippetNode, `context` is its' jump-position. +--- - nil: Returns a flat list of luasnip-nodes, to be used however. +---@param body string: the representation of the snippet. +---@param opts table|nil: optional parameters. Valid keys: +--- - `trim_empty`: boolean, remove empty lines from the snippet. +--- - `dedent`: boolean, remove common indent from the snippet's lines. +--- - `variables`: map[string-> (fn()->string)], variables to be used only in this +--- snippet. +---@return table: the snippet, in the representation dictated by the value of +---`context`. +function M.parse_snippet(context, body, opts) + opts = opts or {} + if opts.dedent == nil then + opts.dedent = true + end + if opts.trim_empty == nil then + opts.trim_empty = true + end + + body = Str.sanitize(body) + + local lines = vim.split(body, "\n") + Str.process_multiline(lines, opts) + body = table.concat(lines, "\n") + + local ast + if body == "" then + ast = Ast.snippet({ + Ast.text(""), + }) + else + ast = parse(body) + end + + local nodes = ast_parser.to_luasnip_nodes(ast, { + var_functions = opts.variables, + }) + + if type(context) == "number" then + return sNode.SN(context, nodes) + end + if type(context) == "nil" then + return nodes + end + + if type(context) == "string" then + context = { trig = context } + end + context.docstring = body + + return sNode.S(context, nodes) +end +local function context_extend(arg, extend) + local argtype = type(arg) + if argtype == "string" then + arg = { trig = arg } + end + + if argtype == "table" then + return vim.tbl_extend("keep", arg, extend or {}) + end + + -- fall back to unchanged arg. + -- log this, probably. + return arg +end +extend_decorator.register( + M.parse_snippet, + { arg_indx = 1, extend = context_extend }, + { arg_indx = 3 } +) + +local function backticks_to_variable(body) + local var_map = {} + local variable_indx = 1 + local var_string = "" + + local processed_to = 1 + for from, to in Str.unescaped_pairs(body, "`", "`") do + local varname = "LUASNIP_SNIPMATE_VAR" .. variable_indx + var_string = var_string + -- since the first unescaped ` is at from, there is no unescaped ` + -- in body:sub(old_to, from-1). We can therefore gsub occurences of + -- \`, without worrying about potentially changing something like + -- \\` (or \\\\`) into \` (\\\`). + .. body:sub(processed_to, from - 1):gsub("\\`", "`") + -- `$varname` is unsafe, might lead to something like "my + -- snip$LUASNIP_SNIPMATE_VAR1pet", where the variable is + -- interpreted as "LUASNIP_SNIPMATE_VAR1pet". + -- This cannot happen with curly braces. + .. "${" + .. varname + .. "}" + + -- don't include backticks in vimscript. + var_map[varname] = + functions.eval_vim_dynamic(body:sub(from + 1, to - 1)) + processed_to = to + 1 + variable_indx = variable_indx + 1 + end + + -- append remaining characters. + var_string = var_string .. body:sub(processed_to, -1):gsub("\\`", "`") + + return var_map, var_string +end + +function M.parse_snipmate(context, body, opts) + local new_vars + new_vars, body = backticks_to_variable(body) + + opts = opts or {} + opts.variables = {} + for name, fn in pairs(new_vars) do + -- created dynamicNode is not interactive. + opts.variables[name] = { fn, util.no } + end + return M.parse_snippet(context, body, opts) +end +extend_decorator.register( + M.parse_snipmate, + { arg_indx = 1, extend = context_extend }, + { arg_indx = 3 } +) + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/neovim_ast.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/neovim_ast.lua new file mode 100644 index 00000000..5fcd6251 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/neovim_ast.lua @@ -0,0 +1,207 @@ +-- ripped out of neovim. + +local M = {} + +local node_type = { + SNIPPET = 0, + TABSTOP = 1, + PLACEHOLDER = 2, + VARIABLE = 3, + CHOICE = 4, + TRANSFORM = 5, + FORMAT = 6, + TEXT = 7, +} + +M.node_type = node_type + +local Node = {} +function Node:__tostring() + local insert_text = {} + if self.type == node_type.SNIPPET then + for _, c in ipairs(self.children) do + table.insert(insert_text, tostring(c)) + end + elseif self.type == node_type.CHOICE then + table.insert(insert_text, self.items[1]) + elseif self.type == node_type.PLACEHOLDER then + for _, c in ipairs(self.children or {}) do + table.insert(insert_text, tostring(c)) + end + elseif self.type == node_type.TEXT then + table.insert(insert_text, self.esc) + end + return table.concat(insert_text, "") +end + +--- @private +local function new(t) + return setmetatable(t, Node) +end + +---Determine whether {t} is an AST-node. +---@param t table +---@return boolean +local function is_node(t) + return getmetatable(t) == Node +end + +M.is_node = is_node + +---Create a new snippet. +---@param children (ast-node[]) Contents of the snippet. +---@return table |lsp-parser-snippet| +function M.snippet(children) + return new({ + type = node_type.SNIPPET, + children = children, + }) +end + +---Create a new tabstop. +---@param tabstop (number) Position of this tabstop. +---@param transform (table|nil) optional transform applied to the tabstop. +---@return table |lsp-parser-tabstop| +function M.tabstop(tabstop, transform) + return new({ + type = node_type.TABSTOP, + tabstop = tabstop, + transform = transform, + }) +end + +---Create a new placeholder. +---@param tabstop (number) Position of the placeholder. +---@param children (ast-node[]) Content of the placeholder. +---@return table |lsp-parser-placeholder| +function M.placeholder(tabstop, children) + return new({ + type = node_type.PLACEHOLDER, + tabstop = tabstop, + children = children, + }) +end + +---Create a new variable. +---@param name (string) Name. +---@param replacement (node[] | transform | nil) +--- - (node[]) Inserted when the variable is empty. +--- - (transform) Applied to the variable's value. +---@return table |lsp-parser-variable| +function M.variable(name, replacement) + local transform, children + -- transform is an ast-node, children a flat list of nodes. + if is_node(replacement) then + transform = replacement + else + children = replacement + end + + return new({ + type = node_type.VARIABLE, + name = name, + transform = transform, + children = children, + }) +end + +---Create a new choice. +---@param tabstop (number) Position of the choice. +---@param items (string[]) Choices. +---@return table |lsp-parser-choice| +function M.choice(tabstop, items) + return new({ + type = node_type.CHOICE, + tabstop = tabstop, + items = items, + }) +end + +---Create a new transform. +---@param pattern (string) Regex applied to the variable/tabstop this transform +--- is supplied to. +---@param format (table of Format|Text) Replacement for the regex. +---@param option (string|nil) Regex-options, default "". +---@return table |lsp-parser-transform| +function M.transform(pattern, format, option) + return new({ + type = node_type.TRANSFORM, + pattern = pattern, + format = format, + option = option or "", + }) +end + +---Create a new format which either inserts the capture at {capture_index}, +---applies a modifier to the capture or inserts {if_text} if the capture is +---nonempty, and {else_text} otherwise. +---@param capture_index (number) Capture this format is applied to. +---@param capture_transform (string | table | nil) +--- - (string): {capture_transform} is a modifier. +--- - (table): {capture_transform} can contain either of +--- - {if_text} (string) Inserted for nonempty +--- capture. +--- - {else_text} (string) Inserted for empty or +--- undefined capture. +---@return table |lsp-parser-format| +function M.format(capture_index, capture_transform) + local if_text, else_text, modifier + if type(capture_transform) == "table" then + if_text = capture_transform.if_text + else_text = capture_transform.else_text + elseif type(capture_transform) == "string" then + modifier = capture_transform + end + + return new({ + type = node_type.FORMAT, + capture_index = capture_index, + modifier = modifier, + if_text = if_text, + else_text = else_text, + }) +end + +---Create new text. +---@param esc (string) Escaped text. +---@param raw (string|nil, default {esc}) Unescaped text. +--- +---@return table |lsp-parser-text| +function M.text(esc, raw) + return new({ + type = node_type.TEXT, + esc = esc, + raw = raw or esc, + }) +end + +function M.merge_adjacent_text(ast) + if ast.children then + -- build new table of children. + local new_children = {} + -- last_child shall always point to the last entry in new_children. + local last_child + + for _, child in ipairs(ast.children) do + -- first, recurse into children. + -- When we do this is not important, since it does not change the TEXT-nodes, here is just comfortable. + M.merge_adjacent_text(child) + + if + child.type == node_type.TEXT + and last_child + and last_child.type == node_type.TEXT + then + last_child.raw = last_child.raw .. child.raw + last_child.esc = last_child.esc .. child.esc + else + table.insert(new_children, child) + last_child = child + end + end + + ast.children = new_children + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/neovim_parser.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/neovim_parser.lua new file mode 100644 index 00000000..5848c83f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/parser/neovim_parser.lua @@ -0,0 +1,479 @@ +-- ripped out of neovim. + +local P = {} + +local ast = require("luasnip.util.parser.neovim_ast") + +---Take characters until the target characters (The escape sequence is '\' + char) +---@param targets string[] The character list for stop consuming text. +---@param specials string[] If the character isn't contained in targets/specials, '\' will be left. +---@private +function P.take_until(targets, specials) + targets = targets or {} + specials = specials or {} + + return function(input, pos) + local new_pos = pos + local raw = {} + local esc = {} + while new_pos <= #input do + local c = string.sub(input, new_pos, new_pos) + if c == "\\" then + table.insert(raw, "\\") + new_pos = new_pos + 1 + c = string.sub(input, new_pos, new_pos) + if + not vim.tbl_contains(targets, c) + and not vim.tbl_contains(specials, c) + then + table.insert(esc, "\\") + end + table.insert(raw, c) + table.insert(esc, c) + new_pos = new_pos + 1 + else + if vim.tbl_contains(targets, c) then + break + end + table.insert(raw, c) + table.insert(esc, c) + new_pos = new_pos + 1 + end + end + + if new_pos == pos then + return P.unmatch(pos) + end + + return { + parsed = true, + value = { + raw = table.concat(raw, ""), + esc = table.concat(esc, ""), + }, + pos = new_pos, + } + end +end + +---@private +function P.unmatch(pos) + return { + parsed = false, + value = nil, + pos = pos, + } +end + +---@private +function P.map(parser, map) + return function(input, pos) + local result = parser(input, pos) + if result.parsed then + return { + parsed = true, + value = map(result.value), + pos = result.pos, + } + end + return P.unmatch(pos) + end +end + +---@private +function P.lazy(factory) + return function(input, pos) + return factory()(input, pos) + end +end + +---@private +function P.token(token) + return function(input, pos) + local maybe_token = string.sub(input, pos, pos + #token - 1) + if token == maybe_token then + return { + parsed = true, + value = maybe_token, + pos = pos + #token, + } + end + return P.unmatch(pos) + end +end + +---@private +function P.pattern(p) + return function(input, pos) + local maybe_match = string.match(string.sub(input, pos), "^" .. p) + if maybe_match then + return { + parsed = true, + value = maybe_match, + pos = pos + #maybe_match, + } + end + return P.unmatch(pos) + end +end + +---@private +function P.many(parser) + return function(input, pos) + local values = {} + local new_pos = pos + while new_pos <= #input do + local result = parser(input, new_pos) + if not result.parsed then + break + end + table.insert(values, result.value) + new_pos = result.pos + end + if #values > 0 then + return { + parsed = true, + value = values, + pos = new_pos, + } + end + return P.unmatch(pos) + end +end + +---@private +function P.any(...) + local parsers = { ... } + return function(input, pos) + for _, parser in ipairs(parsers) do + local result = parser(input, pos) + if result.parsed then + return result + end + end + return P.unmatch(pos) + end +end + +---@private +function P.opt(parser) + return function(input, pos) + local result = parser(input, pos) + return { + parsed = true, + value = result.value, + pos = result.pos, + } + end +end + +---@private +function P.seq(...) + local parsers = { ... } + return function(input, pos) + local values = {} + local new_pos = pos + for i, parser in ipairs(parsers) do + local result = parser(input, new_pos) + if result.parsed then + values[i] = result.value + new_pos = result.pos + else + return P.unmatch(pos) + end + end + return { + parsed = true, + value = values, + pos = new_pos, + } + end +end + +---see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar + +local S = {} +S.dollar = P.token("$") +S.open = P.token("{") +S.close = P.token("}") +S.colon = P.token(":") +S.slash = P.token("/") +S.comma = P.token(",") +S.pipe = P.token("|") +S.plus = P.token("+") +S.minus = P.token("-") +S.question = P.token("?") +S.int = P.map(P.pattern("[0-9]+"), function(value) + return tonumber(value, 10) +end) +S.var = P.pattern("[%a_][%w_]+") +S.text = function(targets, specials) + return P.map(P.take_until(targets, specials), function(value) + return ast.text(value.esc, value.raw) + end) +end +S.patterntext = function(pattern) + return P.map(P.pattern(pattern), function(value) + return ast.text(value, value) + end) +end +S.text_or_empty = function(targets, specials) + return P.map( + P.any(P.take_until(targets, specials), P.token("")), + function(value) + -- if it is empty, we have to return a valid S.text-object. + if value == "" then + return ast.text("", "") + else + return ast.text(value.esc, value.raw) + end + end + ) +end + +S.toplevel = P.lazy(function() + return P.any(S.placeholder, S.tabstop, S.variable, S.choice) +end) + +S.format = P.any( + P.map(P.seq(S.dollar, S.int), function(values) + return ast.format(values[2]) + end), + P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values) + return ast.format(values[3]) + end), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + S.slash, + P.any( + P.token("upcase"), + P.token("downcase"), + P.token("capitalize"), + P.token("camelcase"), + P.token("pascalcase") + ), + S.close + ), + function(values) + return ast.format(values[3], values[6]) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.seq( + S.question, + P.opt(P.take_until({ ":" }, { "\\" })), + S.colon, + P.opt(P.take_until({ "}" }, { "\\" })) + ), + S.close + ), + function(values) + return ast.format(values[3], { + if_text = values[5][2] and values[5][2].esc or "", + else_text = values[5][4] and values[5][4].esc or "", + }) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.seq(S.plus, P.opt(P.take_until({ "}" }, { "\\" }))), + S.close + ), + function(values) + return ast.format(values[3], { + if_text = values[5][2] and values[5][2].esc or "", + }) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + S.minus, + P.opt(P.take_until({ "}" }, { "\\" })), + S.close + ), + function(values) + return ast.format(values[3], { + else_text = values[6] and values[6].esc or "", + }) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.opt(P.take_until({ "}" }, { "\\" })), + S.close + ), + function(values) + return ast.format(values[3], { + else_text = values[5] and values[5].esc or "", + }) + end + ) +) + +S.transform = P.map( + P.seq( + S.slash, + P.take_until({ "/" }, { "\\" }), + S.slash, + P.many( + P.any( + S.format, + S.text({ "$", "/" }, { "\\" }), + S.patterntext("[^/]") + ) + ), + S.slash, + P.opt(P.pattern("[ig]+")) + ), + function(values) + return ast.transform(values[2].raw, values[4], values[6]) + end +) + +S.tabstop = P.any( + P.map(P.seq(S.dollar, S.int), function(values) + return ast.tabstop(values[2]) + end), + P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values) + return ast.tabstop(values[3]) + end), + P.map(P.seq(S.dollar, S.open, S.int, S.transform, S.close), function(values) + return ast.tabstop(values[3], values[4]) + end) +) + +S.placeholder = P.any( + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.opt( + P.many( + P.any( + S.toplevel, + S.text({ "$", "}" }, { "\\" }), + S.patterntext("[^}]") + ) + ) + ), + S.close + ), + function(values) + -- no children -> manually create empty text. + return ast.placeholder(values[3], values[5] or { ast.text("") }) + end + ) +) + +S.choice = P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.pipe, + P.opt( + P.many( + P.map( + P.seq(S.text_or_empty({ ",", "|" }), S.comma), + function(values) + return values[1].esc + end + ) + ) + ), + P.map(P.any(S.text_or_empty({ ",", "|" }), S.empty), function(values) + return values.esc + end), + S.pipe, + S.close + ), + function(values) + local choices = values[5] or {} + table.insert(choices, values[6]) + return ast.choice(values[3], choices) + end +) + +S.variable = P.any( + P.map(P.seq(S.dollar, S.var), function(values) + return ast.variable(values[2]) + end), + P.map(P.seq(S.dollar, S.open, S.var, S.close), function(values) + return ast.variable(values[3]) + end), + P.map(P.seq(S.dollar, S.open, S.var, S.transform, S.close), function(values) + return ast.variable(values[3], values[4]) + end), + P.map( + P.seq( + S.dollar, + S.open, + S.var, + S.colon, + P.many( + P.any( + S.toplevel, + S.text({ "$", "}" }, { "\\" }), + S.patterntext("[^}]") + ) + ), + S.close + ), + function(values) + return ast.variable(values[3], values[5]) + end + ) +) + +S.snippet = P.map( + P.many( + P.any(S.toplevel, S.text({ "$" }, { "}", "\\" }), S.patterntext(".")) + ), + function(values) + -- Insp(values) + return ast.snippet(values) + end +) + +local M = {} + +---Build the AST for {input}. +---@param input string A snippet as defined in +--- https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar +---@return (Snippet) +function M.parse(input) + local result = S.snippet(input, 1) + if not result.parsed then + error("snippet parsing failed.") + end + + ast.merge_adjacent_text(result.value) + return result.value +end + +M.ast = ast + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/path.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/path.lua new file mode 100644 index 00000000..83141098 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/path.lua @@ -0,0 +1,266 @@ +local Path = {} + +local uv = vim.loop + +local sep = (function() + if jit then + local os = jit.os:lower() + if vim.tbl_contains({ "linux", "osx", "bsd" }, os) then + return "/" + else + return "\\" + end + end + return package.config:sub(1, 1) +end)() + +local root_pattern = (function() + return uv.os_uname().sysname:find("Windows") and "%w%:" or "%/" +end)() + +function Path.join(...) + return table.concat({ ... }, sep) +end + +function Path.exists(filepath) + return uv.fs_stat(filepath) and true or false +end + +function Path.async_read_file(path, callback) + uv.fs_open(path, "r", tonumber("0666", 8), function(err, fd) + assert(not err, err) + uv.fs_fstat(fd, function(err, stat) + assert(not err, err) + uv.fs_read(fd, stat.size, 0, function(err, buffer) + assert(not err, err) + uv.fs_close(fd, function(err) + assert(not err, err) + callback(buffer) + end) + end) + end) + end) +end + +---@param path string +---@return string buffer @content of file +function Path.read_file(path) + -- permissions: rrr + local fd = assert(uv.fs_open(path, "r", tonumber("0444", 8))) + local stat = assert(uv.fs_fstat(fd)) + -- read from offset 0. + local buf = assert(uv.fs_read(fd, stat.size, 0)) + uv.fs_close(fd) + + return buf +end + +local MYCONFIG_ROOT + +if vim.env.MYVIMRC then + MYCONFIG_ROOT = vim.fn.fnamemodify(vim.env.MYVIMRC, ":p:h") +else + MYCONFIG_ROOT = vim.fn.getcwd() +end + +-- sometimes we don't want to resolve symlinks, but handle ~/ and ./ +function Path.expand_keep_symlink(filepath) + -- omit second return-value of :gsub + local res = filepath + :gsub("^~", vim.env.HOME) + :gsub("^[.][/\\]", MYCONFIG_ROOT .. sep) + return res +end +function Path.expand(filepath) + return uv.fs_realpath(Path.expand_keep_symlink(filepath)) +end + +-- do our best at normalizing a non-existing path. +function Path.normalize_nonexisting(filepath, cwd) + cwd = cwd or vim.fn.getcwd() + + local normalized = filepath + -- replace multiple slashes by one. + :gsub(sep .. sep .. "+", sep) + -- remove trailing slash. + :gsub(sep .. "$", "") + -- remove ./ from path. + :gsub("%." .. sep, "") + + -- if not yet absolute, prepend path to current directory. + if not normalized:match("^" .. root_pattern .. "") then + normalized = Path.join(cwd, normalized) + end + + return normalized +end + +function Path.expand_nonexisting(filepath, cwd) + filepath + -- replace ~ with home-directory. + :gsub("^~", vim.env.HOME) + -- replace ./ or .\ with config-directory (likely ~/.config/nvim) + :gsub( + "^[.][/\\]", + MYCONFIG_ROOT .. sep + ) + + return Path.normalize_nonexisting(filepath, cwd) +end + +-- do our best at expanding a path that may or may not exist (ie. check if it +-- exists, if so do regular expand, and guess expanded path otherwise) +-- Not the clearest name :/ +function Path.expand_maybe_nonexisting(filepath, cwd) + local real_expanded = Path.expand(filepath) + if not real_expanded then + real_expanded = Path.expand_nonexisting(filepath, cwd) + end + return real_expanded +end + +function Path.normalize_maybe_nonexisting(filepath, cwd) + local real_normalized = Path.normalize(filepath) + if not real_normalized then + real_normalized = Path.normalize_nonexisting(filepath, cwd) + end + return real_normalized +end + +---Return files and directories in path as a list +---@param root string +---@return string[] files, string[] directories +function Path.scandir(root) + local files, dirs = {}, {} + local fs = uv.fs_scandir(root) + if fs then + local name, type = "", "" + while name do + name, type = uv.fs_scandir_next(fs) + local path = Path.join(root, name) + -- On networked filesystems, it can happen that we get + -- a name, but no type. In this case, we must query the + -- type manually via fs_stat(). See issue: + -- https://github.com/luvit/luv/issues/660 + if name and not type then + local stat = uv.fs_stat(path) + type = stat and stat.type + end + if type == "file" then + table.insert(files, path) + elseif type == "directory" then + table.insert(dirs, path) + elseif type == "link" then + local followed_path = uv.fs_realpath(path) + if followed_path then + local stat = uv.fs_stat(followed_path) + if stat.type == "file" then + table.insert(files, path) + elseif stat.type == "directory" then + table.insert(dirs, path) + end + end + end + end + end + return files, dirs +end + +---Get basename +---@param filepath string +---@param ext boolean if true, separate the file extension +---@return string, string? +---Example: +--- Path.basename("~/.config/nvim/init.lua") -> init.lua +--- Path.basename("~/.config/nvim/init.lua", true) -> init, lua +function Path.basename(filepath, ext) + local base = filepath + if base:find(sep) then + base = base:match(("%s([^%s]+)$"):format(sep, sep)) + end + if ext then + return base:match("(.*)%.(.+)") + else + return base + end +end + +function Path.extension(fname) + return fname:match("%.([^%.]+)$") +end + +function Path.components(path) + return vim.split(path, sep, { plain = true, trimempty = true }) +end + +---Get parent of a path, without trailing separator +---if path is a directory or does not have a parent, returns nil +---Example: +--- On platforms that use "\\" backslash as path separator, e.g., Windows: +--- Path.parent("C:/project_root/file.txt") -- returns "C:/project_root" +--- Path.parent([[C:\project_root\file.txt]]) -- returns [[C:\project_root]] +--- +--- -- the followings return `nil`s +--- Path.parent("C:/") +--- Path.parent([[C:\]]) +--- Path.parent([[C:\project_root\]]) +--- +--- -- WARN: although it's unlikely that we will reach the driver's root +--- -- level, Path.parent("C:\file.txt") returns "C:", and please be +--- -- cautious when passing the parent path to some vim functions because +--- -- some vim functions on Windows treat "C:" as a file instead: +--- -- vim.fn.fnamemodify("C:", ":p") -- returns $CWD .. sep .. "C:" +--- -- To get the desired result, use vim.fn.fnamemodify("C:" .. sep, ":p") +--- +--- On platforms that use "/" forward slash as path separator, e.g., linux: +--- Path.parent("/project_root/file.txt") returns "/project_root" +--- Path.parent("/file.txt") returns "" +--- +--- -- the followings return `nil`s +--- Path.parent("/") +--- Path.parent("/project_root/") +--- +--- -- backslash in a valid filename character in linux: +--- Path.parent([[/project_root/\valid\file\name.txt]]) returns "/project_root" +Path.parent = (function() + ---@alias PathSeparator "/" | "\\" + ---@param os_sep PathSeparator + ---@return fun(string): string | nil + local function generate_parent(os_sep) + if os_sep == "/" then + ---@param path string + ---@return string | nil + return function(path) + local last_component = path:match("[/]+[^/]+$") + if not last_component then + return nil + end + + return path:sub(1, #path - #last_component) + end + else + ---@param path string + ---@return string | nil + return function(path) + local last_component = path:match("[/\\]+[^/\\]+$") + if not last_component then + return nil + end + + return path:sub(1, #path - #last_component) + end + end + end + + -- for test only + if __LUASNIP_TEST_SEP_OVERRIDE then + return generate_parent + else + return generate_parent(sep) + end +end)() + +-- returns nil if the file does not exist! +Path.normalize = uv.fs_realpath + +return Path diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/pattern_tokenizer.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/pattern_tokenizer.lua new file mode 100644 index 00000000..8dfc1a44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/pattern_tokenizer.lua @@ -0,0 +1,137 @@ +local is_class = { + a = true, + c = true, + d = true, + l = true, + p = true, + s = true, + u = true, + w = true, + x = true, + z = true, + -- and uppercase versions. + A = true, + C = true, + D = true, + L = true, + P = true, + S = true, + U = true, + W = true, + X = true, + Z = true, + -- all others false. +} + +local is_rep_mod = { + ["+"] = true, + ["*"] = true, + ["-"] = true, + ["?"] = true, +} + +local function is_escaped(text, indx) + local count = 0 + for i = indx - 1, 1, -1 do + if string.sub(text, i, i) == "%" then + count = count + 1 + else + break + end + end + return count % 2 == 1 +end + +local function charset_end_indx(string, start_indx) + -- set plain + local indx = string:find("]", start_indx, true) + -- find unescaped ']' + while indx and is_escaped(string, indx) do + indx = string:find("]", indx + 1, true) + end + return indx +end + +return { + tokenize = function(pattern) + local indx = 1 + local current_text = "" + local tokens = {} + -- assume the pattern starts with text (as opposed to eg. a character + -- class), worst-case an empty textNode is (unnecessarily) inserted at + -- the beginning. + local is_text = true + while indx <= #pattern do + local next_indx + local next_text + local next_is_text + -- for some atoms *,+,-,? are not applicable, ignore them. + local repeatable = true + local char = pattern:sub(indx, indx) + if char == "%" then + if pattern:sub(indx + 1, indx + 1) == "b" then + -- %b seems to consume exactly the next two chars literally. + next_is_text = false + next_indx = indx + 4 + repeatable = false + elseif is_class[pattern:sub(indx + 1, indx + 1)] then + next_is_text = false + next_indx = indx + 2 + else + -- not a class, just an escaped character. + next_is_text = true + next_indx = indx + 2 + -- only append escaped char, not '%'. + end + elseif char == "." then + next_is_text = false + next_indx = indx + 1 + elseif char == "[" then + next_is_text = false + -- if not found, just exit loop now, pattern is malformed. + next_indx = (charset_end_indx(pattern, indx) or #pattern) + 1 + elseif + char == "(" + or char == ")" + or (char == "^" and indx == 1) + then + -- ^ is interpreted literally if not at beginning. + -- $ will always be interpreted literally in triggers. + + -- remove ( and ) from text. + -- keep text or no-text active. + next_is_text = is_text + -- increase indx to exclude ( from tokens. + indx = indx + 1 + next_indx = indx + -- cannot repeat group. + repeatable = false + else + next_is_text = true + next_indx = indx + 1 + end + + if repeatable and is_rep_mod[pattern:sub(next_indx, next_indx)] then + next_indx = next_indx + 1 + next_is_text = false + end + + next_text = pattern:sub(indx, next_indx - 1) + + -- check if this token is still the same as the previous. + if next_is_text == is_text then + current_text = current_text .. next_text + else + tokens[#tokens + 1] = current_text + current_text = next_text + end + + indx = next_indx + is_text = next_is_text + end + + -- add last part, would normally be added at the end of the loop. + tokens[#tokens + 1] = current_text + return tokens + end, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/select.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/select.lua new file mode 100644 index 00000000..decf8fb5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/select.lua @@ -0,0 +1,141 @@ +local M = {} + +local SELECT_RAW = "LUASNIP_SELECT_RAW" +local SELECT_DEDENT = "LUASNIP_SELECT_DEDENT" +local TM_SELECT = "LUASNIP_TM_SELECT" + +function M.retrieve() + local ok, val = pcall(vim.api.nvim_buf_get_var, 0, SELECT_RAW) + if ok then + local result = { + val, + vim.api.nvim_buf_get_var(0, SELECT_DEDENT), + vim.api.nvim_buf_get_var(0, TM_SELECT), + } + + vim.api.nvim_buf_del_var(0, SELECT_RAW) + vim.api.nvim_buf_del_var(0, SELECT_DEDENT) + vim.api.nvim_buf_del_var(0, TM_SELECT) + + return unpack(result) + end + return {}, {}, {} +end + +local function get_min_indent(lines) + -- "^(%s*)%S": match only lines that actually contain text. + local min_indent = lines[1]:match("^(%s*)%S") + for i = 2, #lines do + -- %s* -> at least matches + local line_indent = lines[i]:match("^(%s*)%S") + -- ignore if not matched. + if line_indent then + -- if no line until now matched, use line_indent. + if not min_indent or #line_indent < #min_indent then + min_indent = line_indent + end + end + end + return min_indent +end + +local function store_registers(...) + local names = { ... } + local restore_data = {} + for _, name in ipairs(names) do + restore_data[name] = { + data = vim.fn.getreg(name), + type = vim.fn.getregtype(name), + } + end + return restore_data +end + +local function restore_registers(restore_data) + for name, name_restore_data in pairs(restore_data) do + vim.fn.setreg(name, name_restore_data.data, name_restore_data.type) + end +end + +-- subtle: `:lua` exits VISUAL, which means that the '< '>-marks will be set correctly! +-- Afterwards, we can just use lua, which does not change the mode. +M.select_keys = + [[:lua require("luasnip.util.select").pre_cut()gv"zslua require('luasnip.util.select').post_cut("z")]] + +local saved_registers +local lines +local start_line, start_col, end_line, end_col +local mode +function M.pre_cut() + -- store registers so we don't change any of them. + -- "" is affected since we perform a cut (s), 1-9 also (although :h + -- quote_number seems to state otherwise for cuts to specific registers..?). + saved_registers = + store_registers("", "1", "2", "3", "4", "5", "6", "7", "8", "9", "z") + + -- store data needed for de-indenting lines. + start_line = vim.fn.line("'<") - 1 + start_col = vim.fn.col("'<") + end_line = vim.fn.line("'>") - 1 + end_col = vim.fn.col("'>") + -- +1: include final line. + lines = vim.api.nvim_buf_get_lines(0, start_line, end_line + 1, true) + mode = vim.fn.visualmode() +end + +function M.post_cut(register_name) + -- remove trailing newline. + local chunks = vim.split(vim.fn.getreg(register_name):gsub("\n$", ""), "\n") + + -- make sure to restore the registers to the state they were before cutting. + restore_registers(saved_registers) + + local tm_select, select_dedent = vim.deepcopy(chunks), vim.deepcopy(chunks) + + local min_indent = get_min_indent(lines) or "" + if mode == "V" then + tm_select[1] = tm_select[1]:gsub("^%s+", "") + -- remove indent from all lines: + for i = 1, #select_dedent do + select_dedent[i] = select_dedent[i]:gsub("^" .. min_indent, "") + end + -- due to the trailing newline of the last line, and vim.split's + -- behaviour, the last line of `chunks` is always empty. + -- Keep this + elseif mode == "v" then + -- if selection starts inside indent, remove indent. + if #min_indent > start_col then + select_dedent[1] = lines[1]:gsub(min_indent, "") + end + for i = 2, #select_dedent - 1 do + select_dedent[i] = select_dedent[i]:gsub(min_indent, "") + end + + -- remove as much indent from the last line as possible. + if #min_indent > end_col then + select_dedent[#select_dedent] = "" + else + select_dedent[#select_dedent] = + select_dedent[#select_dedent]:gsub("^" .. min_indent, "") + end + else + -- in block: if indent is in block, remove the part of it that is inside + -- it for select_dedent. + if #min_indent > start_col then + local indent_in_block = min_indent:sub(start_col, #min_indent) + for i, line in ipairs(chunks) do + select_dedent[i] = line:gsub("^" .. indent_in_block, "") + end + end + end + + vim.api.nvim_buf_set_var(0, SELECT_RAW, chunks) + vim.api.nvim_buf_set_var(0, SELECT_DEDENT, select_dedent) + vim.api.nvim_buf_set_var(0, TM_SELECT, tm_select) + + lines = nil + start_line, start_col, end_line, end_col = nil, nil, nil, nil + mode = nil +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/str.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/str.lua new file mode 100644 index 00000000..7b8ec5f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/str.lua @@ -0,0 +1,141 @@ +-- Some string processing utility functions +local M = {} + +---In-place dedents strings in lines. +---@param lines string[]. +local function dedent(lines) + if #lines > 0 then + local ind_size = math.huge + for i, _ in ipairs(lines) do + local i1, i2 = lines[i]:find("^%s*[^%s]") + if i1 and i2 < ind_size then + ind_size = i2 + end + end + for i, _ in ipairs(lines) do + lines[i] = lines[i]:sub(ind_size, -1) + end + end +end + +---Applies opts to lines. +---lines is modified in-place. +---@param lines string[]. +---@param options table, required, can have values: +--- - trim_empty: removes empty first and last lines. +--- - dedent: removes indent common to all lines. +function M.process_multiline(lines, options) + if options.trim_empty then + if lines[1]:match("^%s*$") then + table.remove(lines, 1) + end + if #lines > 0 and lines[#lines]:match("^%s*$") then + lines[#lines] = nil + end + end + + if options.dedent then + dedent(lines) + end +end + +function M.dedent(s) + local lst = vim.split(s, "\n") + dedent(lst) + return table.concat(lst, "\n") +end + +local function is_escaped(s, indx) + local count = 0 + for i = indx - 1, 1, -1 do + if string.sub(s, i, i) == "\\" then + count = count + 1 + else + break + end + end + return count % 2 == 1 +end + +--- return position of next (relative to `start`) unescaped occurence of +--- `target` in `s`. +---@param s string +---@param target string +---@param start number +local function find_next_unescaped(s, target, start) + while true do + local from = s:find(target, start, true) + if not from then + return nil + end + if not is_escaped(s, from) then + return from + end + start = from + 1 + end +end + +--- Creates iterator that returns all positions of substrings .* +--- in `s`, where left and right are not escaped. +--- Only complete pairs left,right are returned, an unclosed left is ignored. +---@param s string +---@param left string +---@param right string +---@return function: iterator, returns pairs from,to. +function M.unescaped_pairs(s, left, right) + local search_from = 1 + + return function() + local match_from = find_next_unescaped(s, left, search_from) + if not match_from then + return nil + end + local match_to = find_next_unescaped(s, right, match_from + 1) + if not match_to then + return nil + end + + search_from = match_to + 1 + return match_from, match_to + end +end + +function M.aupatescape(s) + if vim.fn.has("win32") or vim.fn.has("win64") then + -- windows: replace \ with / for au-pattern. + s, _ = s:gsub("\\", "/") + end + local escaped, _ = s:gsub(",", "\\,") + return vim.fn.fnameescape(escaped) +end + +function M.sanitize(str) + return str:gsub("%\r", "") +end + +-- string-operations implemented according to +-- https://github.com/microsoft/vscode/blob/71c221c532996c9976405f62bb888283c0cf6545/src/vs/editor/contrib/snippet/browser/snippetParser.ts#L372-L415 +-- such that they can be used for snippet-transformations in vscode-snippets. +local function capitalize(str) + -- uppercase first character. + return str:gsub("^.", string.upper) +end +local function pascalcase(str) + local pascalcased = "" + for match in str:gmatch("[a-zA-Z0-9]+") do + pascalcased = pascalcased .. capitalize(match) + end + return pascalcased +end +M.vscode_string_modifiers = { + upcase = string.upper, + downcase = string.lower, + capitalize = capitalize, + pascalcase = pascalcase, + camelcase = function(str) + -- same as pascalcase, but first character lowercased. + return pascalcase(str):gsub("^.", string.lower) + end, +} + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/table.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/table.lua new file mode 100644 index 00000000..31762686 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/table.lua @@ -0,0 +1,39 @@ +---Convert set of values to a list of those values. +---@generic T +---@param tbl T|T[]|table +---@return table +local function set_to_list(tbl) + local ls = {} + + for v, _ in pairs(tbl) do + table.insert(ls, v) + end + + return ls +end + +---Convert value or list of values to a table of booleans for fast lookup. +---@generic T +---@param values T|T[]|table +---@return table +local function list_to_set(values) + if values == nil then + return {} + end + + if type(values) ~= "table" then + return { [values] = true } + end + + local list = {} + for _, v in ipairs(values) do + list[v] = true + end + + return list +end + +return { + list_to_set = list_to_set, + set_to_list = set_to_list, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/time.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/time.lua new file mode 100644 index 00000000..39fc9233 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/time.lua @@ -0,0 +1,13 @@ +-- http://lua-users.org/wiki/TimeZone +local function get_timezone_offset(ts) + local utcdate = os.date("!*t", ts) + local localdate = os.date("*t", ts) + localdate.isdst = false -- this is the trick + local diff = os.difftime(os.time(localdate), os.time(utcdate)) + local h, m = math.modf(diff / 3600) + return string.format("%+.4d", 100 * h + 60 * m) +end + +return { + get_timezone_offset = get_timezone_offset, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/types.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/types.lua new file mode 100644 index 00000000..a9ded11d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/types.lua @@ -0,0 +1,34 @@ +return { + textNode = 1, + insertNode = 2, + functionNode = 3, + snippetNode = 4, + choiceNode = 5, + dynamicNode = 6, + snippet = 7, + exitNode = 8, + restoreNode = 9, + node_types = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + names = { + "textNode", + "insertNode", + "functionNode", + "snippetNode", + "choiceNode", + "dynamicNode", + "snippet", + "exitNode", + "restoreNode", + }, + names_pascal_case = { + "TextNode", + "InsertNode", + "FunctionNode", + "SnippetNode", + "ChoiceNode", + "DynamicNode", + "Snippet", + "ExitNode", + "RestoreNode", + }, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/util.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/util.lua new file mode 100644 index 00000000..956eaf7a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/luasnip/util/util.lua @@ -0,0 +1,522 @@ +local session = require("luasnip.session") + +local function get_cursor_0ind() + local c = vim.api.nvim_win_get_cursor(0) + c[1] = c[1] - 1 + return c +end + +-- don't use utf-indexed column, win_set_cursor ignores these. +local function set_cursor_0ind(c) + c[1] = c[1] + 1 + vim.api.nvim_win_set_cursor(0, c) +end + +-- pos: (0,0)-indexed. +local function line_chars_before(pos) + -- cur-rows are 1-indexed, api-rows 0. + local line = vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false) + return string.sub(line[1], 1, pos[2]) +end + +-- returns current line with text up-to and excluding the cursor. +local function get_current_line_to_cursor() + return line_chars_before(get_cursor_0ind()) +end + +-- delete n chars before cursor, MOVES CURSOR +local function remove_n_before_cur(n) + local cur = get_cursor_0ind() + vim.api.nvim_buf_set_text(0, cur[1], cur[2] - n, cur[1], cur[2], { "" }) + cur[2] = cur[2] - n + set_cursor_0ind(cur) +end + +-- in-place modifies the table. +local function dedent(text, indentstring) + -- 2 because 1 shouldn't contain indent. + for i = 2, #text do + text[i] = text[i]:gsub("^" .. indentstring, "") + end + return text +end + +-- in-place insert indenstrig before each line. +local function indent(text, indentstring) + for i = 2, #text - 1, 1 do + -- only indent if there is actually text. + if #text[i] > 0 then + text[i] = indentstring .. text[i] + end + end + -- assuming that the last line should be indented as it is probably + -- followed by some other node, therefore isn't an empty line. + if #text > 1 then + text[#text] = indentstring .. text[#text] + end + return text +end + +--- In-place expands tabs in `text`. +--- Difficulties: +--- we cannot simply replace tabs with a given number of spaces, the tabs align +--- text at multiples of `tabwidth`. This is also the reason we need the number +--- of columns the text is already indented by (otherwise we can only start a 0). +---@param text string[], multiline string. +---@param tabwidth number, displaycolumns one tab should shift following text +--- by. +---@param parent_indent_displaycolumns number, displaycolumn this text is +--- already at. +---@return string[], `text` (only for simple nesting). +local function expand_tabs(text, tabwidth, parent_indent_displaycolumns) + for i, line in ipairs(text) do + local new_line = "" + local start_indx = 1 + while true do + local tab_indx = line:find("\t", start_indx, true) + -- if no tab found, sub till end (ie. -1). + new_line = new_line .. line:sub(start_indx, (tab_indx or 0) - 1) + if tab_indx then + -- #new_line is index of this tab in new_line. + new_line = new_line + .. string.rep( + " ", + tabwidth + - ( + (parent_indent_displaycolumns + #new_line) + % tabwidth + ) + ) + else + -- reached end of string. + break + end + start_indx = tab_indx + 1 + end + text[i] = new_line + end + return text +end + +local function tab_width() + return vim.bo.shiftwidth ~= 0 and vim.bo.shiftwidth or vim.bo.tabstop +end + +local function mark_pos_equal(m1, m2) + local p1 = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, m1, {}) + local p2 = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, m2, {}) + return p1[1] == p2[1] and p1[2] == p2[2] +end + +local function move_to_mark(id) + local new_cur_pos + new_cur_pos = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + id, + { details = false } + ) + set_cursor_0ind(new_cur_pos) +end + +local function bytecol_to_utfcol(pos) + local line = vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false) + -- line[1]: get_lines returns table. + return { pos[1], vim.str_utfindex(line[1] or "", pos[2]) } +end + +local function replace_feedkeys(keys, opts) + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes(keys, true, false, true), + -- folds are opened manually now, no need to pass t. + -- n prevents langmap from interfering. + opts or "n", + true + ) +end + +-- pos: (0,0)-indexed. +local function cursor_set_keys(pos, before) + if before then + if pos[2] == 0 then + pos[1] = pos[1] - 1 + -- pos2 is set to last columnt of previous line. + -- # counts bytes, but win_set_cursor expects bytes, so all's good. + pos[2] = + #vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false)[1] + else + pos[2] = pos[2] - 1 + end + end + + return "lua vim.api.nvim_win_set_cursor(0,{" + -- +1, win_set_cursor starts at 1. + .. pos[1] + 1 + .. "," + -- -1 works for multibyte because of rounding, apparently. + .. pos[2] + .. "})" + .. ":silent! foldopen!" +end + +-- any for any mode. +-- other functions prefixed with eg. normal have to be in that mode, the +-- initial esc removes that need. +local function any_select(b, e) + -- stylua: ignore + replace_feedkeys( + -- this esc -> movement sometimes leads to a slight flicker + -- TODO: look into preventing that reliably. + -- Go into visual, then place endpoints. + -- This is to allow us to place the cursor on the \n of a line. + -- see #1158 + "" + -- open folds that contain this selection. + -- we assume that the selection is contained in at most one fold, and + -- that that fold covers b. + -- if we open the fold while visual is active, the selection will be + -- wrong, so this is necessary before we enter VISUAL. + .. cursor_set_keys(b) + -- start visual highlight and move to b again. + -- since we are now in visual, this might actually move the cursor. + .. "v" + .. cursor_set_keys(b) + -- swap to other end of selection, and move it to e. + .. "o" + .. (vim.o.selection == "exclusive" and + cursor_set_keys(e) or + -- set before + cursor_set_keys(e, true)) + .. "o_" ) +end + +local function normal_move_on_insert(new_cur_pos) + -- moving in normal and going into insert is kind of annoying, eg. when the + -- cursor is, in normal, on a tab, i will set it on the beginning of the + -- tab. There's more problems, but this is very safe. + replace_feedkeys("i" .. cursor_set_keys(new_cur_pos)) +end + +local function insert_move_on(new_cur_pos) + -- maybe feedkeys this too. + set_cursor_0ind(new_cur_pos) + vim.api.nvim_command("redraw!") +end + +local function multiline_equal(t1, t2) + for i, line in ipairs(t1) do + if line ~= t2[i] then + return false + end + end + + return #t1 == #t2 +end + +local function word_under_cursor(cur, line) + local ind_start = 1 + local ind_end = #line + + while true do + local tmp = string.find(line, "%W%w", ind_start) + if not tmp then + break + end + if tmp > cur[2] + 1 then + break + end + ind_start = tmp + 1 + end + + local tmp = string.find(line, "%w%W", cur[2] + 1) + if tmp then + ind_end = tmp + end + + return string.sub(line, ind_start, ind_end) +end + +-- Put text and update cursor(pos) where cursor is byte-indexed. +local function put(text, pos) + vim.api.nvim_buf_set_text(0, pos[1], pos[2], pos[1], pos[2], text) + -- add rows + pos[1] = pos[1] + #text - 1 + -- add columns, start at 0 if no rows were added, else at old col-value. + pos[2] = (#text > 1 and 0 or pos[2]) + #text[#text] +end + +--[[ Wraps the value in a table if it's not one, makes + the first element an empty str if the table is empty]] +local function to_string_table(value) + if not value then + return { "" } + end + if type(value) == "string" then + return { value } + end + -- at this point it's a table + if #value == 0 then + return { "" } + end + -- non empty table + return value +end + +-- Wrap node in a table if it is not one +local function wrap_nodes(nodes) + -- safe to assume, if nodes has a metatable, it is a single node, not a + -- table. + if getmetatable(nodes) and nodes.type then + return { nodes } + else + return nodes + end +end + +local function pos_equal(p1, p2) + return p1[1] == p2[1] and p1[2] == p2[2] +end + +local function string_wrap(lines, pos) + local new_lines = vim.deepcopy(lines) + if #new_lines == 1 and #new_lines[1] == 0 then + return { "$" .. (pos and tostring(pos) or "{}") } + end + new_lines[1] = "${" + .. (pos and (tostring(pos) .. ":") or "") + .. new_lines[1] + new_lines[#new_lines] = new_lines[#new_lines] .. "}" + return new_lines +end + +-- Heuristic to extract the comment style from the commentstring +local _comments_cache = {} +local function buffer_comment_chars() + local commentstring = vim.bo.commentstring + if _comments_cache[commentstring] then + return _comments_cache[commentstring] + end + local comments = { "//", "/*", "*/" } + local placeholder = "%s" + local index_placeholder = commentstring:find(vim.pesc(placeholder)) + if index_placeholder then + index_placeholder = index_placeholder - 1 + if index_placeholder + #placeholder == #commentstring then + comments[1] = vim.trim(commentstring:sub(1, -#placeholder - 1)) + else + comments[2] = vim.trim(commentstring:sub(1, index_placeholder)) + comments[3] = vim.trim( + commentstring:sub(index_placeholder + #placeholder + 1, -1) + ) + end + end + _comments_cache[commentstring] = comments + return comments +end + +local function to_line_table(table_or_string) + local tbl = to_string_table(table_or_string) + + -- split entries at \n. + local line_table = {} + for _, str in ipairs(tbl) do + local split = vim.split(str, "\n", true) + for i = 1, #split do + line_table[#line_table + 1] = split[i] + end + end + + return line_table +end + +local function find_outer_snippet(node) + while node.parent do + node = node.parent + end + return node +end + +local function redirect_filetypes(fts) + local snippet_fts = {} + + for _, ft in ipairs(fts) do + vim.list_extend(snippet_fts, session.ft_redirect[ft]) + end + + return snippet_fts +end + +local function deduplicate(list) + vim.validate({ list = { list, "table" } }) + local ret = {} + local contains = {} + for _, v in ipairs(list) do + if not contains[v] then + table.insert(ret, v) + contains[v] = true + end + end + return ret +end + +local function get_snippet_filetypes() + local config = require("luasnip.session").config + local fts = config.ft_func() + -- add all last. + table.insert(fts, "all") + + return deduplicate(redirect_filetypes(fts)) +end + +local function pos_add(p1, p2) + return { p1[1] + p2[1], p1[2] + p2[2] } +end +local function pos_sub(p1, p2) + return { p1[1] - p2[1], p1[2] - p2[2] } +end + +local function pop_front(list) + local front = list[1] + for i = 2, #list do + list[i - 1] = list[i] + end + list[#list] = nil + return front +end + +local function sorted_keys(t) + local s = {} + local i = 1 + for k, _ in pairs(t) do + s[i] = k + i = i + 1 + end + table.sort(s) + return s +end + +-- from https://www.lua.org/pil/19.3.html +local function key_sorted_pairs(t) + local sorted = sorted_keys(t) + local i = 0 + return function() + i = i + 1 + if sorted[i] == nil then + return nil + else + return sorted[i], t[sorted[i]], i + end + end +end + +local function no_region_check_wrap(fn, ...) + session.jump_active = true + -- will run on next tick, after autocommands (especially CursorMoved) for this are done. + vim.schedule(function() + session.jump_active = false + end) + return fn(...) +end + +local function id(a) + return a +end + +local function no() + return false +end + +local function yes() + return true +end + +local function reverse_lookup(t) + local rev = {} + for k, v in pairs(t) do + rev[v] = k + end + return rev +end + +local function nop() end + +local function indx_of(t, v) + for i, value in ipairs(t) do + if v == value then + return i + end + end + return nil +end + +local function ternary(cond, if_val, else_val) + if cond == true then + return if_val + else + return else_val + end +end + +-- just compare two integers. +local function cmp(i1, i2) + -- lets hope this ends up as one cmp. + if i1 < i2 then + return -1 + end + if i1 > i2 then + return 1 + end + return 0 +end + +-- compare two positions, <0 => pos1 pos1=pos2, >0 => pos1 > pos2. +local function pos_cmp(pos1, pos2) + -- if row is different it determines result, otherwise the column does. + return 2 * cmp(pos1[1], pos2[1]) + cmp(pos1[2], pos2[2]) +end + +return { + get_cursor_0ind = get_cursor_0ind, + set_cursor_0ind = set_cursor_0ind, + move_to_mark = move_to_mark, + normal_move_on_insert = normal_move_on_insert, + insert_move_on = insert_move_on, + any_select = any_select, + remove_n_before_cur = remove_n_before_cur, + get_current_line_to_cursor = get_current_line_to_cursor, + line_chars_before = line_chars_before, + mark_pos_equal = mark_pos_equal, + multiline_equal = multiline_equal, + word_under_cursor = word_under_cursor, + put = put, + to_string_table = to_string_table, + wrap_nodes = wrap_nodes, + pos_equal = pos_equal, + dedent = dedent, + indent = indent, + expand_tabs = expand_tabs, + tab_width = tab_width, + buffer_comment_chars = buffer_comment_chars, + string_wrap = string_wrap, + to_line_table = to_line_table, + find_outer_snippet = find_outer_snippet, + redirect_filetypes = redirect_filetypes, + get_snippet_filetypes = get_snippet_filetypes, + json_decode = vim.json.decode, + json_encode = vim.json.encode, + bytecol_to_utfcol = bytecol_to_utfcol, + pos_sub = pos_sub, + pos_add = pos_add, + deduplicate = deduplicate, + pop_front = pop_front, + key_sorted_pairs = key_sorted_pairs, + no_region_check_wrap = no_region_check_wrap, + id = id, + no = no, + yes = yes, + reverse_lookup = reverse_lookup, + nop = nop, + indx_of = indx_of, + ternary = ternary, + pos_cmp = pos_cmp, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/doc/luasnip.txt b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/doc/luasnip.txt new file mode 100644 index 00000000..e0ce8a2a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/doc/luasnip.txt @@ -0,0 +1,3709 @@ +*luasnip.txt* For NVIM v0.8.0 Last change: 2024 May 24 + +============================================================================== +Table of Contents *luasnip-table-of-contents* + +1. Basics |luasnip-basics| + - Jump-Index |luasnip-basics-jump-index| + - Adding Snippets |luasnip-basics-adding-snippets| + - Snippet Insertion |luasnip-basics-snippet-insertion| +2. Node |luasnip-node| + - Api |luasnip-node-api| +3. Snippets |luasnip-snippets| + - Data |luasnip-snippets-data| +4. TextNode |luasnip-textnode| +5. InsertNode |luasnip-insertnode| +6. FunctionNode |luasnip-functionnode| +7. Node Reference |luasnip-node-reference| +8. ChoiceNode |luasnip-choicenode| +9. SnippetNode |luasnip-snippetnode| +10. IndentSnippetNode |luasnip-indentsnippetnode| +11. DynamicNode |luasnip-dynamicnode| +12. RestoreNode |luasnip-restorenode| +13. Key Indexer |luasnip-key-indexer| +14. Absolute Indexer |luasnip-absolute-indexer| +15. MultiSnippet |luasnip-multisnippet| +16. Extras |luasnip-extras| + - Lambda |luasnip-extras-lambda| + - Match |luasnip-extras-match| + - Repeat |luasnip-extras-repeat| + - Partial |luasnip-extras-partial| + - Nonempty |luasnip-extras-nonempty| + - Dynamic Lambda |luasnip-extras-dynamic-lambda| + - FMT |luasnip-extras-fmt| + - Conditions |luasnip-extras-conditions| + - On The Fly-Snippets |luasnip-extras-on-the-fly-snippets| + - select_choice |luasnip-extras-select_choice| + - Filetype-Functions |luasnip-extras-filetype-functions| + - Postfix-Snippet |luasnip-extras-postfix-snippet| + - Treesitter-Postfix-Snippet |luasnip-extras-treesitter-postfix-snippet| + - Snippet List |luasnip-extras-snippet-list| + - Snippet Location |luasnip-extras-snippet-location| +17. Extend Decorator |luasnip-extend-decorator| +18. LSP-Snippets |luasnip-lsp-snippets| + - Snipmate Parser |luasnip-lsp-snippets-snipmate-parser| + - Transformations |luasnip-lsp-snippets-transformations| +19. Variables |luasnip-variables| + - Environment Namespaces |luasnip-variables-environment-namespaces| + - LSP-Variables |luasnip-variables-lsp-variables| +20. Loaders |luasnip-loaders| + - Snippet-specific filetypes |luasnip-loaders-snippet-specific-filetypes| + - VS-Code |luasnip-loaders-vs-code| + - SNIPMATE |luasnip-loaders-snipmate| + - Lua |luasnip-loaders-lua| + - edit_snippets |luasnip-loaders-edit_snippets| +21. SnippetProxy |luasnip-snippetproxy| +22. ext_opts |luasnip-ext_opts| +23. Docstrings |luasnip-docstrings| +24. Docstring-Cache |luasnip-docstring-cache| +25. Events |luasnip-events| +26. Cleanup |luasnip-cleanup| +27. Logging |luasnip-logging| +28. Source |luasnip-source| +29. Config-Options |luasnip-config-options| +30. Troubleshooting |luasnip-troubleshooting| + - Adding Snippets |luasnip-troubleshooting-adding-snippets| +31. API |luasnip-api| +> + __ ____ + /\ \ /\ _`\ __ + \ \ \ __ __ __ \ \,\L\_\ ___ /\_\ _____ + \ \ \ __/\ \/\ \ /'__`\\/_\__ \ /' _ `\/\ \/\ '__`\ + \ \ \L\ \ \ \_\ \/\ \L\.\_/\ \L\ \/\ \/\ \ \ \ \ \L\ \ + \ \____/\ \____/\ \__/.\_\ `\____\ \_\ \_\ \_\ \ ,__/ + \/___/ \/___/ \/__/\/_/\/_____/\/_/\/_/\/_/\ \ \/ + \ \_\ + \/_/ +< + +LuaSnip is a snippet engine written entirely in Lua. It has some great features +like inserting text (`luasnip-function-node`) or nodes (`luasnip-dynamic-node`) +based on user input, parsing LSP syntax and switching nodes +(`luasnip-choice-node`). For basic setup like mappings and installing, check +the README. + +All code snippets in this help assume the following: + +>lua + local ls = require("luasnip") + local s = ls.snippet + local sn = ls.snippet_node + local isn = ls.indent_snippet_node + local t = ls.text_node + local i = ls.insert_node + local f = ls.function_node + local c = ls.choice_node + local d = ls.dynamic_node + local r = ls.restore_node + local events = require("luasnip.util.events") + local ai = require("luasnip.nodes.absolute_indexer") + local extras = require("luasnip.extras") + local l = extras.lambda + local rep = extras.rep + local p = extras.partial + local m = extras.match + local n = extras.nonempty + local dl = extras.dynamic_lambda + local fmt = require("luasnip.extras.fmt").fmt + local fmta = require("luasnip.extras.fmt").fmta + local conds = require("luasnip.extras.expand_conditions") + local postfix = require("luasnip.extras.postfix").postfix + local types = require("luasnip.util.types") + local parse = require("luasnip.util.parser").parse_snippet + local ms = ls.multi_snippet + local k = require("luasnip.nodes.key_indexer").new_key +< + +As noted in the |luasnip-loaders-lua|-section: + + + By default, the names from `luasnip.config.snip_env` + + will be used, but it’s possible to customize them by setting `snip_env` in + `setup`. +Furthermore, note that while this document assumes you have defined `ls` to be +`require("luasnip")`, it is **not** provided in the default set of variables. + + +============================================================================== +1. Basics *luasnip-basics* + +In LuaSnip, snippets are made up of `nodes`. These can contain either + +- static text (`textNode`) +- text that can be edited (`insertNode`) +- text that can be generated from the contents of other nodes (`functionNode`) +- other nodes + - `choiceNode`: allows choosing between two nodes (which might contain more + nodes) + - `restoreNode`: store and restore input to nodes +- or nodes that can be generated based on input (`dynamicNode`). + +Snippets are always created using the `s(trigger:string, +nodes:table)`-function. It is explained in more detail in |luasnip-snippets|, +but the gist is that it creates a snippet that contains the nodes specified in +`nodes`, which will be inserted into a buffer if the text before the cursor +matches `trigger` when `ls.expand` is called. + + +JUMP-INDEX *luasnip-basics-jump-index* + +Nodes that can be jumped to (`insertNode`, `choiceNode`, `dynamicNode`, +`restoreNode`, `snippetNode`) all require a "jump-index" so luasnip knows the +order in which these nodes are supposed to be visited ("jumped to"). + +>lua + s("trig", { + i(1), t"text", i(2), t"text again", i(3) + }) +< + +These indices don’t "run" through the entire snippet, like they do in +textmate-snippets (`"$1 ${2: $3 $4}"`), they restart at 1 in each nested +snippetNode: + +>lua + s("trig", { + i(1), t" ", sn(2, { + t" ", i(1), t" ", i(2) + }) + }) +< + +(roughly equivalent to the given textmate-snippet). + + +ADDING SNIPPETS *luasnip-basics-adding-snippets* + +The snippets for a given filetype have to be added to luasnip via +`ls.add_snippets(filetype, snippets)`. Snippets that should be accessible +globally (in all filetypes) have to be added to the special filetype `all`. + +>lua + ls.add_snippets("all", { + s("ternary", { + -- equivalent to "${1:cond} ? ${2:then} : ${3:else}" + i(1, "cond"), t(" ? "), i(2, "then"), t(" : "), i(3, "else") + }) + }) +< + +It is possible to make snippets from one filetype available to another using +`ls.filetype_extend`, more info on that in the section |luasnip-api|. + + +SNIPPET INSERTION *luasnip-basics-snippet-insertion* + +When a new snippet is expanded, it can be connected with the snippets that have +already been expanded in the buffer in various ways. First of all, Luasnip +distinguishes between root-snippets and child-snippets. The latter are nested +inside other snippets, so when jumping through a snippet, one may also traverse +the child-snippets expanded inside it, more or less as if the child just +contains more nodes of the parent. Root-snippets are of course characterised by +not being child-snippets. When expanding a new snippet, it becomes a child of +the snippet whose region it is expanded inside, and a root if it is not inside +any snippet’s region. If it is inside another snippet, the specific node it +is inside is determined, and the snippet then nested inside that node. + +- If that node is interactive (for example, an `insertNode`), the new snippet + will be traversed when the node is visited, as long as the + configuration-option `link_children` is enabled. If it is not enabled, it is + possible to jump from the snippet to the node, but not the other way around. +- If that node is not interactive, the snippet will be linked to the currently + active node, also such that it will not be jumped to again once it is left. + This is to prevent jumping large distances across the buffer as much as + possible. There may still be one large jump from the snippet back to the + current node it is nested inside, but that seems hard to avoid. + Thus, one should design snippets such that the regions where other snippets + may be expanded are inside `insertNodes`. + +If the snippet is not a child, but a root, it can be linked up with the roots +immediately adjacent to it by enabling `link_roots` in `setup`. Since by +default only one root is remembered, one should also set `keep_roots` if +`link_roots` is enabled. The two are separate options, since roots that are not +linked can still be reached by `ls.activate_node()`. This setup (remember +roots, but don’t jump to them) is useful for a super-tab like mapping +(`` and jump on the same key), where one would like to still enter +previous roots. Since there would almost always be more jumps if the roots are +linked, regular `` would not work almost all the time, and thus +`link_roots` has to stay disabled. + + +============================================================================== +2. Node *luasnip-node* + +Every node accepts, as its last parameter, an optional table of arguments. +There are some common ones (which are listed here), and some that only apply to +some nodes (`user_args` for function/dynamicNode). These `opts` are only +mentioned if they accept options that are not common to all nodes. + +Common opts: + +- `node_ext_opts` and `merge_node_ext_opts`: Control `ext_opts` (most likely + highlighting) of the node. Described in detail in |luasnip-ext_opts| +- `key`: The node can be reffered to by this key. Useful for either |luasnip-key-indexer| or for finding the node at runtime (See + |luasnip-snippets-api|), for example inside a `dynamicNode`. The keys + do not have to be unique across the entire lifetime of the snippet, but at any + point in time, the snippet may contain each key only once. This means it is + fine to return a keyed node from a `dynamicNode`, because even if it will be + generated multiple times, those will not be valid at the same time. +- `node_callbacks`: Define event-callbacks for this node (see + |luasnip-events|). + Accepts a table that maps an event, e.g. `events.enter` to the callback + (essentially the same as `callbacks` passed to `s`, only that there is no + first mapping from jump-index to the table of callbacks). + + +API *luasnip-node-api* + +- `get_jump_index()`: this method returns the jump-index of a node. If a node + doesn’t have a jump-index, this method returns `nil` instead. +- `get_buf_position(opts) -> {from_position, to_position}`: + Determines the range of the buffer occupied by this node. `from`- and + `to_position` are `row,column`-tuples, `0,0`-indexed (first line is 0, first + column is 0) and end-inclusive (see |api-indexing|, this is extmarks + indexing). + - `opts`: `table|nil`, options, valid keys are: + - `raw`: `bool`, default `true`. This can be used to switch between + byte-columns (`raw=true`) and visual columns (`raw=false`). This makes a + difference if the line contains characters represented by multiple bytes + in UTF, for example `ÿ`. + + +============================================================================== +3. Snippets *luasnip-snippets* + +The most direct way to define snippets is `s`: + +>lua + s({trig="trigger"}, {}) +< + +(This snippet is useless beyond serving as a minimal example) + +`s(context, nodes, opts) -> snippet` + +- `context`: Either table or a string. Passing a string is equivalent to passing + >lua + { + trig = context + } + < + The following keys are valid: + - `trig`: string, the trigger of the snippet. If the text in front of (to the + left of) the cursor when `ls.expand()` is called matches it, the snippet will + be expanded. By default, "matches" means the text in front of the cursor + matches the trigger exactly, this behaviour can be modified through + `trigEngine` + - `name`: string, can be used by e.g. `nvim-compe` to identify the snippet. + - `desc` (or `dscr`): string, description of the snippet, -separated or table for + multiple lines. + - `wordTrig`: boolean, if true, the snippet is only expanded if the word + (`[%w_]+`) before the cursor matches the trigger entirely. True by default. + - `regTrig`: boolean, whether the trigger should be interpreted as a lua pattern. + False by default. Consider setting `trigEngine` to `"pattern"` instead, it is + more expressive, and in line with other settings. + - `trigEngine`: (function|string), determines how `trig` is interpreted, and what + it means for it to "match" the text in front of the cursor. This behaviour can + be completely customized by passing a function, but the predefined ones, which + are accessible by passing their identifier, should suffice in most cases: + - `"plain"`: the default-behaviour, the trigger has to match the text before + the cursor exactly. + - `"pattern"`: the trigger is interpreted as a lua-pattern, and is a match if + `trig .. "$"` matches the line up to the cursor. Capture-groups will be + accessible as `snippet.captures`. + - `"ecma"`: the trigger is interpreted as an ECMAscript-regex, and is a + match if `trig .. "$"` matches the line up to the cursor. Capture-groups + will be accessible as `snippet.captures`. + This `trigEngine` requires `jsregexp` (see + |luasnip-lsp-snippets-transformations|) to be installed, if it + is not, this engine will behave like `"plain"`. + - `"vim"`: the trigger is interpreted as a vim-regex, and is a match if + `trig .. "$"` matches the line up to the cursor. As with the other + regex/pattern-engines, captures will be available as `snippet.captures`, + but there is one caveat: the matching is done using `matchlist`, so for + now empty-string submatches will be interpreted as unmatched, and the + corresponding `snippet.capture[i]` will be `nil` (this will most likely + change, don’t rely on this behavior). + Besides these predefined engines, it is also possible to create new ones: + Instead of a string, pass a function which satisfies `trigEngine(trigger, opts) + -> (matcher(line_to_cursor, trigger) -> whole_match, captures)` (ie. the + function receives `trig` and `trigEngineOpts` can, for example, precompile a + regex, and then returns a function responsible for determining whether the + current cursor-position (represented by the line up to the cursor) matches the + trigger (it is passed again here so engines which don’t do any + trigger-specific work (like compilation) can just return a static `matcher`), + and what the capture-groups are). The `lua`-engine, for example, can be + implemented like this: + >lua + local function matcher(line_to_cursor, trigger) + -- look for match which ends at the cursor. + -- put all results into a list, there might be many capture-groups. + local find_res = { line_to_cursor:find(trigger .. "$") } + + if #find_res > 0 then + -- if there is a match, determine matching string, and the + -- capture-groups. + local captures = {} + -- find_res[1] is `from`, find_res[2] is `to` (which we already know + -- anyway). + local from = find_res[1] + local match = line_to_cursor:sub(from, #line_to_cursor) + -- collect capture-groups. + for i = 3, #find_res do + captures[i - 2] = find_res[i] + end + return match, captures + else + return nil + end + end + + local function engine(trigger) + -- don't do any special work here, can't precompile lua-pattern. + return matcher + end + < + The predefined engines are defined in `trig_engines.lua` + , + read it for more examples. + - `trigEngineOpts`: `table`, options for the used trigEngine. The + valid options are: + - `max_len`: number, upper bound on the length of the trigger. + If this is set, the `line_to_cursor` will be truncated (from the cursor of + course) to `max_len` characters before performing the match. + This is implemented because feeding long `line_to_cursor` into eg. the + pattern-trigEngine will hurt performance quite a bit (see issue + Luasnip#1103). + This option is implemented for all `trigEngines`. + - `docstring`: string, textual representation of the snippet, specified like + `desc`. Overrides docstrings loaded from json. + - `docTrig`: string, used as `line_to_cursor` during docstring-generation. This + might be relevant if the snippet relies on specific values in the + capture-groups (for example, numbers, which won’t work with the default + `$CAPTURESN` used during docstring-generation) + - `hidden`: boolean, hint for completion-engines. If set, the snippet should not + show up when querying snippets. + - `priority`: positive number, Priority of the snippet, 1000 by default. Snippets + with high priority will be matched to a trigger before those with a lower one. + The priority for multiple snippets can also be set in `add_snippets`. + - `snippetType`: string, should be either `snippet` or `autosnippet` (ATTENTION: + singular form is used), decides whether this snippet has to be triggered by + `ls.expand()` or whether is triggered automatically (don’t forget to set + `ls.config.setup({ enable_autosnippets = true })` if you want to use this + feature). If unset it depends on how the snippet is added of which type the + snippet will be. + - `resolveExpandParams`: `fn(snippet, line_to_cursor, matched_trigger, captures) + -> table|nil`, where + - `snippet`: `Snippet`, the expanding snippet object + - `line_to_cursor`: `string`, the line up to the cursor. + - `matched_trigger`: `string`, the fully matched trigger (can be retrieved + from `line_to_cursor`, but we already have that info here :D) + - `captures`: `captures` as returned by `trigEngine`. + This function will be evaluated in `Snippet:matches()` to decide whether the + snippet can be expanded or not. Returns a table if the snippet can be expanded, + `nil` if can not. The returned table can contain any of these fields: + - `trigger`: `string`, the fully matched trigger. + - `captures`: `table`, this list could update the capture-groups from + parameter in snippet expansion. + Both `trigger` and `captures` can override the values returned via + `trigEngine`. + - `clear_region`: `{ "from": {, }, "to": {, } }`, + both (0, 0)-indexed, the region where text has to be cleared before + inserting the snippet. + - `env_override`: `map string->(string[]|string)`, override or extend + the snippet’s environment (`snip.env`) + If any of these is `nil`, the default is used (`trigger` and `captures` as + returned by `trigEngine`, `clear_region` such that exactly the trigger is + deleted, no overridden environment-variables). + A good example for the usage of `resolveExpandParams` can be found in the + implementation of `postfix` + . + - `condition`: `fn(line_to_cursor, matched_trigger, captures) -> bool`, where + - `line_to_cursor`: `string`, the line up to the cursor. + - `matched_trigger`: `string`, the fully matched trigger (can be retrieved + from `line_to_cursor`, but we already have that info here :D) + - `captures`: if the trigger is pattern, this list contains the + capture-groups. Again, could be computed from `line_to_cursor`, but we + already did so. + - `show_condition`: `f(line_to_cursor) -> bool`. + - `line_to_cursor`: `string`, the line up to the cursor. + This function is (should be) evaluated by completion engines, indicating + whether the snippet should be included in current completion candidates. + Defaults to a function returning `true`. This is different from `condition` + because `condition` is evaluated by LuaSnip on snippet expansion (and thus has + access to the matched trigger and captures), while `show_condition` is (should + be) evaluated by the completion engines when scanning for available snippet + candidates. + - `filetype`: `string`, the filetype of the snippet. This overrides the filetype + the snippet is added (via `add_snippet`) as. +- `nodes`: A single node or a list of nodes. The nodes that make up the snippet. +- `opts`: A table with the following valid keys: + - `callbacks`: Contains functions that are called upon entering/leaving a node of + this snippet. For example: to print text upon entering the _second_ node of a + snippet, `callbacks` should be set as follows: + >lua + { + -- position of the node, not the jump-index!! + -- s("trig", {t"first node", t"second node", i(1, "third node")}). + [2] = { + [events.enter] = function(node, _event_args) print("2!") end + } + } + < + To register a callback for the snippets’ own events, the key `[-1]` may be + used. More info on events in |luasnip-events| + - `child_ext_opts`, `merge_child_ext_opts`: Control `ext_opts` applied to the + children of this snippet. More info on those in the |luasnip-ext_opts|-section. + +The `opts`-table, as described here, can also be passed to e.g. `snippetNode` +and `indentSnippetNode`. It is also possible to set `condition` and +`show_condition` (described in the documentation of the `context`-table) from +`opts`. They should, however, not be set from both. + + +DATA *luasnip-snippets-data* + +Snippets contain some interesting tables during runtime: + +- `snippet.env`: Contains variables used in the LSP-protocol, for example + `TM_CURRENT_LINE` or `TM_FILENAME`. It’s possible to add customized variables + here too, check |luasnip-variables-environment-namespaces| +- `snippet.captures`: If the snippet was triggered by a pattern (`regTrig`), and + the pattern contained capture-groups, they can be retrieved here. +- `snippet.trigger`: The string that triggered this snippet. Again, only + interesting if the snippet was triggered through `regTrig`, for getting the + full match. + +These variables/tables primarily come in handy in `dynamic/functionNodes`, +where the snippet can be accessed through the immediate parent +(`parent.snippet`), which is passed to the function. (in most cases `parent == +parent.snippet`, but the `parent` of the dynamicNode is not always the +surrounding snippet, it could be a `snippetNode`). + +## Api + +- `invalidate()`: call this method to effectively remove the snippet. The + snippet will no longer be able to expand via `expand` or `expand_auto`. It + will also be hidden from lists (at least if the plugin creating the list + respects the `hidden`-key), but it might be necessary to call + `ls.refresh_notify(ft)` after invalidating snippets. +- `get_keyed_node(key)`: Returns the currently visible node associated with + `key`. + + +============================================================================== +4. TextNode *luasnip-textnode* + +The most simple kind of node; just text. + +>lua + s("trigger", { t("Wow! Text!") }) +< + +This snippet expands to + +> + Wow! Text!⎵ +< + +where ⎵ is the cursor. + +Multiline strings can be defined by passing a table of lines rather than a +string: + +>lua + s("trigger", { + t({"Wow! Text!", "And another line."}) + }) +< + +`t(text, node_opts)`: + +- `text`: `string` or `string[]` +- `node_opts`: `table`, see |luasnip-node| + + +============================================================================== +5. InsertNode *luasnip-insertnode* + +These Nodes contain editable text and can be jumped to- and from (e.g. +traditional placeholders and tabstops, like `$1` in textmate-snippets). + +The functionality is best demonstrated with an example: + +>lua + s("trigger", { + t({"After expanding, the cursor is here ->"}), i(1), + t({"", "After jumping forward once, cursor is here ->"}), i(2), + t({"", "After jumping once more, the snippet is exited there ->"}), i(0), + }) +< + +The Insert Nodes are visited in order `1,2,3,..,n,0`. (The jump-index 0 also +_has_ to belong to an `insertNode`!) So the order of InsertNode-jumps is as +follows: + +1. After expansion, the cursor is at InsertNode 1, +2. after jumping forward once at InsertNode 2, +3. and after jumping forward again at InsertNode 0. + +If no 0-th InsertNode is found in a snippet, one is automatically inserted +after all other nodes. + +The jump-order doesn’t have to follow the "textual" order of the nodes: + +>lua + s("trigger", { + t({"After jumping forward once, cursor is here ->"}), i(2), + t({"", "After expanding, the cursor is here ->"}), i(1), + t({"", "After jumping once more, the snippet is exited there ->"}), i(0), + }) +< + +The above snippet will behave as follows: + +1. After expansion, we will be at InsertNode 1. +2. After jumping forward, we will be at InsertNode 2. +3. After jumping forward again, we will be at InsertNode 0. + +An **important** (because here Luasnip differs from other snippet engines) +detail is that the jump-indices restart at 1 in nested snippets: + +>lua + s("trigger", { + i(1, "First jump"), + t(" :: "), + sn(2, { + i(1, "Second jump"), + t" : ", + i(2, "Third jump") + }) + }) +< + +as opposed to e.g. the textmate syntax, where tabstops are snippet-global: + +>snippet + ${1:First jump} :: ${2: ${3:Third jump} : ${4:Fourth jump}} +< + +(this is not exactly the same snippet of course, but as close as possible) (the +restart-rule only applies when defining snippets in lua, the above +textmate-snippet will expand correctly when parsed). + +`i(jump_index, text, node_opts)` + +- `jump_index`: `number`, this determines when this node will be jumped to (see + |luasnip-basics-jump-index|). +- `text`: `string|string[]`, a single string for just one line, a list with >1 + entries for multiple lines. + This text will be SELECTed when the `insertNode` is jumped into. +- `node_opts`: `table`, described in |luasnip-node| + +If the `jump_index` is `0`, replacing its’ `text` will leave it outside the +`insertNode` (for reasons, check out Luasnip#110). + + +============================================================================== +6. FunctionNode *luasnip-functionnode* + +Function Nodes insert text based on the content of other nodes using a +user-defined function: + +>lua + local function fn( + args, -- text from i(2) in this example i.e. { { "456" } } + parent, -- parent snippet or parent node + user_args -- user_args from opts.user_args + ) + return '[' .. args[1][1] .. user_args .. ']' + end + + s("trig", { + i(1), t '<-i(1) ', + f(fn, -- callback (args, parent, user_args) -> string + {2}, -- node indice(s) whose text is passed to fn, i.e. i(2) + { user_args = { "user_args_value" }} -- opts + ), + t ' i(2)->', i(2), t '<-i(2) i(0)->', i(0) + }) +< + +`f(fn, argnode_references, node_opts)`: - `fn`: `function(argnode_text, parent, +user_args1,...,user_argsn) -> text` - `argnode_text`: `string[][]`, the text +currently contained in the argnodes (e.g. `{{line1}, {line1, line2}}`). The +snippet indent will be removed from all lines following the first. + +- `parent`: The immediate parent of the `functionNode`. It is included here as it + allows easy access to some information that could be useful in functionNodes + (see |luasnip-snippets-data| for some examples). Many snippets access the + surrounding snippet just as `parent`, but if the `functionNode` is nested + within a `snippetNode`, the immediate parent is a `snippetNode`, not the + surrounding snippet (only the surrounding snippet contains data like `env` or + `captures`). +- `user_args`: The `user_args` passed in `opts`. Note that there may be multiple + user_args (e.g. `user_args1, ..., user_argsn`). + +`fn` shall return a string, which will be inserted as is, or a table of strings +for multiline strings, where all lines following the first will be prefixed +with the snippets’ indentation. + +- `argnode_references`: `node_reference[]|node_refernce|nil`. Either no, a + single, or multiple |luasnip-node-reference|s. Changing any of these will + trigger a re-evaluation of `fn`, and insertion of the updated text. If no node + reference is passed, the `functionNode` is evaluated once upon expansion. +- `node_opts`: `table`, see |luasnip-node|. One additional key is supported: + - `user_args`: `any[]`, these will be passed to `fn` as `user_arg1`-`user_argn`. + These make it easier to reuse similar functions, for example a functionNode + that wraps some text in different delimiters (`()`, `[]`, …). + >lua + local function reused_func(_,_, user_arg1) + return user_arg1 + end + + s("trig", { + f(reused_func, {}, { + user_args = {"text"} + }), + f(reused_func, {}, { + user_args = {"different text"} + }), + }) + < + +**Examples**: + +- Use captures from the regex trigger using a functionNode: + >lua + s({trig = "b(%d)", regTrig = true}, + f(function(args, snip) return + "Captured Text: " .. snip.captures[1] .. "." end, {}) + ) + < +- `argnodes_text` during function evaluation: + >lua + s("trig", { + i(1, "text_of_first"), + i(2, {"first_line_of_second", "second_line_of_second"}), + f(function(args, snip) + --here + -- order is 2,1, not 1,2!! + end, {2, 1} )}) + < + At `--here`, `args` would look as follows (provided no text was changed after + expansion): + >lua + args = { + {"first_line_of_second", "second_line_of_second"}, + {"text_of_first"} + } + < +- |luasnip-absolute-indexer|: + >lua + s("trig", { + i(1, "text_of_first"), + i(2, {"first_line_of_second", "second_line_of_second"}), + f(function(args, snip) + -- just concat first lines of both. + return args[1][1] .. args[2][1] + end, {ai[2], ai[1]} )}) + < + +If the function only performs simple operations on text, consider using the +`lambda` from `luasnip.extras` (See |luasnip-extras-lambda|) + + +============================================================================== +7. Node Reference *luasnip-node-reference* + +Node references are used to refer to other nodes in various parts of +luasnip’s API. For example, argnodes in functionNode, dynamicNode or lambda +are node references. These references can be either of: + +- `number`: the jump-index of the node. + This will be resolved relative to the parent of the node this is passed to. + (So, only nodes with the same parent can be referenced. This is very easy to + grasp, but also limiting) +- `key_indexer`: the key of the node, if it is present. This will come in + handy if the node that is being referred to is not in the same + snippet/snippetNode as the one the node reference is passed to. + Also, it is the proper way to refer to a non-interactive node (a + functionNode, for example) +- `absolute_indexer`: the absolute position of the node. Just like + `key_indexer`, it allows addressing non-sibling nodes, but is a bit more + awkward to handle since a path from root to node has to be determined, + whereas `key_indexer` just needs the key to match. + Due to this, `key_indexer` should be generally preferred. + (More information in |luasnip-absolute-indexer|). +- `node`: just the node. Usage of this is discouraged since it can lead to + subtle errors (for example, if the node passed here is captured in a closure + and therefore not copied with the remaining tables in the snippet; there’s a + big comment about just this in commit 8bfbd61). + + +============================================================================== +8. ChoiceNode *luasnip-choicenode* + +ChoiceNodes allow choosing between multiple nodes. + +>lua + s("trig", c(1, { + t("Ugh boring, a text node"), + i(nil, "At least I can edit something now..."), + f(function(args) return "Still only counts as text!!" end, {}) + })) +< + +`c(jump_index, choices, node_opts)` + +- `jump_index`: `number`, since choiceNodes can be jumped to, they need a + jump-index (Info in |luasnip-basics-jump-index|). +- `choices`: `node[]|node`, the choices. The first will be initialliy active. + A list of nodes will be turned into a `snippetNode`. +- `node_opts`: `table`. `choiceNode` supports the keys common to all nodes + described in |luasnip-node|, and one additional key: + - `restore_cursor`: `false` by default. If it is set, and the node that was being + edited also appears in the switched to choice (can be the case if a + `restoreNode` is present in both choice) the cursor is restored relative to + that node. The default is `false` as enabling might lead to decreased + performance. It’s possible to override the default by wrapping the + `choiceNode` constructor in another function that sets `opts.restore_cursor` to + `true` and then using that to construct `choiceNode`s: + >lua + local function restore_cursor_choice(pos, choices, opts) + if opts then + opts.restore_cursor = true + else + opts = {restore_cursor = true} + end + return c(pos, choices, opts) + end + < + +Jumpable nodes that normally expect an index as their first parameter don’t +need one inside a choiceNode; their jump-index is the same as the +choiceNodes’. + +As it is only possible (for now) to change choices from within the choiceNode, +make sure that all of the choices have some place for the cursor to stop at! + +This means that in `sn(nil, {...nodes...})` `nodes` has to contain e.g. an +`i(1)`, otherwise luasnip will just "jump through" the nodes, making it +impossible to change the choice. + +>lua + c(1, { + t"some text", -- textNodes are just stopped at. + i(nil, "some text"), -- likewise. + sn(nil, {t"some text"}) -- this will not work! + sn(nil, {i(1), t"some text"}) -- this will. + }) +< + +The active choice for a choiceNode can be changed by either calling one of +`ls.change_choice(1)` (forwards) or `ls.change_choice(-1)` (backwards), or by +calling `ls.set_choice(choice_indx)`. + +One way to easily interact with choiceNodes is binding `change_choice(1/-1)` to +keys: + +>lua + -- set keybinds for both INSERT and VISUAL. + vim.api.nvim_set_keymap("i", "", "luasnip-next-choice", {}) + vim.api.nvim_set_keymap("s", "", "luasnip-next-choice", {}) + vim.api.nvim_set_keymap("i", "", "luasnip-prev-choice", {}) + vim.api.nvim_set_keymap("s", "", "luasnip-prev-choice", {}) +< + +Apart from this, there is also a picker (see |luasnip-select_choice| where no +cycling is necessary and any choice can be selected right away, via +`vim.ui.select`. + + +============================================================================== +9. SnippetNode *luasnip-snippetnode* + +SnippetNodes directly insert their contents into the surrounding snippet. This +is useful for `choiceNode`s, which only accept one child, or `dynamicNode`s, +where nodes are created at runtime and inserted as a `snippetNode`. + +Their syntax is similar to `s`, however, where snippets require a table +specifying when to expand, `snippetNode`s, similar to `insertNode`s, expect a +jump-index. + +>lua + s("trig", sn(1, { + t("basically just text "), + i(1, "And an insertNode.") + })) +< + +`sn(jump_index, nodes, node_opts)` + +- `jump_index`: `number`, the usual |luasnip-jump-index|. +- `nodes`: `node[]|node`, just like for `s`. + Note that `snippetNode`s don’t accept an `i(0)`, so the jump-indices of the nodes + inside them have to be in `1,2,...,n`. +- `node_opts`: `table`: again, the keys common to all nodes (documented in + |luasnip-node|) are supported, but also + - `callbacks`, + - `child_ext_opts` and + - `merge_child_ext_opts`, + which are further explained in |luasnip-snippets|. + + +============================================================================== +10. IndentSnippetNode *luasnip-indentsnippetnode* + +By default, all nodes are indented at least as deep as the trigger. With these +nodes it’s possible to override that behaviour: + +>lua + s("isn", { + isn(1, { + t({"This is indented as deep as the trigger", + "and this is at the beginning of the next line"}) + }, "") + }) +< + +(Note the empty string passed to isn). + +Indent is only applied after linebreaks, so it’s not possible to remove +indent on the line where the snippet was triggered using `ISN` (That is +possible via regex triggers where the entire line before the trigger is +matched). + +Another nice use case for `ISN` is inserting text, e.g. `//` or some other +comment string before the nodes of the snippet: + +>lua + s("isn2", { + isn(1, t({"//This is", "A multiline", "comment"}), "$PARENT_INDENT//") + }) +< + +Here the `//` before `This is` is important, once again, because indent is only +applied after linebreaks. + +To enable such usage, `$PARENT_INDENT` in the indentstring is replaced by the +parent’s indent. + +`isn(jump_index, nodes, indentstring, node_opts)` + +All of these parameters except `indentstring` are exactly the same as in +|luasnip-snippetnode|. + +- `indentstring`: `string`, will be used to indent the nodes inside this + `snippetNode`. + All occurences of `"$PARENT_INDENT"` are replaced with the actual indent of + the parent. + + +============================================================================== +11. DynamicNode *luasnip-dynamicnode* + +Very similar to functionNode, but returns a snippetNode instead of just text, +which makes them very powerful as parts of the snippet can be changed based on +user input. + +`d(jump_index, function, node-references, opts)`: + +- `jump_index`: `number`, just like all jumpable nodes, its’ position in the + jump-list (|luasnip-basics-jump-index|). +- `function`: `fn(args, parent, old_state, user_args) -> snippetNode` This + function is called when the argnodes’ text changes. It should generate and + return (wrapped inside a `snippetNode`) nodes, which will be inserted at the + dynamicNode’s place. `args`, `parent` and `user_args` are also explained in + |luasnip-functionnode| + - `args`: `table of text` (`{{"node1line1", "node1line2"}, {"node2line1"}}`) + from nodes the `dynamicNode` depends on. + - `parent`: the immediate parent of the `dynamicNode`. + - `old_state`: a user-defined table. This table may contain anything; its + intended usage is to preserve information from the previously generated + `snippetNode`. If the `dynamicNode` depends on other nodes, it may be + reconstructed, which means all user input (text inserted in `insertNodes`, + changed choices) to the previous `dynamicNode` is lost. + The `old_state` table must be stored in `snippetNode` returned by + the function (`snippetNode.old_state`). + The second example below illustrates the usage of `old_state`. + - `user_args`: passed through from `dynamicNode`-opts; may have more than one + argument. +- `node_references`: `node_reference[]|node_references|nil`, + |luasnip-node-references| to the nodes the dynamicNode depends on: if any of + these trigger an update (for example, if the text inside them changes), the + `dynamicNode`s’ function will be executed, and the result inserted at the + `dynamicNode`s place. (`dynamicNode` behaves exactly the same as `functionNode` + in this regard). +- `opts`: In addition to the common |luasnip-node|-keys, there is, again, + - `user_args`, which is described in |luasnip-functionnode|. + +**Examples**: + +This `dynamicNode` inserts an `insertNode` which copies the text inside the +first `insertNode`. + +>lua + s("trig", { + t"text: ", i(1), t{"", "copy: "}, + d(2, function(args) + -- the returned snippetNode doesn't need a position; it's inserted + -- "inside" the dynamicNode. + return sn(nil, { + -- jump-indices are local to each snippetNode, so restart at 1. + i(1, args[1]) + }) + end, + {1}) + }) +< + +This snippet makes use of `old_state` to count the number of updates. + +To store/restore values generated by the `dynamicNode` or entered into +`insert/choiceNode`, consider using the shortly-introduced `restoreNode` +instead of `old_state`. + +>lua + local function count(_, _, old_state) + old_state = old_state or { + updates = 0 + } + + old_state.updates = old_state.updates + 1 + + local snip = sn(nil, { + t(tostring(old_state.updates)) + }) + + snip.old_state = old_state + return snip + end + + ls.add_snippets("all", + s("trig", { + i(1, "change to update"), + d(2, count, {1}) + }) + ) +< + +As with `functionNode`, `user_args` can be used to reuse similar `dynamicNode`- +functions. + + +============================================================================== +12. RestoreNode *luasnip-restorenode* + +This node can store and restore a snippetNode as is. This includes changed +choices and changed text. Its’ usage is best demonstrated by an example: + +>lua + s("paren_change", { + c(1, { + sn(nil, { t("("), r(1, "user_text"), t(")") }), + sn(nil, { t("["), r(1, "user_text"), t("]") }), + sn(nil, { t("{"), r(1, "user_text"), t("}") }), + }), + }, { + stored = { + -- key passed to restoreNodes. + ["user_text"] = i(1, "default_text") + } + }) +< + +Here the text entered into `user_text` is preserved upon changing choice. + +`r(jump_index, key, nodes, node_opts)`: + +- `jump_index`, when to jump to this node. +- `key`, `string`: `restoreNode`s with the same key share their content. +- `nodes`, `node[]|node`: the content of the `restoreNode`. + Can either be a single node, or a table of nodes (both of which will be + wrapped inside a `snippetNode`, except if the single node already is a + `snippetNode`). + The content for a given key may be defined multiple times, but if the + contents differ, it’s undefined which will actually be used. + If a key’s content is defined in a `dynamicNode`, it will not be initially + used for `restoreNodes` outside that `dynamicNode`. A way around this + limitation is defining the content in the `restoreNode` outside the + `dynamicNode`. + +The content for a key may also be defined in the `opts`-parameter of the +snippet-constructor, as seen in the example above. The `stored`-table accepts +the same values as the `nodes`-parameter passed to `r`. If no content is +defined for a key, it defaults to the empty `insertNode`. + +An important-to-know limitation of `restoreNode` is that, for a given key, only +one may be visible at a time. See this issue + for details. + +The `restoreNode` is especially useful for storing input across updates of a +`dynamicNode`. Consider this: + +>lua + local function simple_restore(args, _) + return sn(nil, {i(1, args[1]), i(2, "user_text")}) + end + + s("rest", { + i(1, "preset"), t{"",""}, + d(2, simple_restore, 1) + }) +< + +Every time the `i(1)` in the outer snippet is changed, the text inside the +`dynamicNode` is reset to `"user_text"`. This can be prevented by using a +`restoreNode`: + +>lua + local function simple_restore(args, _) + return sn(nil, {i(1, args[1]), r(2, "dyn", i(nil, "user_text"))}) + end + + s("rest", { + i(1, "preset"), t{"",""}, + d(2, simple_restore, 1) + }) +< + +Now the entered text is stored. + +`restoreNode`s indent is not influenced by `indentSnippetNodes` right now. If +that really bothers you feel free to open an issue. + + +============================================================================== +13. Key Indexer *luasnip-key-indexer* + +A very flexible way of referencing nodes (|luasnip-node-reference|). While the +straightforward way of addressing nodes via their |luasnip-jump-index| suffices +in most cases, a `dynamic/functionNode` can only depend on nodes in the same +snippet(Node), its siblings (since the index is interpreted as relative to +their parent). Accessing a node with a different parent is thus not possible. +Secondly, and less relevant, only nodes that actually have a jump-index can be +referred to (a `functionNode`, for example, cannot be depended on). Both of +these restrictions are lifted with `key_indexer`: It allows addressing nodes by +their key, which can be set when the node is constructed, and is wholly +independent of the nodes’ position in the snippet, thus enabling descriptive +labeling. + +The following snippets demonstrate the issue and the solution by using +`key_indexer`: + +First, the addressed problem of referring to nodes outside the `functionNode`s +parent: + +>lua + s("trig", { + i(1), c(2, { + sn(nil, { + t"cannot access the argnode :(", + f(function(args) + return args[1] + end, {???}) -- can't refer to i(1), since it isn't a sibling of `f`. + }), + t"sample_text" + }) + }) +< + +And the solution: first give the node we want to refer to a key, and then pass +the same to the `functionNode`. + +>lua + s("trig", { + i(1, "", {key = "i1-key"}), c(2, { + sn(nil, { i(1), + t"can access the argnode :)", + f(function(args) + return args[1] + end, k("i1-key") ) + }), + t"sample_text" + }) + }) +< + + +============================================================================== +14. Absolute Indexer *luasnip-absolute-indexer* + +`absolute_indexer` allows accessing nodes by their unique jump-index path from +the snippet-root. This makes it almost as powerful as |luasnip-key-indexer|, +but again removes the possibility of referring to non-jumpable nodes and makes +it all a bit more error-prone since the jump-index paths are hard to follow, +and (unfortunately) have to be a bit verbose (see the long example of +`absolute_indexer`-positions below). Consider just using |luasnip-key-indexer| +instead. + +(The solution-snippet from |luasnip-key-indexer|, but using `ai` instead.) + +>lua + s("trig", { + i(1), c(2, { + sn(nil, { i(1), + t"can access the argnode :)", + f(function(args) + return args[1] + end, ai(1) ) + }), + t"sample_text" + }) + }) +< + +There are some quirks in addressing nodes: + +>lua + s("trig", { + i(2), -- ai[2]: indices based on jump-index, not position. + sn(1, { -- ai[1] + i(1), -- ai[1][1] + t"lel", -- not addressable. + i(2) -- ai[1][2] + }), + c(3, { -- ai[3] + i(nil), -- ai[3][1] + t"lel", -- ai[3][2]: choices are always addressable. + }), + d(4, function() -- ai[4] + return sn(nil, { -- ai[4][0] + i(1), -- ai[4][0][1] + }) + end, {}), + r(5, "restore_key", -- ai[5] + i(1) -- ai[5][0][1]: restoreNodes always store snippetNodes. + ), + r(6, "restore_key_2", -- ai[6] + sn(nil, { -- ai[6][0] + i(1) -- ai[6][0][1] + }) + ) + }) +< + +Note specifically that the index of a dynamicNode differs from that of the +generated snippetNode, and that restoreNodes (internally) always store a +snippetNode, so even if the restoreNode only contains one node, that node has +to be accessed as `ai[restoreNodeIndx][0][1]`. + +`absolute_indexer`s’ can be constructed in different ways: + +- `ai[1][2][3]` +- `ai(1, 2, 3)` +- `ai{1, 2, 3}` + +are all the same node. + + +============================================================================== +15. MultiSnippet *luasnip-multisnippet* + +There are situations where it might be comfortable to access a snippet in +different ways. For example, one might want to enable auto-triggering in +regions where the snippets usage is common, while leaving it manual-only in +others. This is where `ms` should be used: A single snippet can be associated +with multiple `context`s (the `context`-table determines the conditions under +which a snippet may be triggered). This has the advantage (compared with just +registering copies) that all `context`s are backed by a single snippet, and not +multiple, and it’s (at least should be :D) more comfortable to use. + +`ms(contexts, nodes, opts) -> addable`: + +- `contexts`: table containing list of `contexts`, and some keywords. + `context` are described in |luasnip-snippets|, here they may also be tables + or strings. + So far, there is only one valid keyword: + - `common`: Accepts yet another context. + The options in `common` are applied to (but don’t override) the other + contexts specified in `contexts`. +- `nodes`: List of nodes, exactly like in |luasnip-snippets|. +- `opts`: Table, options for this function: + - `common_opts`: The snippet-options (see also |luasnip-snippets|) applied + to the snippet generated from `nodes`. + +The returned object is an `addable`, something which can be passed to +`add_snippets`, or returned from the lua-loader. + +**Examples**: + +>lua + ls.add_snippets("all", { + ms({"a", "b"}, {t"a or b"}) + }) +< + +>lua + ls.add_snippets("all", { + ms({ + common = {snippetType = "autosnippet"}, + "a", + "b" + }, { + t"a or b (but autotriggered!!)" + }) + }) +< + +>lua + ls.add_snippets("all", { + ms({ + common = {snippetType = "autosnippet"}, + {trig = "a", snippetType = "snippet"}, + "b", + {trig = "c", condition = function(line_to_cursor) + return line_to_cursor == "" + end} + }, { + t"a or b (but autotriggered!!)" + }) + }) +< + + +============================================================================== +16. Extras *luasnip-extras* + + +LAMBDA *luasnip-extras-lambda* + +A shortcut for `functionNode`s that only do very basic string manipulation. + +`l(lambda, argnodes)`: + +- `lambda`: An object created by applying string-operations to `l._n`, objects + representing the `n`th argnode. + For example: + - `l._1:gsub("a", "e")` replaces all occurences of "a" in the text of the + first argnode with "e", or + - `l._1 .. l._2` concatenates text of the first and second argnode. + If an argnode contains multiple lines of text, they are concatenated with + `"\n"` prior to any operation. +- `argnodes`, a |luasnip-node-reference|, just like in function- and + dynamicNode. + +There are many examples for `lambda` in `Examples/snippets.lua` + + +MATCH *luasnip-extras-match* + +`match` can insert text based on a predicate (again, a shorthand for +`functionNode`). + +`match(argnodes, condition, then, else)`: + +- `argnode`: A single |luasnip-node-reference|. May not be nil, or + a table. +- `condition` may be either of + - `string`: interpreted as a lua pattern. Matched on the `\n`-joined (in case + it’s multiline) text of the first argnode (`args[1]:match(condition)`). + - `function`: `fn(args, snip) -> bool`: takes the same parameters as the + `functionNode`-function, any value other than nil or false is interpreted + as a match. + - `lambda`: `l._n` is the `\n`-joined text of the nth argnode. + Useful if string manipulations have to be performed before the string is matched. + Should end with `match`, but any other truthy result will be interpreted + as matching. +- `then` is inserted if the condition matches, +- `else` if it does not. + +Both `then` and `else` can be either text, lambda or function (with the same +parameters as specified above). `then`’s default-value depends on the +`condition`: + +- `pattern`: Simply the return value from the `match`, e.g. the entire match, + or, if there were capture groups, the first capture group. +- `function`: the return value of the function if it is either a string, or a + table (if there is no `then`, the function cannot return a table containing + something other than strings). +- `lambda`: Simply the first value returned by the lambda. + +Examples: + +- `match(n, "^ABC$", "A")`. +- `match(n, lambda._1:match(lambda._1:reverse()), "PALINDROME")` + >lua + s("trig", { + i(1), t":", + i(2), t"::", + m({1, 2}, l._1:match("^"..l._2.."$"), l._1:gsub("a", "e")) + }) + < +- >lua + s("extras1", { + i(1), t { "", "" }, m(1, "^ABC$", "A") + }) + < + Inserts "A" if the node with jump-index `n` matches "ABC" exactly, nothing + otherwise. +- >lua + s("extras2", { + i(1, "INPUT"), t { "", "" }, m(1, l._1:match(l._1:reverse()), "PALINDROME") + }) + < + Inserts `"PALINDROME"` if i(1) contains a palindrome. +- >lua + s("extras3", { + i(1), t { "", "" }, i(2), t { "", "" }, + m({ 1, 2 }, l._1:match("^" .. l._2 .. "$"), l._1:gsub("a", "e")) + }) + < + This inserts the text of the node with jump-index 1, with all occurences of `a` + replaced with `e`, if the second insertNode matches the first exactly. + + +REPEAT *luasnip-extras-repeat* + +Inserts the text of the passed node. + +`rep(node_reference)` - `node_reference`, a single |luasnip-node-reference|. + +>lua + s("extras4", { i(1), t { "", "" }, extras.rep(1) }) +< + + +PARTIAL *luasnip-extras-partial* + +Evaluates a function on expand and inserts its value. + +`partial(fn, params...)` - `fn`: any function - `params`: varargs, any, will be +passed to `fn`. + +For example `partial(os.date, "%Y")` inserts the current year on expansion. + +>lua + s("extras5", { extras.partial(os.date, "%Y") }) +< + + +NONEMPTY *luasnip-extras-nonempty* + +Inserts text if the referenced node doesn’t contain any text. + +`nonempty(node_reference, not_empty, empty)`: + +- `node_reference`, a single |luasnip-node-reference|. +- `not_empty`, `string`: inserted if the node is not empty. +- `empty`, `string`: inserted if the node is empty. + +>lua + s("extras6", { i(1, ""), t { "", "" }, extras.nonempty(1, "not empty!", "empty!") }) +< + + +DYNAMIC LAMBDA *luasnip-extras-dynamic-lambda* + +Pretty much the same as lambda, but it inserts the resulting text as an +insertNode, and, as such, it can be quickly overridden. + +`dynamic_lambda(jump_indx, lambda, node_references)` - `jump_indx`, as usual, +the jump-indx. + +The remaining arguments carry over from lambda. + +>lua + s("extras7", { i(1), t { "", "" }, extras.dynamic_lambda(2, l._1 .. l._1, 1) }) +< + + +FMT *luasnip-extras-fmt* + +Authoring snippets can be quite clunky, especially since every second node is +probably a `textNode`, inserting a small number of characters between two more +complicated nodes. + +`fmt` can be used to define snippets in a much more readable way. This is +achieved by borrowing (as the name implies) from `format`-functionality (our +syntax is very similar to python’s +). + +`fmt` accepts a string and a table of nodes. Each occurrence of a delimiter +pair in the string is replaced by one node from the table, while text outside +the delimiters is turned into textNodes. + +Simple example: + +>lua + ls.add_snippets("all", { + -- important! fmt does not return a snippet, it returns a table of nodes. + s("example1", fmt("just an {iNode1}", { + iNode1 = i(1, "example") + })), + s("example2", fmt([[ + if {} then + {} + end + ]], { + -- i(1) is at nodes[1], i(2) at nodes[2]. + i(1, "not now"), i(2, "when") + })), + s("example3", fmt([[ + if <> then + <> + end + ]], { + -- i(1) is at nodes[1], i(2) at nodes[2]. + i(1, "not now"), i(2, "when") + }, { + delimiters = "<>" + })), + s("example4", fmt([[ + repeat {a} with the same key {a} + ]], { + a = i(1, "this will be repeat") + }, { + repeat_duplicates = true + })) + }) +< + +One important detail here is that the position of the delimiters does not, in +any way, correspond to the jump-index of the nodes! + +`fmt(format:string, nodes:table of nodes, opts:table|nil) -> table of nodes` + +- `format`: a string. Occurences of `{}` ( `{}` are customizable; more + on that later) are replaced with `content[]` (which should be a + node), while surrounding text becomes `textNode`s. + To escape a delimiter, repeat it (`"{{"`). + If no key is given (`{}`) are numbered automatically: + `"{} ? {} : {}"` becomes `"{1} ? {2} : {3}"`, while + `"{} ? {3} : {}"` becomes `"{1} ? {3} : {4}"` (the count restarts at each + numbered placeholder). + If a key appears more than once in `format`, the node in + `content[]` is inserted for the first, and copies of it for + subsequent occurences. +- `nodes`: just a table of nodes. +- `opts`: optional arguments: + - `delimiters`: string, two characters. Change `{}` to some other pair, e.g. + `"<>"`. + - `strict`: Warn about unused nodes (default true). + - `trim_empty`: remove empty (`"%s*"`) first and last line in `format`. Useful + when passing multiline strings via `[[]]` (default true). + - `dedent`: remove indent common to all lines in `format`. Again, makes + passing multiline-strings a bit nicer (default true). + - `repeat_duplicates`: repeat nodes when a key is reused instead of copying + the node if it has a jump-index, refer to |luasnip-basics-jump-index| to + know which nodes have a jump-index (default false). + +There is also `require("luasnip.extras.fmt").fmta`. This only differs from +`fmt` by using angle brackets (`<>`) as the default delimiter. + + +CONDITIONS *luasnip-extras-conditions* + +This module (`luasnip.extras.condition`) contains functions that can be passed +to a snippet’s `condition` or `show_condition`. These are grouped accordingly +into `luasnip.extras.conditions.expand` and `luasnip.extras.conditions.show`: + +**expand**: + +- `line_begin`: only expand if the cursor is at the beginning of the line. + +**show**: + +- `line_end`: only expand at the end of the line. +- `has_selected_text`: only expand if there’s selected text stored after pressing + `store_selection_keys`. + +Additionally, `expand` contains all conditions provided by `show`. + + +CONDITION OBJECTS ~ + +`luasnip.extras.conditions` also contains condition objects. These can, just +like functions, be passed to `condition` or `show_condition`, but can also be +combined with each other into logical expressions: + +- `-c1 -> not c1` +- `c1 * c2 -> c1 and c2` +- `c1 + c2 -> c1 or c2` +- `c1 - c2 -> c1 and not c2`: This is similar to set differences: + `A \ B = {a in A | a not in B}`. This makes `-(a + b) = -a - b` an identity + representing de Morgan’s law: `not (a or b) = not a and not b`. However, + since boolean algebra lacks an additive inverse, `a + (-b) = a - b` does not + hold. Thus, this is NOT the same as `c1 + (-c2)`. +- `c1 ^ c2 -> c1 xor(!=) c2` +- `c1 % c2 -> c1 xnor(==) c2`: This decision may seem weird, considering how + there is an overload for the `==`-operator. Unfortunately, it’s not possible + to use this for our purposes (some info + here ), + so we decided to make use of a more obscure symbol (which will hopefully avoid + false assumptions about its meaning). + +This makes logical combinations of conditions very readable. Compare + +>lua + condition = conditions.expand.line_end + conditions.expand.line_begin +< + +with the more verbose + +>lua + condition = function(...) return conditions.expand.line_end(...) or conditions.expand.line_begin(...) end +< + +The conditions provided in `show` and `expand` are already condition objects. +To create new ones, use +`require("luasnip.extras.conditions").make_condition(condition_fn)` + + +ON THE FLY-SNIPPETS *luasnip-extras-on-the-fly-snippets* + +Sometimes it’s desirable to create snippets tailored for exactly the current +situation. For example inserting repetitive, but just slightly different +invocations of some function, or supplying data in some schema. + +On-the-fly snippets enable exactly this use case: they can be quickly created +and expanded with as little disruption as possible. + +Since they should mainly fast to write and don’t necessarily need all bells +and whistles, they don’t make use of lsp/textmate-syntax, but a more +simplistic one: + +- `$anytext` denotes a placeholder (`insertNode`) with text "anytext". The text + also serves as a unique key: if there are multiple placeholders with the same + key, only the first will be editable, the others will just mirror it. +- … That’s it. `$` can be escaped by preceding it with a second `$`, all other + symbols will be interpreted literally. + +There is currently only one way to expand on-the-fly snippets: +`require('luasnip.extras.otf').on_the_fly("")` will interpret +whatever text is in the register `` as a snippet, and expand it +immediately. The idea behind this mechanism is that it enables a very immediate +way of supplying and retrieving (expanding) the snippet: write the snippet-body +into the buffer, cut/yank it into some register, and call +`on_the_fly("")` to expand the snippet. + +Here’s one set of example keybindings: + +>vim + " in the first call: passing the register is optional since `on_the_fly` + " defaults to the unnamed register, which will always contain the previously cut + " text. + vnoremap "eclua require('luasnip.extras.otf').on_the_fly("e") + inoremap lua require('luasnip.extras.otf').on_the_fly("e") +< + +Obviously, `` is arbritary and can be changed to any other key combo. +Another interesting application is allowing multiple on-the-fly snippets at the +same time by retrieving snippets from multiple registers: + +>vim + " For register a + vnoremap a "aclua require('luasnip.extras.otf').on_the_fly() + inoremap a lua require('luasnip.extras.otf').on_the_fly("a") + + + " For register b + vnoremap a "bc:lua require('luasnip.extras.otf').on_the_fly() + inoremap b lua require('luasnip.extras.otf').on_the_fly("b") +< + + +SELECT_CHOICE *luasnip-extras-select_choice* + +It’s possible to leverage `vim.ui.select` for selecting a choice directly, +without cycling through the available choices. All that is needed for this is +calling `require("luasnip.extras.select_choice")`, most likely via some +keybind, e.g. + +>vim + inoremap lua require("luasnip.extras.select_choice")() +< + +while inside a choiceNode. The `opts.kind` hint for `vim.ui.select` will be set +to `luasnip`. + + +FILETYPE-FUNCTIONS *luasnip-extras-filetype-functions* + +Contains some utility functions that can be passed to the `ft_func` or +`load_ft_func`-settings. + +- `from_filetype`: the default for `ft_func`. Simply returns the filetype(s) of + the buffer. +- `from_cursor_pos`: uses treesitter to determine the filetype at the cursor. + With that, it’s possible to expand snippets in injected regions, as long as + the treesitter parser supports them. If this is used in conjuction with + `lazy_load`, extra care must be taken that all the filetypes that can be + expanded in a given buffer are also returned by `load_ft_func` (otherwise their + snippets may not be loaded). This can easily be achieved with `extend_load_ft`. +- `extend_load_ft`: `fn(extend_ft:map) -> fn` A simple solution to the problem + described above is loading more filetypes than just that of the target buffer + when `lazy_load`ing. This can be done ergonomically via `extend_load_ft`: + calling it with a table where the keys are filetypes, and the values are the + filetypes that should be loaded additionaly returns a function that can be + passed to `load_ft_func` and takes care of extending the filetypes properly. + >lua + ls.setup({ + load_ft_func = + -- Also load both lua and json when a markdown-file is opened, + -- javascript for html. + -- Other filetypes just load themselves. + require("luasnip.extras.filetype_functions").extend_load_ft({ + markdown = {"lua", "json"}, + html = {"javascript"} + }) + }) + < + + +POSTFIX-SNIPPET *luasnip-extras-postfix-snippet* + +Postfix snippets, famously used in rust analyzer + and various IDEs, are a type of snippet +which alters text before the snippet’s trigger. While these can be +implemented using regTrig snippets, this helper makes the process easier in +most cases. + +The simplest example, which surrounds the text preceeding the `.br` with +brackets `[]`, looks like: + +>lua + postfix(".br", { + f(function(_, parent) + return "[" .. parent.snippet.env.POSTFIX_MATCH .. "]" + end, {}), + }) +< + +and is triggered with `xxx.br` and expands to `[xxx]`. + +Note the `parent.snippet.env.POSTFIX_MATCH` in the function node. This is +additional field generated by the postfix snippet. This field is generated by +extracting the text matched (using a configurable matching string, see below) +from before the trigger. In the case above, the field would equal `"xxx"`. This +is also usable within dynamic nodes. + +This field can also be used within lambdas and dynamic nodes. + +>lua + postfix(".br", { + l("[" .. l.POSTFIX_MATCH .. "]"), + }) +< + +>lua + postfix(".brd", { + d(1, function (_, parent) + return sn(nil, {t("[" .. parent.env.POSTFIX_MATCH .. "]")}) + end) + }) +< + +The arguments to `postfix` are identical to the arguments to `s` but with a few +extra options. + +The first argument can be either a string or a table. If it is a string, that +string will act as the trigger, and if it is a table it has the same valid keys +as the table in the same position for `s` except: + +- `wordTrig`: This key will be ignored if passed in, as it must always be + false for postfix snippets. +- `match_pattern`: The pattern that the line before the trigger is matched + against. The default match pattern is `"[%w%.%_%-]+$"`. Note the `$`. This + matches since only the line _up until_ the beginning of the trigger is + matched against the pattern, which makes the character immediately + preceeding the trigger match as the end of the string. + +Some other match strings, including the default, are available from the postfix +module. `require("luasnip.extras.postfix).matches`: + +- `default`: `[%w%.%_%-%"%']+$` +- `line`: `^.+$` + +The second argument is identical to the second argument for `s`, that is, a +table of nodes. + +The optional third argument is the same as the third (`opts`) argument to the +`s` function, but with one difference: + +The postfix snippet works using a callback on the pre_expand event of the +snippet. If you pass a callback on the pre_expand event (structure example +below) it will get run after the builtin callback. + +>lua + { + callbacks = { + [-1] = { + [events.pre_expand] = function(snippet, event_args) + -- function body to match before the dot + -- goes here + end + } + } + } +< + + +TREESITTER-POSTFIX-SNIPPET *luasnip-extras-treesitter-postfix-snippet* + +Instead of triggering a postfix-snippet when some pattern matches in front of +the trigger, it might be useful to match if some specific treesitter-nodes +surround/are in front of the trigger. While this functionality can also be +implemented by a cusutom `resolveExpandParams`, this helper simplifies the +common cases. + +This matching of treesitter-nodes can be done either + +- by providing a query and the name of the capture that should be in front of + the trigger (in most cases, the complete match, but requiring specific nodes + before/after the matched node may be useful as well), or +- by providing a function that manually walks the node-tree, and returns the + node in front of the trigger on success (for increased flexibility). + +A simple example, which surrounds the previous node’s text preceeding the +`.mv` with `std::move()` in cpp files, looks like: + +>lua + local treesitter_postfix = require("luasnip.extras.treesitter_postfix").treesitter_postfix + + treesitter_postfix({ + trig = ".mv", + matchTSNode = { + query = [[ + [ + (call_expression) + (identifier) + (template_function) + (subscript_expression) + (field_expression) + (user_defined_literal) + ] @prefix + ]] + query_lang = "cpp" + }, + },{ + f(function(_, parent) + local node_content = table.concat(parent.snippet.env.LS_TSMATCH, '\n') + local replaced_content = ("std::move(%s)"):format(node_content) + return vim.split(ret_str, "\n", { trimempty = false }) + end) + }) +< + +`LS_TSMATCH` is the treesitter-postfix equivalent to `POSTFIX_MATCH`, and is +populated with the match (in this case the text of a treesitter-node) in front +of the trigger. + +The arguments to `treesitter_postfix` are identical to the arguments to `s` but +with a few extra options. + +The first argument has to be a table, which defines at least `trig` and +`matchTSNode`. All keys from the regular `s` may be set here (except for +`wordTrig`, which will be ignored), and additionally the following: + +- `reparseBuffer`, `string?`: Sometimes the trigger may interfere with + treesitter recognizing queries correctly. With this option, the trigger may + either be removed from the live-buffer (`"live"`), from a copy of the buffer + (`"copy"`), or not at all (`nil`). +- `matchTSNode`: How to determine whether there is a matching node in front of + the cursor. There are two options: + - `fun(parser: LuaSnip.extra.TSParser, pos: { [1]: number, [2]: number }): LuaSnip.extra.NamedTSMatch?, TSNode? Manually determine whether there is a matching node that ends just before`pos`(the beginning of the trigger). Return`nil,nil`if there is no match, otherwise first return a table mapping names to nodes (the text, position and type of these will be provided via`snip.env`), and second the node that is the matched node. + - `LuaSnip.extra.MatchTSNodeOpts`, which represents a query and provides all + captures of the matched pattern in `NamedTSMatch`. It contains the following + options: + - `query`, `string`: The query, in textual form. + - `query_name`, `string`: The name of the runtime-query to be used (passed + to `query.get()`), defaults to `"luasnip"` (so one could create a + file which only contains queries used by luasnip, like + `$CONFDIR/queries//luasnip.scm`, which might make sense to define + general concepts independent of a single snippet). + `query` and `query_name` are mutually exclusive, only one of both shall be + defined. + - `query_lang`, `string`: The language of the query. This is the only + required parameter to this function, since there’s no sufficiently + straightforward way to determine the language of the query for us. + Consider using `extend_override` to define a `ts_postfix`-function that + automatically fills in the language for the filetype of the snippet-file. + - `match_captures`, `string|string[]`: The capture(s) to use for determining + the actual prefix (so the node that should be immediately in front of the + trigger). This defaults to just `"prefix"`. + - `select`, `string?|fun(): LuaSnip.extra.MatchSelector`: Since there may be + multiple matching captures in front of the cursor, there has to be some + way to select the node that will actually be used. + If this is a string, it has to be one of "any", "shortest", or "longest", + which mean that any, the shortest, or the longest match is used. + If it is a function, it must return a table with two fields, `record` and + `retrieve`. `record` is called with a TSMatch and a potential node for the + ts-match, and may return `true` to abort the selection-procedure. + `retrieve` must return either a TSMatch-TSNode-tuple (which is used as the + match) or `nil`, to signify that there is no match. + `lua/luasnip/extras/_treesitter.lua` contains the table + `builtin_tsnode_selectors`, which contains the implementations for + any/shortest/longest, which can be used as examples for more complicated + custom-selectors. + +The text of the matched node can be accessed as `snip.env.LS_TSMATCH`. The text +of the nodes returned as `NamedTSMatch` can be accessed as +`snip.env.LS_TSCAPTURE_`, and their range and type as +`snip.env.LS_TSDATA..range/type` (where range is a tuple +of row-col-tuples, both 0-indexed). + +For a query like + +>scm + (function_declaration + name: (identifier) @fname + parameters: (parameters) @params + body: (block) @body + ) @prefix +< + +matched against + +>lua + function add(a, b) + return a + b + end +< + +`snip.env` would contain: + +- `LS_TSMATCH`: `{ "function add(a, b)", "\treturn a + b", "end" }` +- `LS_TSDATA`: + >lua + { + body = { + range = { { 1, 1 }, { 1, 13 } }, + type = "block" + }, + fname = { + range = { { 0, 9 }, { 0, 12 } }, + type = "identifier" + }, + params = { + range = { { 0, 12 }, { 0, 18 } }, + type = "parameters" + }, + prefix = { + range = { { 0, 0 }, { 2, 3 } }, + type = "function_declaration" + } + } + < +- `LS_TSCAPTURE_FNAME`: `{ "add" }` +- `LS_TSCAPTURE_PARAMS`: `{ "(a, b)" }` +- `LS_TSCAPTURE_BODY`: `{ "return a + b" }` +- `LS_TSCAPTURE_PREFIX`: `{ "function add(a, b)", "\treturn a + b", "end" }` + +(note that all variables containing text of nodes are string-arrays, one entry +for each line) + +There is one important caveat when accessing `LS_TSDATA` in +function/dynamicNodes: It won’t contain the values as specified here while +generating docstrings (in fact, it won’t even be a table). Since docstrings +have to be generated without any runtime-information, we just have to provide +dummy-data in `env`, which will be some kind of string related to the name of +the env-variable. Since the structure of `LS_TSDATA` obviously does not fit +that model, we can’t really handle it in a nice way (at least yet). So, for +now, best include a check like `local static_evaluation = type(env.LS_TSDATA) +== "string"`, and behave accordingly if `static_evaluation` is true (for +example, return some value tailored for displaying it in a docstring). + +One more example, which actually uses a few captures: + +>lua + ts_post({ + matchTSNode = { + query = [[ + (function_declaration + name: (identifier) @fname + parameters: (parameters) @params + body: (block) @body + ) @prefix + ]], + query_lang = "lua", + }, + trig = ".var" + }, fmt([[ + local {} = function{} + {} + end + ]], { + l(l.LS_TSCAPTURE_FNAME), + l(l.LS_TSCAPTURE_PARAMS), + l(l.LS_TSCAPTURE_BODY), + })) +< + +The module `luasnip.extras.treesitter_postfix` contains a few functions that +may be useful for creating more efficient ts-postfix-snippets. Nested in +`builtin.tsnode_matcher` are: + +- `fun find_topmost_types(types: string[]): MatchTSNodeFunc`: Generates + a `LuaSnip.extra.MatchTSNodeFunc` which returns the last parent whose type + is in `types`. +- `fun find_first_types(types: string[]): MatchTSNodeFunc`: Similar to + `find_topmost_types`, only this one matches the first parent whose type is in + types. +- `find_nth_parent(n: number): MatchTSNodeFunc`: Simply matches the `n`-th + parent of the innermost node infront of the trigger. + +With `find_topmost_types`, the first example can be implemented more +efficiently (without needing a whole query): + +>lua + local postfix_builtin = require("luasnip.extras.treesitter_postfix").builtin + + ls.add_snippets("all", { + ts_post({ + matchTSNode = postfix_builtin.tsnode_matcher.find_topmost_types({ + "call_expression", + "identifier", + "template_function", + "subscript_expression", + "field_expression", + "user_defined_literal" + }), + trig = ".mv" + }, { + l(l_str.format("std::move(%s)", l.LS_TSMATCH)) + }) + }, {key = "asdf"}) +< + + +SNIPPET LIST *luasnip-extras-snippet-list* + +>lua + local sl = require("luasnip.extras.snippet_list") +< + +Makes an `open` function available to use to open currently available snippets +in a different buffer/window/tab. + +`sl.open(opts:table|nil)` + +- `opts`: optional arguments: + - `snip_info`: `snip_info(snippet) -> table representation of snippet` + - `printer`: `printer(snippets:table) -> any` + - `display`: `display(snippets:any)` + +Benefits include: syntax highlighting, searching, and customizability. + +Simple Example: + +>lua + sl.open() +< + +Customization Examples: + +>lua + -- making our own snip_info + local function snip_info(snippet) + return { name = snippet.name } + end + + -- using it + sl.open({snip_info = snip_info}) +< + +>lua + -- making our own printer + local function printer(snippets) + local res = "" + + for ft, snips in pairs(snippets) do + res = res .. ft .. "\n" + for _, snip in pairs(snips) do + res = res .. " " .. "Name: " .. snip.name .. "\n" + res = res .. " " .. "Desc: " .. snip.description[1] .. "\n" + res = res .. " " .. "Trigger: " .. snip.trigger .. "\n" + res = res .. " ----" .. "\n" + end + end + + return res + end + + + -- using it + sl.open({printer = printer}) +< + +>lua + -- making our own display + local function display(printer_result) + -- right vertical split + vim.cmd("botright vnew") + + -- get buf and win handle + local buf = vim.api.nvim_get_current_buf() + local win = vim.api.nvim_get_current_win() + + -- setting window and buffer options + vim.api.nvim_win_set_option(win, "foldmethod", "manual") + vim.api.nvim_buf_set_option(buf, "filetype", "javascript") + + vim.api.nvim_buf_set_option(buf, "buftype", "nofile") + vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe") + vim.api.nvim_buf_set_option(buf, "buflisted", false) + + vim.api.nvim_buf_set_name(buf, "Custom Display buf " .. buf) + + -- dump snippets + local replacement = vim.split(printer_result) + vim.api.nvim_buf_set_lines(buf, 0, 0, false, replacement) + end + + -- using it + sl.open({display = display}) +< + +There is a **caveat** with implementing your own printer and/or display +function. The **default** behavior for the printer function is to return a +string representation of the snippets. The display function uses the results +from the printer function, therefore by **default** the display function is +expecting that result to be a string. + +However, this doesn’t have to be the case. For example, you can implement +your own printer function that returns a table representation of the snippets +**but** you would have to then implement your own display function or some +other function in order to return the result as a string. + +An `options` table, which has some core functionality that can be used to +customize 'common' settings, is provided. + +- `sl.options`: options table: + - `display`: `display(opts:table|nil) -> function(printer_result:string)` + +You can see from the example above that making a custom display is a fairly +involved process. What if you just wanted to change a buffer option like the +name or just the filetype? This is where `sl.options.display` comes in. It +allows you to customize buffer and window options while keeping the default +behavior. + +`sl.options.display(opts:table|nil) -> function(printer_result:string)` + +- `opts`: optional arguments: + - `win_opts`: `table which has a {window_option = value} form` + - `buf_opts`: `table which has a {buffer_option = value} form` + - `get_name`: `get_name(buf) -> string` + +Let’s recreate the custom display example above: + +>lua + -- keeping the default display behavior but modifying window/buffer + local modified_default_display = sl.options.display({ + buf_opts = {filetype = "javascript"}, + win_opts = {foldmethod = "manual"}, + get_name = function(buf) return "Custom Display buf " .. buf end + }) + + -- using it + sl.open({display = modified_default_display}) +< + + +SNIPPET LOCATION *luasnip-extras-snippet-location* + +This module can consume a snippets |luasnip-source|, more specifically, jump to +the location referred by it. This is primarily implemented for snippet which +got their source from one of the loaders, but might also work for snippets +where the source was set manually. + +`require("luasnip.extras.snip_location")`: + +- `snip_location.jump_to_snippet(snip, opts)` + Jump to the definition of `snip`. + - `snip`: a snippet with attached source-data. + - `opts`: `nil|table`, optional arguments, valid keys are: + - `hl_duration_ms`: `number`, duration for which the definition should be highlighted, + in milliseconds. 0 disables the highlight. + - `edit_fn`: `function(file)`, this function will be called with the file + the snippet is located in, and is responsible for jumping to it. + We assume that after it has returned, the current buffer contains `file`. +- `snip_location.jump_to_active_snippet(opts)` + Jump to definition of active snippet. + - `opts`: `nil|table`, accepts the same keys as the `opts`-parameter of + `jump_to_snippet`. + + +============================================================================== +17. Extend Decorator *luasnip-extend-decorator* + +Most of luasnip’s functions have some arguments to control their behaviour. +Examples include `s`, where `wordTrig`, `regTrig`, … can be set in the first +argument to the function, or `fmt`, where the delimiter can be set in the third +argument. This is all good and well, but if these functions are often used with +non-default settings, it can become cumbersome to always explicitly set them. + +This is where the `extend_decorator` comes in: it can be used to create +decorated functions which always extend the arguments passed directly with +other previously defined ones. + +An example: + +>lua + local fmt = require("luasnip.extras.fmt").fmt + + fmt("{}", {i(1)}) -- -> list of nodes, containing just the i(1). + + -- when authoring snippets for some filetype where `{` and `}` are common, they + -- would always have to be escaped in the format-string. It might be preferable + -- to use other delimiters, like `<` and `>`. + + fmt("<>", {i(1)}, {delimiters = "<>"}) -- -> same as above. + + -- but it's quite annoying to always pass the `{delimiters = "<>"}`. + + -- with extend_decorator: + local fmt_angle = ls.extend_decorator.apply(fmt, {delimiters = "<>"}) + fmt_angle("<>", {i(1)}) -- -> same as above. + + -- the same also works with other functions provided by luasnip, for example all + -- node/snippet-constructors and `parse_snippet`. +< + +`extend_decorator.apply(fn, ...)` requires that `fn` is previously registered +via `extend_decorator.register`. (This is not limited to LuaSnip’s functions; +although, for usage outside of LuaSnip, best copy the source file: +`/lua/luasnip/util/extend_decorator.lua`). + +`register(fn, ...)`: + +- `fn`: the function. +- `...`: any number of tables. Each specifies how to extend an argument of `fn`. + The tables accept: + - arg_indx, `number` (required): the position of the parameter to override. + - extend, `fn(arg, extend_value) -> effective_arg` (optional): this function + is used to extend the args passed to the decorated function. + It defaults to a function which just extends the the arg-table with the + extend table (accepts `nil`). + This extend behaviour is adaptable to accomodate `s`, where the first + argument may be string or table. + +`apply(fn, ...) -> decorated_fn`: + +- `fn`: the function to decorate. +- `...`: The values to extend with. These should match the descriptions passed + in `register` (the argument first passed to `register` will be extended with + the first value passed here). + +One more example for registering a new function: + +>lua + local function somefn(arg1, arg2, opts1, opts2) + -- not important + end + + -- note the reversed arg_indx!! + extend_decorator.register(somefn, {arg_indx=4}, {arg_indx=3}) + local extended = extend_decorator.apply(somefn, + {key = "opts2 is extended with this"}, + {key = "and opts1 with this"}) + extended(...) +< + + +============================================================================== +18. LSP-Snippets *luasnip-lsp-snippets* + +LuaSnip is capable of parsing LSP-style snippets using +`ls.parser.parse_snippet(context, snippet_string, opts)`: + +>lua + ls.parser.parse_snippet({trig = "lsp"}, "$1 is ${2|hard,easy,challenging|}") +< + +`context` can be: - `string|table`: treated like the first argument to `ls.s`, +`parse_snippet` returns a snippet. - `number`: `parse_snippet` returns a +snippetNode, with the position `context`. - `nil`: `parse_snippet` returns a +flat table of nodes. This can be used like `fmt`. + +Nested placeholders(`"${1:this is ${2:nested}}"`) will be turned into +choiceNodes with: - the given snippet(`"this is ${1:nested}"`) and - an empty +insertNode + +This behaviour can be modified by changing `parser_nested_assembler` in +`ls.setup()`. + +LuaSnip will also modify some snippets that it is incapable of representing +accurately: + +- if the `$0` is a placeholder with something other than just text inside +- if the `$0` is a choice +- if the `$0` is not an immediate child of the snippet (it could be inside a + placeholder: `"${1: $0 }"`) + +To remedy those incompatibilities, the invalid `$0` will be replaced with a +tabstop/placeholder/choice which will be visited just before the new `$0`. This +new `$0` will be inserted at the (textually) earliest valid position behind the +invalid `$0`. + +`opts` can contain the following keys: - `trim_empty`: boolean, remove empty +lines from the snippet. Default true. - `dedent`: boolean, remove common indent +from the snippet’s lines. Default true. + +Both `trim_empty` and `dedent` will be disabled for snippets parsed via +`ls.lsp_expand`: it might prevent correct expansion of snippets sent by lsp. + + +SNIPMATE PARSER *luasnip-lsp-snippets-snipmate-parser* + +It is furthermore possible to parse SnipMate snippets (this includes support +for vimscript-evaluation!!) + +SnipMate snippets need to be parsed with a different function, +`ls.parser.parse_snipmate`: + +>lua + ls.parser.parse_snipmate("year", "The year is `strftime('%Y')`") +< + +`parse_snipmate` accepts the same arguments as `parse_snippet`, only the +snippet body is parsed differently. + + +TRANSFORMATIONS *luasnip-lsp-snippets-transformations* + +To apply Variable/Placeholder-transformations +, +luasnip needs to apply ECMAScript regexes. This is implemented by relying on +`jsregexp` . + +The easiest (but potentially error-prone) way to install it is by calling `make +install_jsregexp` in the repo root. + +This process can be automated by `packer.nvim`: + +>lua + use { "L3MON4D3/LuaSnip", run = "make install_jsregexp" } +< + +If this fails, first open an issue :P, and then try installing the +`jsregexp`-luarock. This is also possible via `packer.nvim`, although actual +usage may require a small workaround, see here + or here +. + +Alternatively, `jsregexp` can be cloned locally, `make`d, and the resulting +`jsregexp.so` placed in some place where nvim can find it (probably +`~/.config/nvim/lua/`). + +If `jsregexp` is not available, transformations are replaced by a simple copy. + + +============================================================================== +19. Variables *luasnip-variables* + +All `TM_something`-variables are supported with two additions: `LS_SELECT_RAW` +and `LS_SELECT_DEDENT`. These were introduced because `TM_SELECTED_TEXT` is +designed to be compatible with VSCode’s behavior, which can be +counterintuitive when the snippet can be expanded at places other than the +point where selection started (or when doing transformations on selected text). +Besides those we also provide `LS_TRIGGER` which contains the trigger of the +snippet, and `LS_CAPTURE_n` (where n is a positive integer) that contains the +n-th capture when using a regex with capture groups as `trig` in the snippet +definition. + +All variables can be used outside of lsp-parsed snippets as their values are +stored in a snippets’ `snip.env`-table: + +>lua + s("selected_text", f(function(args, snip) + local res, env = {}, snip.env + table.insert(res, "Selected Text (current line is " .. env.TM_LINE_NUMBER .. "):") + for _, ele in ipairs(env.LS_SELECT_RAW) do table.insert(res, ele) end + return res + end, {})) +< + +To use any `*SELECT*` variable, the `store_selection_keys` must be set via +`require("luasnip").config.setup({store_selection_keys=""})`. In this +case, hitting `` while in visual mode will populate the `*SELECT*`-vars +for the next snippet and then clear them. + + +ENVIRONMENT NAMESPACES *luasnip-variables-environment-namespaces* + +You can also add your own variables by using the `ls.env_namespace(name, opts)` +where: + +- `name`: `string` the names the namespace, can’t contain the character “_” +- `opts` is a table containing (in every case `EnvVal` is the same as `string|list[string]`: + - `vars`: `(fn(name:string)->EnvVal) | map[string, EnvVal]` + Is a function that receives a string and returns a value for the var with that name + or a table from var name to a value + (in this case, if the value is a function it will be executed lazily once per snippet expansion). + - `init`: `fn(info: table)->map[string, EnvVal]` Returns + a table of variables that will set to the environment of the snippet on expansion, + use this for vars that have to be calculated in that moment or that depend on each other. + The `info` table argument contains `pos` (0-based position of the cursor on expansion), + the `trigger` of the snippet and the `captures` list. + - `eager`: `list[string]` names of variables that will be taken from `vars` and appended eagerly (like those in init) + - `multiline_vars`: `(fn(name:string)->bool)|map[sting, bool]|bool|string[]` Says if certain vars are a table or just a string, + can be a function that get’s the name of the var and returns true if the var is a key, + a list of vars that are tables or a boolean for the full namespace, it’s false by default. Refer to + issue#510 for more information. + +The four fields of `opts` are optional but you need to provide either `init` or +`vars`, and `eager` can’t be without `vars`. Also, you can’t use namespaces +that override default vars. + +A simple example to make it more clear: + +>lua + local function random_lang() + return ({"LUA", "VIML", "VIML9"})[math.floor(math.random()/2 + 1.5)] + end + + ls.env_namespace("MY", {vars={ NAME="LuaSnip", LANG=random_lang }}) + + -- then you can use $MY_NAME and $MY_LANG in your snippets + + ls.env_namespace("SYS", {vars=os.getenv, eager={"HOME"}}) + + -- then you can use $SYS_HOME which was eagerly initialized but also $SYS_USER (or any other system environment var) in your snippets + + lsp.env_namespace("POS", {init=function(info) return {VAL=vim.inspect(info.pos)} end}) + + -- then you can use $POS_VAL in your snippets + + s("custom_env", d(1, function(args, parent) + local env = parent.snippet.env + return sn(nil, t { + "NAME: " .. env.MY_NAME, + "LANG: " .. env.MY_LANG, + "HOME: " .. env.SYS_HOME, + "USER: " .. env.SYS_USER, + "VAL: " .. env.POS_VAL + }) + end, {})) +< + + +LSP-VARIABLES *luasnip-variables-lsp-variables* + +All variables, even ones added via `env_namespace`, can be accessed in LSP +snippets as `$VAR_NAME`. + +The lsp-spec states: + +------------------------------------------------------------------------------ +With `$name` or `${name:default}` you can insert the value of a variable. When +a variable isn’t set, its default or the empty string is inserted. When a +variable is unknown (that is, its name isn’t defined) the name of the +variable is inserted and it is transformed into a placeholder. + +------------------------------------------------------------------------------ +The above necessiates a differentiation between `unknown` and `unset` +variables: + +For LuaSnip, a variable `VARNAME` is `unknown` when `env.VARNAME` returns `nil` +and `unset` if it returns an empty string. + +Consider this when adding env-variables which might be used in LSP snippets. + + +============================================================================== +20. Loaders *luasnip-loaders* + +Luasnip is capable of loading snippets from different formats, including both +the well-established VSCode and SnipMate format, as well as plain Lua files for +snippets written in Lua. + +All loaders (except the vscode-standalone-loader) share a similar interface: +`require("luasnip.loaders.from_{vscode,snipmate,lua}").{lazy_,}load(opts:table|nil)` + +where `opts` can contain the following keys: + +- `paths`: List of paths to load. Can be a table, or a single + comma-separated string. + The paths may begin with `~/` or `./` to indicate that the path is + relative to your `$HOME` or to the directory where your `$MYVIMRC` resides + (useful to add your snippets). + If not set, `runtimepath` is searched for + directories that contain snippets. This procedure differs slightly for + each loader: + - `lua`: the snippet-library has to be in a directory named + `"luasnippets"`. + - `snipmate`: similar to lua, but the directory has to be `"snippets"`. + - `vscode`: any directory in `runtimepath` that contains a + `package.json` contributing snippets. +- `lazy_paths`: behaves essentially like `paths`, with two exceptions: if it is + `nil`, it does not default to `runtimepath`, and the paths listed here do not + need to exist, and will be loaded on creation. + LuaSnip will do its best to determine the path that this should resolve to, + but since the resolving we do is not very sophisticated it may produce + incorrect paths. Definitely check the log if snippets are not loaded as + expected. +- `exclude`: List of languages to exclude, empty by default. +- `include`: List of languages to include, includes everything by default. +- `{override,default}_priority`: These keys are passed straight to the + `add_snippets`-calls (documented in |luasnip-api|) and can therefore change the + priority of snippets loaded from some colletion (or, in combination with + `{in,ex}clude`, only some of its snippets). +- `fs_event_providers`: `table?`, specifies which mechanisms + should be used to watch files for updates/creation. + If `autocmd` is set to `true`, a `BufWritePost`-hook watches files of this + collection, if `libuv` is set, the file-watcher-api exposed by libuv is used + to watch for updates. + Use `libuv` if you want snippets to update from other neovim-instances, and + `autocmd` if the collection resides on a filesystem where the libuv-watchers + may not work correctly. Or, of course, just enable both :D + By default, only `autocmd` is enabled. + +While `load` will immediately load the snippets, `lazy_load` will defer loading +until the snippets are actually needed (whenever a new buffer is created, or +the filetype is changed luasnip actually loads `lazy_load`ed snippets for the +filetypes associated with this buffer. This association can be changed by +customizing `load_ft_func` in `setup`: the option takes a function that, passed +a `bufnr`, returns the filetypes that should be loaded (`fn(bufnr) -> filetypes +(string[])`)). + +All of the loaders support reloading, so simply editing any file contributing +snippets will reload its snippets (according to `fs_event_providers` in the +instance where the file was edited, or in other instances as well). + +As an alternative (or addition) to automatic reloading, luasnip can also +process manual updates to files: Call +`require("luasnip.loaders").reload_file(path)` to reload the file at `path`. +This may be useful when the collection is controlled by some other plugin, or +when enabling the other reload-mechanisms is for some reason undesirable +(performance? minimalism?). + +For easy editing of these files, LuaSnip provides a `vim.ui.select`-based +dialog (|luasnip-loaders-edit_snippets|) where first the filetype, and then the +file can be selected. + + +SNIPPET-SPECIFIC FILETYPES *luasnip-loaders-snippet-specific-filetypes* + +Some loaders (vscode,lua) support giving snippets generated in some file their +own filetype (vscode via `scope`, lua via the underlying `filetype`-option for +snippets). These snippet-specific filetypes are not considered when determining +which files to `lazy_load` for some filetype, this is exclusively determined by +the `language` associated with a file in vscodes’ `package.json`, and the +file/directory-name in lua. + +- This can be resolved relatively easily in vscode, where the `language` + advertised in `package.json` can just be a superset of the `scope`s in the file. +- Another simplistic solution is to set the language to `all` (in lua, it might + make sense to create a directory `luasnippets/all/*.lua` to group these files + together). +- Another approach is to modify `load_ft_func` to load a custom filetype if the + snippets should be activated, and store the snippets in a file for that + filetype. This can be used to group snippets by e.g. framework, and load them + once a file belonging to such a framework is edited. + +**Example**: `react.lua` + +>lua + return { + s({filetype = "css", trig = ...}, ...), + s({filetype = "html", trig = ...}, ...), + s({filetype = "js", trig = ...}, ...), + } +< + +`luasnip_config.lua` + +>lua + load_ft_func = function(bufnr) + if "" then + -- will load `react.lua` for this buffer + return {"react"} + else + return require("luasnip.extras.filetype_functions").from_filetype_load + end + end +< + +See the |luasnip-troubleshooting-adding-snippets-loaders| section if one is +having issues adding snippets via loaders. + + +VS-CODE *luasnip-loaders-vs-code* + +As a reference on the structure of these snippet libraries, see +`friendly-snippets` . + +We support a small extension: snippets can contain LuaSnip-specific options in +the `luasnip`-table: + +>json + "example1": { + "prefix": "options", + "body": [ + "whoa! :O" + ], + "luasnip": { + "priority": 2000, + "autotrigger": true, + "wordTrig": false + } + } +< + +Files with the extension `jsonc` will be parsed as `jsonc`, json with comments +, while +`*.json` are parsed with a regular `json` parser, where comments are +disallowed. (the json-parser is a bit faster, so don’t default to `jsonc` if +it’s not necessary). + +**Example**: + +`~/.config/nvim/my_snippets/package.json`: + +>json + { + "name": "example-snippets", + "contributes": { + "snippets": [ + { + "language": [ + "all" + ], + "path": "./snippets/all.json" + }, + { + "language": [ + "lua" + ], + "path": "./lua.json" + } + ] + } + } +< + +`~/.config/nvim/my_snippets/snippets/all.json`: + +>json + { + "snip1": { + "prefix": "all1", + "body": [ + "expands? jumps? $1 $2 !" + ] + }, + "snip2": { + "prefix": "all2", + "body": [ + "multi $1", + "line $2", + "snippet$0" + ] + } + } +< + +`~/.config/nvim/my_snippets/lua.json`: + +>json + { + "snip1": { + "prefix": "lua", + "body": [ + "lualualua" + ] + } + } +< + +This collection can be loaded with any of + +>lua + -- don't pass any arguments, luasnip will find the collection because it is + -- (probably) in rtp. + require("luasnip.loaders.from_vscode").lazy_load() + -- specify the full path... + require("luasnip.loaders.from_vscode").lazy_load({paths = "~/.config/nvim/my_snippets"}) + -- or relative to the directory of $MYVIMRC + require("luasnip.loaders.from_vscode").load({paths = "./my_snippets"}) +< + + +STANDALONE ~ + +Beside snippet-libraries provided by packages, vscode also supports another +format which can be used for project-local snippets, or user-defined snippets, +`.code-snippets`. + +The layout of these files is almost identical to that of the package-provided +snippets, but there is one additional field supported in the +snippet-definitions, `scope`, with which the filetype of the snippet can be +set. If `scope` is not set, the snippet will be added to the global filetype +(`all`). + +`require("luasnip.loaders.from_vscode").load_standalone(opts)` + +- `opts`: `table`, can contain the following keys: + - `path`: `string`, Path to the `*.code-snippets`-file that should be loaded. + Just like the paths in `load`, this one can begin with a `"~/"` to be + relative to `$HOME`, and a `"./"` to be relative to the + neovim-config-directory. + - `{override,default}_priority`: These keys are passed straight to the + `add_snippets`-calls (documented in |luasnip-api|) and can be used to change + the priority of the loaded snippets. + - `lazy`: `boolean`, if it is set, the file does not have to exist when + `load_standalone` is called, and it will be loaded on creation. + `false` by default. + +**Example**: `a.code-snippets`: + +>jsonc + { + // a comment, since `.code-snippets` may contain jsonc. + "c/cpp-snippet": { + "prefix": [ + "trigger1", + "trigger2" + ], + "body": [ + "this is $1", + "my snippet $2" + ], + "description": "A description of the snippet.", + "scope": "c,cpp" + }, + "python-snippet": { + "prefix": "trig", + "body": [ + "this is $1", + "a different snippet $2" + ], + "description": "Another snippet-description.", + "scope": "python" + }, + "global snippet": { + "prefix": "trigg", + "body": [ + "this is $1", + "the last snippet $2" + ], + "description": "One last snippet-description.", + } + } +< + +This file can be loaded by calling + +>lua + require("luasnip.loaders.from_vscode").load_standalone({path = "a.code-snippets"}) +< + + +SNIPMATE *luasnip-loaders-snipmate* + +Luasnip does not support the full snipmate format: Only `./{ft}.snippets` and +`./{ft}/*.snippets` will be loaded. See honza/vim-snippets + for lots of examples. + +Like VSCode, the SnipMate format is also extended to make use of some of +LuaSnip’s more advanced capabilities: + +>snippets + priority 2000 + autosnippet options + whoa :O +< + +**Example**: + +`~/.config/nvim/snippets/c.snippets`: + +>snippets + # this is a comment + snippet c c-snippet + c! +< + +`~/.config/nvim/snippets/cpp.snippets`: + +>snippets + extends c + + snippet cpp cpp-snippet + cpp! +< + +This can, again, be loaded with any of + +>lua + require("luasnip.loaders.from_snipmate").load() + -- specify the full path... + require("luasnip.loaders.from_snipmate").lazy_load({paths = "~/.config/nvim/snippets"}) + -- or relative to the directory of $MYVIMRC + require("luasnip.loaders.from_snipmate").lazy_load({paths = "./snippets"}) +< + +Stuff to watch out for: + +- Using both `extends ` in `.snippets` and + `ls.filetype_extend("", {""})` leads to duplicate snippets. +- `${VISUAL}` will be replaced by `$TM_SELECTED_TEXT` to make the snippets + compatible with LuaSnip +- We do not implement eval using ` (backtick). This may be implemented in the + future. + + +LUA *luasnip-loaders-lua* + +Instead of adding all snippets via `add_snippets`, it’s possible to store +them in separate files and load all of those. The file-structure here is +exactly the supported snipmate-structure, e.g. `.lua` or `/*.lua` to +add snippets for the filetype ``. + +There are two ways to add snippets: + +- the files may return two lists of snippets, the snippets in the first are all + added as regular snippets, while the snippets in the second will be added as + autosnippets (both are the defaults, if a snippet defines a different + `snippetType`, that will have preference) +- snippets can also be appended to the global (only for these files - they are + not visible anywhere else) tables `ls_file_snippets` and + `ls_file_autosnippets`. This can be combined with a custom `snip_env` to define + and add snippets with one function call: + >lua + ls.setup({ + snip_env = { + s = function(...) + local snip = ls.s(...) + -- we can't just access the global `ls_file_snippets`, since it will be + -- resolved in the environment of the scope in which it was defined. + table.insert(getfenv(2).ls_file_snippets, snip) + end, + parse = function(...) + local snip = ls.parser.parse_snippet(...) + table.insert(getfenv(2).ls_file_snippets, snip) + end, + -- remaining definitions. + ... + }, + ... + }) + < + This is more flexible than the previous approach since the snippets don’t + have to be collected; they just have to be defined using the above `s` and + `parse`. + +As defining all of the snippet constructors (`s`, `c`, `t`, …) in every file +is rather cumbersome, LuaSnip will bring some globals into scope for executing +these files. By default, the names from `luasnip.config.snip_env` + +will be used, but it’s possible to customize them by setting `snip_env` in +`setup`. + +**Example**: + +`~/snippets/all.lua`: + +>lua + return { + s("trig", t("loaded!!")) + } +< + +`~/snippets/c.lua`: + +>lua + return { + s("ctrig", t("also loaded!!")) + }, { + s("autotrig", t("autotriggered, if enabled")) + } +< + +Load via + +>lua + require("luasnip.loaders.from_lua").load({paths = "~/snippets"}) +< + + +RELOADING WHEN EDITING REQUIRE’D FILES ~ + +While the lua-snippet-files will be reloaded on edit, this does not +automatically happen if a file the snippet-file depends on (eg. via `require`) +is changed. Since this still may still be desirable, there are two functions +exposed when a file is loaded by the lua-loader: `ls_tracked_dofile` and +`ls_tracked_dopackage`. They perform like `dofile` and (almost like) `require`, +but both register the loaded file internally as a dependency of the +snippet-file, so it can be reloaded when the loaded file is edited. As stated, +`ls_tracked_dofile` behaves exactly like `dofile`, but does the dependency-work +as well. `ls_tracked_dopackage` mimics `require` in that it does not take a +path, but a module-name like `"luasnip.loaders.from_lua"`, and then searches +the `runtimepath/lua`-directories, and path and cpath for the module. Unlike +`require`, the file will not be cached, since that would complicate the +reload-on-edit-behaviour. + + +EDIT_SNIPPETS *luasnip-loaders-edit_snippets* + +To easily edit snippets for the current session, the files loaded by any loader +can be quickly edited via +`require("luasnip.loaders").edit_snippet_files(opts:table|nil)` + +When called, it will open a `vim.ui.select`-dialog to select first a filetype, +and then (if there are multiple) the associated file to edit. + +`opts` contains four settings: + +- `ft_filter`: `fn(filetype:string) -> bool` Optionally filter initially listed + filetypes. `true` -> filetype will be listed, `false` -> not listed. Accepts + all filetypes by default. +- `format`: `fn(file:string, source_name:string) -> string|nil` `file` is simply + the path to the file, `source_name` is one of `"lua"`, `"snipmate"` or + `"vscode"`. If a string is returned, it is used as the title of the item, `nil` + on the other hand will filter out this item. The default simply replaces some + long strings (packer-path and config-path) in `file` with shorter, symbolic + names (`"$PLUGINS"`, `"$CONFIG"`), but this can be extended to + - filter files from some specific source/path + - more aggressively shorten paths using symbolic names, e.g. + `"$FRIENDLY_SNIPPETS"`. Example: hide the `*.lua` snippet files, and shorten + the path with `$LuaSnip`: + >lua + require "luasnip.loaders" .edit_snippet_files { + format = function(file, source_name) + if source_name == "lua" then return nil + else return file:gsub("/root/.config/nvim/luasnippets", "$LuaSnip") + end + end + } + < +- `edit`: `fn(file:string)` This function is supposed to open the file for + editing. The default is a simple `vim.cmd("edit " .. file)` (replace the + current buffer), but one could open the file in a split, a tab, or a floating + window, for example. +- `extend`: `fn(ft:string, ft_paths:string[]) -> (string,string)[]` This function + can be used to create additional choices for the file-selection. + - `ft`: The filetype snippet-files are queried for. + - `ft_paths`: list of paths to the known snippet files. + The function should return a list of `(string,string)`-tuples. The first of + each pair is the label that will appear in the selection-prompt, and the second + is the path that will be passed to the `edit()` function if that item was + selected. + This can be used to create a new snippet file for the current filetype: + >lua + require("luasnip.loaders").edit_snippet_files { + extend = function(ft, paths) + if #paths == 0 then + return { + { "$CONFIG/" .. ft .. ".snippets", + string.format("%s/%s.snippets", , ft) } + } + end + + return {} + end + } + < + +One comfortable way to call this function is registering it as a command: + +>vim + command! LuaSnipEdit :lua require("luasnip.loaders").edit_snippet_files() +< + + +============================================================================== +21. SnippetProxy *luasnip-snippetproxy* + +`SnippetProxy` is used internally to alleviate the upfront cost of loading +snippets from e.g. a SnipMate library or a VSCode package. This is achieved by +only parsing the snippet on expansion, not immediately after reading it from +some file. `SnippetProxy` may also be used from Lua directly to get the same +benefits: + +This will parse the snippet on startup: + +>lua + ls.parser.parse_snippet("trig", "a snippet $1!") +< + +while this will parse the snippet upon expansion: + +>lua + local sp = require("luasnip.nodes.snippetProxy") + sp("trig", "a snippet $1") +< + +`sp(context, body, opts) -> snippetProxy` + +- `context`: exactly the same as the first argument passed to `ls.s`. +- `body`: the snippet body. +- `opts`: accepts the same `opts` as `ls.s`, with some additions: + - `parse_fn`: the function for parsing the snippet. Defaults to + `ls.parser.parse_snippet` (the parser for LSP snippets), an alternative is + the parser for SnipMate snippets (`ls.parser.parse_snipmate`). + + +============================================================================== +22. ext_opts *luasnip-ext_opts* + +`ext_opts` can be used to set the `opts` (see `nvim_buf_set_extmark`) of the +extmarks used for marking node positions, either globally, per snippet or per +node. This means that they allow highlighting the text inside of nodes, or +adding virtual text to the line the node begins on. + +This is an example for the `node_ext_opts` used to set `ext_opts` of single +nodes: + +>lua + local ext_opts = { + -- these ext_opts are applied when the node is active (e.g. it has been + -- jumped into, and not out yet). + active = + -- this is the table actually passed to `nvim_buf_set_extmark`. + { + -- highlight the text inside the node red. + hl_group = "GruvboxRed" + }, + -- these ext_opts are applied when the node is not active, but + -- the snippet still is. + passive = { + -- add virtual text on the line of the node, behind all text. + virt_text = {{"virtual text!!", "GruvboxBlue"}} + }, + -- visited or unvisited are applied when a node was/was not jumped into. + visited = { + hl_group = "GruvboxBlue" + }, + unvisited = { + hl_group = "GruvboxGreen" + }, + -- and these are applied when both the node and the snippet are inactive. + snippet_passive = {} + } + + s("trig", { + i(1, "text1", { + node_ext_opts = ext_opts + }), + i(2, "text2", { + node_ext_opts = ext_opts + }) + }) +< + +In the above example, the text inside the insertNodes is higlighted in green if +they were not yet visited, in blue once they were, and red while they are. The +virtual text "virtual text!!" is visible as long as the snippet is active. + +To make defining `ext_opts` less verbose, more specific states inherit from +less specific ones: + +- `passive` inherits from `snippet_passive` +- `visited` and `unvisited` from `passive` +- `active` from `visited` + +To disable a key from a less specific state, it has to be explicitly set to its +default, e.g. to disable highlighting inherited from `passive` when the node +is `active`, `hl_group` should be set to `None`. + +------------------------------------------------------------------------------ +As stated earlier, these `ext_opts` can also be applied globally or for an +entire snippet. For this, it’s necessary to specify which kind of node a +given set of `ext_opts` should be applied to: + +>lua + local types = require("luasnip.util.types") + + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = {...}, + visited = {...}, + passive = {...}, + snippet_passive = {...} + }, + [types.choiceNode] = { + active = {...}, + unvisited = {...} + }, + [types.snippet] = { + passive = {...} + } + } + }) +< + +The above applies the given `ext_opts` to all nodes of these types, in all +snippets. + +>lua + local types = require("luasnip.util.types") + + s("trig", { i(1, "text1"), i(2, "text2") }, { + child_ext_opts = { + [types.insertNode] = { + passive = { + hl_group = "GruvboxAqua" + } + } + } + }) +< + +However, the `ext_opts` here are only applied to the `insertNodes` inside this +snippet. + +------------------------------------------------------------------------------ +By default, the `ext_opts` actually used for a node are created by extending +the `node_ext_opts` with the `effective_child_ext_opts[node.type]` of the +parent, which are in turn the parent’s `child_ext_opts` extended with the +global `ext_opts` (those set `ls.setup`). + +It’s possible to prevent both of these merges by passing +`merge_node/child_ext_opts=false` to the snippet/node-opts: + +>lua + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = {...} + } + } + }) + + s("trig", { + i(1, "text1", { + node_ext_opts = { + active = {...} + }, + merge_node_ext_opts = false + }), + i(2, "text2") + }, { + child_ext_opts = { + [types.insertNode] = { + passive = {...} + } + }, + merge_child_ext_opts = false + }) +< + +------------------------------------------------------------------------------ +The `hl_group` of the global `ext_opts` can also be set via standard highlight +groups: + +>lua + vim.cmd("hi link LuasnipInsertNodePassive GruvboxRed") + vim.cmd("hi link LuasnipSnippetPassive GruvboxBlue") + + -- needs to be called for resolving the effective ext_opts. + ls.setup({}) +< + +The names for the used highlight groups are +`"Luasnip{Passive,Active,SnippetPassive}"`, where `` can be any +kind of node in PascalCase (or "Snippet"). + +------------------------------------------------------------------------------ +One problem that might arise when nested nodes are highlighted is that the +highlight of inner nodes should be visible, e.g. above that of nodes they are +nested inside. + +This can be controlled using the `priority`-key in `ext_opts`. In +`nvim_buf_set_extmark`, that value is an absolute value, but here it is +relative to some base-priority, which is increased for each nesting level of +snippet(Nodes)s. + +Both the initial base-priority and its’ increase and can be controlled using +`ext_base_prio` and `ext_prio_increase`: + +>lua + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = { + hl_group = "GruvboxBlue", + -- the priorities should be \in [0, ext_prio_increase). + priority = 1 + } + }, + [types.choiceNode] = { + active = { + hl_group = "GruvboxRed" + -- priority defaults to 0 + } + } + } + ext_base_prio = 200, + ext_prio_increase = 2 + }) +< + +Here the highlight of an insertNode nested directly inside a choiceNode is +always visible on top of it. + + +============================================================================== +23. Docstrings *luasnip-docstrings* + +Snippet docstrings can be queried using `snippet:get_docstring()`. The function +evaluates the snippet as if it was expanded regularly, which can be problematic +if e.g. a dynamicNode in the snippet relies on inputs other than the argument +nodes. `snip.env` and `snip.captures` are populated with the names of the +queried variable and the index of the capture respectively +(`snip.env.TM_SELECTED_TEXT` -> `'$TM_SELECTED_TEXT'`, `snip.captures[1]` -> +`'$CAPTURES1'`). Although this leads to more expressive docstrings, it can +cause errors in functions that e.g. rely on a capture being a number: + +>lua + s({trig = "(%d)", regTrig = true}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +This snippet works fine because `snippet.captures[1]` is always a number. +During docstring generation, however, `snippet.captures[1]` is `'$CAPTURES1'`, +which will cause an error in the functionNode. Issues with `snippet.captures` +can be prevented by specifying `docTrig` during snippet-definition: + +>lua + s({trig = "(%d)", regTrig = true, docTrig = "3"}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +`snippet.captures` and `snippet.trigger` will be populated as if actually +triggered with `3`. + +Other issues will have to be handled manually by checking the contents of e.g. +`snip.env` or predefining the docstring for the snippet: + +>lua + s({trig = "(%d)", regTrig = true, docstring = "repeatmerepeatmerepeatme"}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +Refer to #515 for a better +example to understand `docTrig` and `docstring`. + + +============================================================================== +24. Docstring-Cache *luasnip-docstring-cache* + +Although generation of docstrings is pretty fast, it’s preferable to not redo +it as long as the snippets haven’t changed. Using +`ls.store_snippet_docstrings(snippets)` and its counterpart +`ls.load_snippet_docstrings(snippets)`, they may be serialized from or +deserialized into the snippets. Both functions accept a table structsured like +this: `{ft1={snippets}, ft2={snippets}}`. Such a table containing all snippets +can be obtained via `ls.get_snippets()`. `load` should be called before any of +the `loader`-functions as snippets loaded from VSCode style packages already +have their `docstring` set (`docstrings` wouldn’t be overwritten, but +there’d be unnecessary calls). + +The cache is located at `stdpath("cache")/luasnip/docstrings.json` (probably +`~/.cache/nvim/luasnip/docstrings.json`). + + +============================================================================== +25. Events *luasnip-events* + +Events can be used to react to some action inside snippets. These callbacks can +be defined per snippet (`callbacks`-key in snippet constructor), per-node by +passing them as `node_callbacks` in `node_opts`, or globally (autocommand). + +`callbacks`: `fn(node[, event_args]) -> event_res` All callbacks receive the +`node` associated with the event and event-specific optional arguments, +`event_args`. `event_res` is only used in one event, `pre_expand`, where some +properties of the snippet can be changed. If multiple callbacks return +`event_res`, we only guarantee that one of them will be effective, not all of +them. + +`autocommand`: Luasnip uses `User`-events. Autocommands for these can be +registered using + +>vim + au User SomeUserEvent echom "SomeUserEvent was triggered" +< + +or + +>lua + vim.api.nvim_create_autocommand("User", { + patter = "SomeUserEvent", + command = "echom SomeUserEvent was triggered" + }) +< + +The node and `event_args` can be accessed through `require("luasnip").session`: + +- `node`: `session.event_node` +- `event_args`: `session.event_args` + +**Events**: + +- `enter/leave`: Called when a node is entered/left (for example when jumping + around in a snippet). + `User-event`: `"Luasnip{Enter,Leave}"`, with `` in + PascalCase, e.g. `InsertNode` or `DynamicNode`. + `event_args`: none +- `change_choice`: When the active choice in a choiceNode is changed. + `User-event`: `"LuasnipChangeChoice"` + `event_args`: none +- `pre_expand`: Called before a snippet is expanded. Modifying text is allowed, + the expand-position will be adjusted so the snippet expands at the same + position relative to existing text. + `User-event`: `"LuasnipPreExpand"` + `event_args`: + - `expand_pos`: `{, }`, position at which the snippet will be + expanded. `` and `` are both 0-indexed. + `event_res`: + - `env_override`: `map string->(string[]|string)`, override or extend the + snippet’s environment (`snip.env`). + +A pretty useless, beyond serving as an example here, application of these would +be printing e.g. the node’s text after entering: + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipInsertNodeEnter", + callback = function() + local node = require("luasnip").session.event_node + print(table.concat(node:get_text(), "\n")) + end + }) +< + +or some information about expansions: + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipPreExpand", + callback = function() + -- get event-parameters from `session`. + local snippet = require("luasnip").session.event_node + local expand_position = + require("luasnip").session.event_args.expand_pos + + print(string.format("expanding snippet %s at %s:%s", + table.concat(snippet:get_docstring(), "\n"), + expand_position[1], + expand_position[2] + )) + end + }) +< + + +============================================================================== +26. Cleanup *luasnip-cleanup* + +The function ls.cleanup() triggers the `LuasnipCleanup` user event, that you +can listen to do some kind of cleaning in your own snippets; by default it will +empty the snippets table and the caches of the lazy_load. + + +============================================================================== +27. Logging *luasnip-logging* + +Luasnip uses logging to report unexpected program states, and information on +what’s going on in general. If something does not work as expected, taking a +look at the log (and potentially increasing the loglevel) might give some good +hints towards what is going wrong. + +The log is stored in `/luasnip.log` +(`/luasnip.log` for Neovim versions where +`stdpath("log")` does not exist), and can be opened by calling `ls.log.open()`. +The loglevel (granularity of reported events) can be adjusted by calling +`ls.log.set_loglevel("error"|"warn"|"info"|"debug")`. `"debug"` has the highest +granularity, `"error"` the lowest, the default is `"warn"`. + +Once this log grows too large (10MiB, currently not adjustable), it will be +renamed to `luasnip.log.old`, and a new, empty log created in its place. If +there already exists a `luasnip.log.old`, it will be deleted. + +`ls.log.ping()` can be used to verify the log is working correctly: it will +print a short message to the log. + + +============================================================================== +28. Source *luasnip-source* + +It is possible to attach, to a snippet, information about its source. This can +be done either by the various loaders (if it is enabled in `ls.setup` +(|luasnip-config-options|, `loaders_store_source`)), or manually. The attached +data can be used by |luasnip-extras-snippet-location| to jump to the definition +of a snippet. + +It is also possible to get/set the source of a snippet via API: + +`ls.snippet_source`: + +- `get(snippet) -> source_data`: + Retrieve the source-data of `snippet`. `source_data` always contains the key + `file`, the file in which the snippet was defined, and may additionally + contain `line` or `line_end`, the first and last line of the definition. +- `set(snippet, source)`: + Set the source of a snippet. + - `snippet`: a snippet which was added via `ls.add_snippets`. + - `source`: a `source`-object, obtained from either `from_debuginfo` or + `from_location`. +- `from_location(file, opts) -> source`: + - `file`: `string`, The path to the file in which the snippet is defined. + - `opts`: `table|nil`, optional parameters for the source. + - `line`: `number`, the first line of the definition. 1-indexed. + - `line_end`: `number`, the final line of the definition. 1-indexed. +- `from_debuginfo(debuginfo) -> source`: + Generates source from the table returned by `debug.getinfo` (from now on + referred to as `debuginfo`). `debuginfo` has to be of a frame of a function + which is backed by a file, and has to contain this information, ie. has to be + generated by `debug.get_info(*, "Sl")` (at least `"Sl"`, it may also contain + more info). + + +============================================================================== +29. Config-Options *luasnip-config-options* + +These are the settings you can provide to `luasnip.setup()`: + +- `keep_roots`: Whether snippet-roots should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `link_roots`: Whether snippet-roots should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `exit_roots`: Whether snippet-roots should exit at reaching at their last node, + `$0`. This setting is only valid for root snippets, not child snippets. This + setting may avoid unexpected behavior by disallowing to jump earlier (finished) + snippets. Check |luasnip-basics-snippet-insertion| for more information on + snippet-roots. +- `link_children`: Whether children should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `history` (deprecated): if not nil, `keep_roots`, `link_roots`, and + `link_children` will be set to the value of `history`, and `exit_roots` will + set to inverse value of `history`. This is just to ensure + backwards-compatibility. +- `update_events`: Choose which events trigger an update of the active nodes’ + dependents. Default is just `'InsertLeave'`, `'TextChanged,TextChangedI'` would + update on every change. These, like all other `*_events` are passed to + `nvim_create_autocmd` as `events`, so they can be wrapped in a table, like + >lua + ls.setup({ + update_events = {"TextChanged", "TextChangedI"} + }) + < +- `region_check_events`: Events on which to leave the current snippet-root if the + cursor is outside its’ 'region'. Disabled by default, `'CursorMoved'`, + `'CursorHold'` or `'InsertEnter'` seem reasonable. +- `delete_check_events`: When to check if the current snippet was deleted, and if + so, remove it from the history. Off by default, `'TextChanged'` (perhaps + `'InsertLeave'`, to react to changes done in Insert mode) should work just fine + (alternatively, this can also be mapped using `luasnip-delete-check`). +- `store_selection_keys`: Mapping for populating `TM_SELECTED_TEXT` and related + variables (not set by default). If you want to set this mapping yourself, map + `ls.select_keys` (not a function, actually a string/key-combination) as a rhs. +- `enable_autosnippets`: Autosnippets are disabled by default to minimize + performance penalty if unused. Set to `true` to enable. +- `ext_opts`: Additional options passed to extmarks. Can be used to add + passive/active highlight on a per-node-basis (more info in DOC.md) +- `parser_nested_assembler`: Override the default behaviour of inserting a + `choiceNode` containing the nested snippet and an empty `insertNode` for nested + placeholders (`"${1: ${2: this is nested}}"`). For an example (behaviour more + similar to vscode), check here + +- `ft_func`: Source of possible filetypes for snippets. Defaults to a function, + which returns `vim.split(vim.bo.filetype, ".", true)`, but check + filetype_functions or the + |luasnip-extras-filetype-functions|-section for more options. +- `load_ft_func`: Function to determine which filetypes belong to a given buffer + (used for `lazy_loading`). `fn(bufnr) -> filetypes (string[])`. Again, there + are some examples in filetype_functions + . +- `snip_env`: The best way to author snippets in lua involves the lua-loader (see + |luasnip-loaders-lua|). Unfortunately, this requires that snippets are defined + in separate files, which means that common definitions like `s`, `i`, `sn`, + `t`, `fmt`, … have to be repeated in each of them, and that adding more + customized functions to ease writing snippets also requires some setup. + `snip_env` can be used to insert variables into exactly the places where + lua-snippets are defined (for now only the file loaded by the lua-loader). + Setting `snip_env` to `{ some_global = "a value" }` will add (amongst the + defaults stated at the beginning of this documentation) the global variable + `some_global` while evaluating these files. There are special keys which, when + set in `snip_env` change the behaviour of this option, and are not passed + through to the lua-files: + - `__snip_env_behaviour`, string: either `"set"` or `"extend"` (default + `"extend"`) + If this is `"extend"`, the variables defined in `snip_env` will complement (and + override) the defaults. If this is not desired, `"set"` will not include the + defaults, but only the variables set here. + One side-effect of this is that analysis-tools (most likely + `lua-language-server`) for lua will generate diagnostics for the usage of + undefined symbols. If you mind the (probably) large number of generated + warnings, consider adding the undefined globals to the globals recognized by + `lua-language-server` or add `---@diagnostic disable: undefined-global` + somewhere in the affected files. +- `loaders_store_source`, boolean, whether loaders should store the source of the + loaded snippets. Enabling this means that the definition of any snippet can be + jumped to via |luasnip-extras-snippet-location|, but also entails slightly + increased memory consumption (and load-time, but it’s not really noticeable). + + +============================================================================== +30. Troubleshooting *luasnip-troubleshooting* + + +ADDING SNIPPETS *luasnip-troubleshooting-adding-snippets* + +### Loaders + +- **Filetypes**. LuaSnip uses `all` as the global filetype. As most snippet + collections don’t explicitly target LuaSnip, they may not provide global + snippets for this filetype, but another, like `_` (`honza/vim-snippets`). In + these cases, it’s necessary to extend LuaSnip’s global filetype with the + collection’s global filetype: + >lua + ls.filetype_extend("all", { "_" }) + < + In general, if some snippets don’t show up when loading a collection, a good + first step is checking the filetype LuaSnip is actually looking into (print + them for the current buffer via `:lua + print(vim.inspect(require("luasnip").get_snippet_filetypes()))`), against the + one the missing snippet is provided for (in the collection). If there is indeed + a mismatch, `filetype_extend` can be used to also search the collection’s + filetype: + >lua + ls.filetype_extend("", { "" }) + < +- **Non-default ft_func loading**. As we only load `lazy_load`ed snippets on some + events, `lazy_load` will probably not play nice when a non-default `ft_func` is + used: if it depends on e.g. the cursor position, only the filetypes for the + cursor position when the `lazy_load` events are triggered will be loaded. Check + |luasnip-extras-filetype-function|’s `extend_load_ft` for a solution. + + +GENERAL ~ + +- **Snippets sharing triggers**. If multiple snippets could be triggered at the + current buffer-position, the snippet that was defined first in one’s + configuration will be expanded first. As a small, real-world LaTeX math + example, given the following two snippets with triggers `.ov` and `ov`: + >lua + postfix( -- Insert over-line command to text via post-fix + { trig = ".ov", snippetType = "autosnippet" }, + { + f(function(_, parent) + return "\\overline{" .. parent.snippet.env.POSTFIX_MATCH .. "}" + end, {}), + } + ), + s( -- Insert over-line command + { trig = "ov", snippetType="autosnippet" }, + fmt( + [[\overline{<>}]], + { i(1) }, + { delimiters = "<>" } + ) + ), + < + If one types `x` followed by `.ov`, the postfix snippet expands producing + `\overline{x}`. However, if the `postfix` snippet above is defined _after_ the + normal snippet `s`, then the same key press sequence produces `x.\overline{}`. + This behaviour can be overridden by explicitly providing a priority to such + snippets. For example, in the above code, if the `postfix` snippet was defined + after the normal snippet `s`, then adding `priority=1001` to the `postfix` + snippet will cause it to expand as if it were defined before the normal snippet + `s`. Snippet `priority` is discussed in the Snippets section + of the + documentation. + + +============================================================================== +31. API *luasnip-api* + +`require("luasnip")`: + +- `add_snippets(ft:string or nil, snippets:list or table, opts:table or nil)`: + Makes `snippets` (list of snippets) available in `ft`. If `ft` is `nil`, + `snippets` should be a table containing lists of snippets, the keys are + corresponding filetypes. `opts` may contain the following keys: + - `type`: type of `snippets`, `"snippets"` or `"autosnippets"` (ATTENTION: + plural form used here). This serves as default value for the `snippetType` + key of each snippet added by this call see |luasnip-snippets|. + - `key`: Key that identifies snippets added via this call. + If `add_snippets` is called with a key that was already used, the snippets + from that previous call will be removed. + This can be used to reload snippets: pass an unique key to each + `add_snippets` and just redo the `add_snippets`-call when the snippets have + changed. + - `override_priority`: set priority for all snippets. + - `default_priority`: set priority only for snippets without snippet priority. +- `clean_invalidated(opts: table or nil) -> bool`: clean invalidated snippets + from internal snippet storage. Invalidated snippets are still stored; it might + be useful to actually remove them as they still have to be iterated during + expansion. + `opts` may contain: + - `inv_limit`: how many invalidated snippets are allowed. If the number of + invalid snippets doesn’t exceed this threshold, they are not yet cleaned up. + A small number of invalidated snippets (<100) probably doesn’t affect runtime + at all, whereas recreating the internal snippet storage might. +- `get_id_snippet(id)`: returns snippet corresponding to id. +- `in_snippet()`: returns true if the cursor is inside the current snippet. +- `jumpable(direction)`: returns true if the current node has a next(`direction` + = 1) or previous(`direction` = -1), e.g. whether it’s possible to jump + forward or backward to another node. +- `jump(direction)`: returns true if the jump was successful. +- `expandable()`: true if a snippet can be expanded at the current cursor + position. +- `expand(opts)`: expands the snippet at(before) the cursor. `opts` may contain: + - `jump_into_func` passed through to `ls.snip_expand`, check its’ doc for a + description. +- `expand_or_jumpable()`: returns `expandable() or jumpable(1)` (exists only + because commonly, one key is used to both jump forward and expand). +- `expand_or_locally_jumpable()`: same as `expand_or_jumpable()` except jumpable + is ignored if the cursor is not inside the current snippet. +- `locally_jumpable(direction)`: same as `jumpable()` except it is ignored if the + cursor is not inside the current snippet. +- `expand_or_jump()`: returns true if jump/expand was succesful. +- `expand_auto()`: expands the autosnippets before the cursor (not necessary to + call manually, will be called via autocmd if `enable_autosnippets` is set in + the config). +- `snip_expand(snip, opts)`: expand `snip` at the current cursor position. `opts` + may contain the following keys: + - `clear_region`: A region of text to clear after expanding (but before jumping + into) snip. It has to be at this point (and therefore passed to this function) + as clearing before expansion will populate `TM_CURRENT_LINE` and + `TM_CURRENT_WORD` with wrong values (they would miss the snippet trigger) and + clearing after expansion may move the text currently under the cursor and have + it end up not at the `i(1)`, but a `#trigger` chars to its right. The actual + values used for clearing are `from` and `to`, both (0,0)-indexed + byte-positions. If the variables don’t have to be populated with the correct + values, it’s safe to remove the text manually. + - `expand_params`: table, override `trigger`, `captures` or environment of the + snippet. This is useful for manually expanding snippets where the trigger + passed via `trig` is not the text triggering the snippet, or those which expect + `captures` (basically, snippets with a non-plaintext `trigEngine`). + One example: ```lua snip_expand(snip, { trigger = "override_trigger", captures + = {"first capture", "second capture"}, env_override = { this_key = "some + value", other_key = {"multiple", "lines"}, TM_FILENAME = + "some_other_filename.lua" } }) + - `pos`: position (`{line, col}`), (0,0)-indexed (in bytes, as returned by + `nvim_win_get_cursor()`), where the snippet should be expanded. The snippet + will be put between `(line,col-1)` and `(line,col)`. The snippet will be + expanded at the current cursor if pos is nil. + - `jump_into_func`: fn(snippet) -> node: Callback responsible for jumping into + the snippet. The returned node is set as the new active node, ie. it is the + origin of the next jump. The default is basically this: `lua function(snip) -- + jump_into set the placeholder of the snippet, 1 -- to jump forwards. return + snip:jump_into(1)` while this can be used to only insert the snippet: `lua + function(snip) return snip.insert_nodes[0] end` + - `indent`: bool?, defaults to `true`. Whether LuaSnip will try to add additional + indents to fit current indent level in snippet expanding. This option is useful + when some LSP server already take indents into consideration. In such cases, + LuaSnip should not try to add additional indents. If you are using `nvim-cmp`, + sample config: + >lua + require("cmp").setup { + snippet = { + expand = function(args) + local indent_nodes = true + if vim.api.nvim_get_option_value("filetype", { buf = 0 }) == "dart" then + indent_nodes = false + end + require("luasnip").lsp_expand(args.body, { + indent = indent_nodes, + }) + end, + }, + } + < + `opts` and any of its parameters may be nil. +- `get_active_snip()`: returns the currently active snippet (not node!). +- `choice_active()`: true if inside a choiceNode. +- `change_choice(direction)`: changes the choice in the innermost currently + active choiceNode forward (`direction` = 1) or backward (`direction` = -1). +- `unlink_current()`: removes the current snippet from the jumplist (useful if + luasnip fails to automatically detect e.g. deletion of a snippet) and sets the + current node behind the snippet, or, if not possible, before it. +- `lsp_expand(snip_string, opts)`: expands the LSP snippet defined via + `snip_string` at the cursor. `opts` can have the same options as `opts` in + `snip_expand`. +- `active_update_dependents()`: update all function/dynamicNodes that have the + current node as an argnode (will actually only update them if the text in any + of the argnodes changed). +- `available(snip_info)`: returns a table of all snippets defined for the current + filetypes(s) (`{ft1={snip1, snip2}, ft2={snip3, snip4}}`). The structure of the + snippet is defined by `snip_info` which is a function (`snip_info(snip)`) that + takes in a snippet (`snip`), finds the desired information on it, and returns + it. `snip_info` is an optional argument as a default has already been defined. + You can use it for more granular control over the table of snippets that is + returned. +- `exit_out_of_region(node)`: checks whether the cursor is still within the range + of the root-snippet `node` belongs to. If yes, no change occurs; if no, the + root-snippet is exited and its `$0` will be the new active node. If a jump + causes an error (happens mostly because the text of a snippet was deleted), the + snippet is removed from the jumplist and the current node set to the + end/beginning of the next/previous snippet. +- `store_snippet_docstrings(snippet_table)`: Stores the docstrings of all + snippets in `snippet_table` to a file + (`stdpath("cache")/luasnip/docstrings.json`). Calling + `store_snippet_docstrings(snippet_table)` after adding/modifying snippets and + `load_snippet_docstrings(snippet_table)` on startup after all snippets have + been added to `snippet_table` is a way to avoide regenerating the (unchanged) + docstrings on each startup. (Depending on when the docstrings are required and + how luasnip is loaded, it may be more sensible to let them load lazily, + e.g. just before they are required). `snippet_table` should be laid out just + like `luasnip.snippets` (it will most likely always _be_ `luasnip.snippets`). +- `load_snippet_docstrings(snippet_table)`: Load docstrings for all snippets in + `snippet_table` from `stdpath("cache")/luasnip/docstrings.json`. The docstrings + are stored and restored via trigger, meaning if two snippets for one filetype + have the same (very unlikely to happen in actual usage), bugs could occur. + `snippet_table` should be laid out as described in `store_snippet_docstrings`. +- `unlink_current_if_deleted()`: Checks if the current snippet was deleted; if + so, it is removed from the jumplist. This is not 100% reliable as LuaSnip only + sees the extmarks and their beginning/end may not be on the same position, even + if all the text between them was deleted. +- `filetype_extend(filetype:string, extend_filetypes:table of string)`: Tells + luasnip that for a buffer with `ft=filetype`, snippets from `extend_filetypes` + should be searched as well. `extend_filetypes` is a lua-array (`{ft1, ft2, + ft3}`). `luasnip.filetype_extend("lua", {"c", "cpp"})` would search and expand + c and cpp snippets for lua files. +- `filetype_set(filetype:string, replace_filetypes:table of string)`: Similar to + `filetype_extend`, but where _append_ appended filetypes, _set_ sets them: + `filetype_set("lua", {"c"})` causes only c snippets to be expanded in lua + files; lua snippets aren’t even searched. +- `cleanup()`: clears all snippets. Not useful for regular usage, only when + authoring and testing snippets. +- `refresh_notify(ft:string)`: Triggers an autocmd that other plugins can hook + into to perform various cleanup for the refreshed filetype. Useful for + signaling that new snippets were added for the filetype `ft`. +- `set_choice(indx:number)`: Changes to the `indx`th choice. If no `choiceNode` + is active, an error is thrown. If the active `choiceNode` doesn’t have an + `indx`th choice, an error is thrown. +- `get_current_choices() -> string[]`: Returns a list of multiline-strings + (themselves lists, even if they have only one line), the `i`th string + corresponding to the `i`th choice of the currently active `choiceNode`. If no + `choiceNode` is active, an error is thrown. +- `setup_snip_env()`: Adds the variables defined (during `setup`) in `snip_env` + to the callers environment. +- `get_snip_env()`: Returns `snip_env`. +- `jump_destination(direction)`: Returns the node the next jump in `direction` + (either -1 or 1, for backwards, forwards respectively) leads to, or `nil` if + the destination could not be determined (most likely because there is no node + that can be jumped to in the given direction, or there is no active node). +- `activate_node(opts)`: Activate a node in any snippet. `opts` contains the + following options: + - `pos`, `{[1]: row, [2]: byte-column}?`: The position at which a node should + be activated. Defaults to the position of the cursor. + - `strict`, `bool?`: If set, throw an error if the node under the cursor can’t + be jumped into. If not set, fall back to any node of the snippet and enter + that instead. + - `select`, `bool?`: Whether the text inside the node should be selected. + Defaults to true. + +Not covered in this section are the various node-constructors exposed by the +module, their usage is shown either previously in this file or in +`Examples/snippets.lua` (in the repo). + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/ftplugin/snippets.vim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/ftplugin/snippets.vim new file mode 100644 index 00000000..b13cc8af --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/ftplugin/snippets.vim @@ -0,0 +1,14 @@ +" Vim filetype plugin for SnipMate snippets (.snippets files) + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl et< sts< cms< fdm< fde<" + +" Use hard tabs +setlocal noexpandtab softtabstop=0 + +setlocal commentstring=#\ %s +setlocal nospell diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/luasnip-2.3.0-1.rockspec b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/luasnip-2.3.0-1.rockspec new file mode 100644 index 00000000..a392685b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/luasnip-2.3.0-1.rockspec @@ -0,0 +1,37 @@ +local git_ref = 'v2.3.0' +local modrev = '2.3.0' +local specrev = '1' + +local repo_url = 'https://github.com/L3MON4D3/LuaSnip' + +rockspec_format = '3.0' +package = 'luasnip' +version = modrev ..'-'.. specrev + +description = { + summary = 'Snippet Engine for Neovim written in Lua.', + detailed = '', + labels = { 'lua', 'neovim', 'snippet-engine', 'snippets' } , + homepage = 'https://github.com/L3MON4D3/LuaSnip', + license = 'Apache-2.0' +} + +dependencies = { 'lua >= 5.1', 'jsregexp >= 0.0.5, <= 0.0.7' } + +test_dependencies = { } + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = 'LuaSnip-' .. '2.3.0', +} + +if modrev == 'scm' or modrev == 'dev' then + source = { + url = repo_url:gsub('https', 'git') + } +end + +build = { + type = 'builtin', + copy_directories = { 'doc', 'ftplugin', 'plugin', 'syntax' } , +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/plugin/luasnip.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/plugin/luasnip.lua new file mode 100644 index 00000000..420e4c0a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/plugin/luasnip.lua @@ -0,0 +1,90 @@ +vim.filetype.add({ + extension = { snippets = "snippets" }, +}) + +local function silent_map(mode, lhs, rhs, desc) + vim.keymap.set(mode, lhs, rhs, { silent = true, desc = desc or "" }) +end + +silent_map("i", "luasnip-expand-or-jump", function() + require("luasnip").expand_or_jump() +end, "LuaSnip: Expand or jump in the current snippet") +silent_map("i", "luasnip-expand-snippet", function() + require("luasnip").expand() +end, "LuaSnip: Expand the current snippet") +silent_map("i", "luasnip-next-choice", function() + require("luasnip").change_choice(1) +end, "LuaSnip: Change to the next choice from the choiceNode") +silent_map("i", "luasnip-prev-choice", function() + require("luasnip").change_choice(-1) +end, "LuaSnip: Change to the previous choice from the choiceNode") +silent_map("i", "luasnip-jump-next", function() + require("luasnip").jump(1) +end, "LuaSnip: Jump to the next node") +silent_map("i", "luasnip-jump-prev", function() + require("luasnip").jump(-1) +end, "LuaSnip: Jump to the previous node") + +silent_map("n", "luasnip-delete-check", function() + require("luasnip").unlink_current_if_deleted() +end, "LuaSnip: Removes current snippet from jumplist") +silent_map("!", "luasnip-delete-check", function() + require("luasnip").unlink_current_if_deleted() +end, "LuaSnip: Removes current snippet from jumplist") + +silent_map("", "luasnip-expand-repeat", function() + require("luasnip").expand_repeat() +end, "LuaSnip: Repeat last node expansion") +silent_map("!", "luasnip-expand-repeat", function() + require("luasnip").expand_repeat() +end, "LuaSnip: Repeat last node expansion") + +silent_map("s", "luasnip-expand-or-jump", function() + require("luasnip").expand_or_jump() +end, "LuaSnip: Expand or jump in the current snippet") +silent_map("s", "luasnip-expand-snippet", function() + require("luasnip").expand() +end, "LuaSnip: Expand the current snippet") +silent_map("s", "luasnip-next-choice", function() + require("luasnip").change_choice(1) +end, "LuaSnip: Change to the next choice from the choiceNode") +silent_map("s", "luasnip-prev-choice", function() + require("luasnip").change_choice(-1) +end, "LuaSnip: Change to the previous choice from the choiceNode") +silent_map("s", "luasnip-jump-next", function() + require("luasnip").jump(1) +end, "LuaSnip: Jump to the next node") +silent_map("s", "luasnip-jump-prev", function() + require("luasnip").jump(-1) +end, "LuaSnip: Jump to the previous node") + +vim.api.nvim_create_user_command("LuaSnipUnlinkCurrent", function() + require("luasnip").unlink_current() +end, { force = true }) + +--stylua: ignore +vim.api.nvim_create_user_command("LuaSnipListAvailable", function() + ( + ( + vim.version + and type(vim.version) == "table" + and ( + ((vim.version().major == 0) and (vim.version().minor >= 9)) + or (vim.version().major > 0) ) + ) and vim.print + or vim.pretty_print + )(require("luasnip").available()) +end, { force = true }) + +require("luasnip.config")._setup() + +-- register these during startup so lazy_load will also load filetypes whose +-- events fired only before lazy_load is actually called. +-- (BufWinEnter -> lazy_load() wouldn't load any files without these). +vim.api.nvim_create_augroup("_luasnip_lazy_load", {}) +vim.api.nvim_create_autocmd({ "BufWinEnter", "FileType" }, { + callback = function(event) + require("luasnip.loaders").load_lazy_loaded(tonumber(event.buf)) + end, + group = "_luasnip_lazy_load", +}) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/plugin/luasnip.vim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/plugin/luasnip.vim new file mode 100644 index 00000000..d8628ea7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/plugin/luasnip.vim @@ -0,0 +1,23 @@ +function! luasnip#expandable() + return luaeval('require("luasnip").expandable()') +endfunction + +function! luasnip#expand_or_jumpable() + return luaeval('require("luasnip").expand_or_jumpable()') +endfunction + +function! luasnip#expand_or_locally_jumpable() + return luaeval('require("luasnip").expand_or_locally_jumpable()') +endfunction + +function! luasnip#locally_jumpable(direction) + return luaeval('require("luasnip").locally_jumpable(_A)', a:direction) +endfunction + +function! luasnip#jumpable(direction) + return luaeval('require("luasnip").jumpable(_A)', a:direction) +endfunction + +function! luasnip#choice_active() + return luaeval('require("luasnip").choice_active()') +endfunction diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/rock_manifest b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/rock_manifest new file mode 100644 index 00000000..2ed6b589 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/rock_manifest @@ -0,0 +1,116 @@ +rock_manifest = { + doc = { + ["luasnip.txt"] = "5014cf9a2bb7c7360fd34a1a02356207" + }, + ftplugin = { + ["snippets.vim"] = "ecd77fcf256c792985acf12ea7603322" + }, + lua = { + luasnip = { + ["_types.lua"] = "a1b1fc45d496f8ece3e17dc3541e5f93", + ["config.lua"] = "1bb0edf593b14b243b116d70cbb605c9", + ["default_config.lua"] = "51eea9c217eed18af81d580129c70461", + extras = { + ["_extra_types.lua"] = "b8f4a120d5abe22f0112efdcae358817", + ["_lambda.lua"] = "e94a2ad0606ed3c4276a573d4e7ab205", + ["_parser_combinator.lua"] = "bacc166557d1b5f9f03aff25a56bc801", + ["_treesitter.lua"] = "d9fb19599b9d95edab033fdda0684c32", + conditions = { + ["expand.lua"] = "35c3ab55ec8e9916ed7cde31cc807b08", + ["init.lua"] = "12f7e4b6fd6b5796c36ce61db5844efd", + ["show.lua"] = "0cd4059f6ba5582f409ced580e9fef13" + }, + ["expand_conditions.lua"] = "6ea7479cea2e5fac95a2045a6a283d4b", + ["filetype_functions.lua"] = "bdab365ff7bd2d7d148fdc6b3b78d9b4", + ["fmt.lua"] = "014768af82d3e7e58437e41335553eb6", + ["init.lua"] = "560335e3043e97a826fc8aee4b250fbc", + ["otf.lua"] = "8a95cdb7b582497542069bdd0886776b", + ["postfix.lua"] = "5e94359e6642b52d8ef6c9df3a90e167", + ["select_choice.lua"] = "8c924f05ee0d55ab9b0d9e5c603e1a52", + ["snip_location.lua"] = "bd0f8a7f1c61f6a001fa5781c15839d5", + ["snippet_list.lua"] = "fe61183934e0bb966b83461febdd1dcb", + ["treesitter_postfix.lua"] = "42a5143ad3c647d292b2183566fd6776" + }, + ["health.lua"] = "b6bd288f728f6897674347ad46917a5b", + ["init.lua"] = "96451aae98dbaf3ece53873298479172", + loaders = { + ["data.lua"] = "498490d7dfcf2f0374b0d20f429ba6fb", + ["from_lua.lua"] = "78d20ec3694e16581e21ed4948c26385", + ["from_snipmate.lua"] = "93e1cdc6e024549d9aa6bc917325de24", + ["from_vscode.lua"] = "0ff819764a09a735f9ea8ef51413ae83", + ["fs_watchers.lua"] = "b36b9f60988b568602350c41b032f9e6", + ["init.lua"] = "d470bc3c7bd4690199cf1c0d214782cf", + ["snippet_cache.lua"] = "e2b5cf9a46713fb3f108067100e77e0c", + ["types.lua"] = "89e18f0f21c1e77be74c1cbe85757d11", + ["util.lua"] = "77a85743643bf4d15cbe91af429908d5" + }, + nodes = { + ["absolute_indexer.lua"] = "efa73978bd91f2d90f2fc9ef53a9c38c", + ["choiceNode.lua"] = "c63618056997ec5aec6524fffff7f2fb", + ["duplicate.lua"] = "454e20ad45dbf371aa7d09aa21861f1c", + ["dynamicNode.lua"] = "28f4e7a46281dc3a2af0875ffc5ff58c", + ["functionNode.lua"] = "cf7cb4efb677a139618fd9255734873e", + ["insertNode.lua"] = "a25a723746e7ab5973901855de1d1f11", + ["key_indexer.lua"] = "d1c4887dfc10501f09b7851aea25f842", + ["multiSnippet.lua"] = "2eab1e75c5ee87096f03db006da31844", + ["node.lua"] = "c1d2f45dd25dcf5c1574ff63e0f9e88c", + ["restoreNode.lua"] = "9613ce23458968aa12737365dd302be7", + ["snippet.lua"] = "d6a31a62f45a460bc642822b6d0244f7", + ["snippetProxy.lua"] = "68262858f0f9a20a41640d5a11c43481", + ["textNode.lua"] = "c22395ab8305a581f021982cd88e2931", + util = { + ["trig_engines.lua"] = "a023c5ca92103478cbf40b7ffe2de903" + }, + ["util.lua"] = "a6be1172f1b37f2018460900b0ab987d" + }, + session = { + ["enqueueable_operations.lua"] = "2e4f57314f0573601e35943f56e8d4d8", + ["init.lua"] = "213d2ea8110e267278d62f5853151ceb", + snippet_collection = { + ["init.lua"] = "2d5015eb7cb5717f5aa44fdeebffbe59", + ["source.lua"] = "17f2f0c590d4deb57ae0e7af20c153ec" + } + }, + ["snippets.lua"] = "d41d8cd98f00b204e9800998ecf8427e", + util = { + ["_builtin_vars.lua"] = "cb7e73099c5711556f8df8821ca4a182", + ["auto_table.lua"] = "f9c5f84a99e71df229c4b6506a447727", + ["dict.lua"] = "83d98b784cfe6ab28c1d3727e7220110", + ["directed_graph.lua"] = "7eb06677cf726e6be7d64d470660677c", + ["environ.lua"] = "61b0b01947a335f749e854f039ec77ac", + ["events.lua"] = "cdac0c08202f1295a0bd9f5ee5909b3b", + ["ext_opts.lua"] = "55f3ee33870b070d50c3eae516b4724a", + ["extend_decorator.lua"] = "07576b8535b2729c9d70f5ba5b036a92", + ["functions.lua"] = "86ccff508ce6b6eeefc455308e7d4994", + ["jsonc.lua"] = "94fbde2a919a24f3957d004aaf7d136d", + ["jsregexp.lua"] = "59eb40a43fa328e82b086863dcbfa626", + ["lazy_table.lua"] = "7b0f31805982e74c3e693fd60ad42ec2", + ["log.lua"] = "ffe073da229ae489cc72e576c0ab6bee", + ["mark.lua"] = "135f7a32a6f1031ea0eb80688997f3d3", + parser = { + ["ast_parser.lua"] = "230087c74af6009d8a858259808f3e51", + ["ast_utils.lua"] = "7013bc099f5ed408c4cd49b29e4ce63c", + ["init.lua"] = "5ae80471a9893a45b12b77a35ecc8d81", + ["neovim_ast.lua"] = "08e136ffd26023ef3172ec2aed4ad2e9", + ["neovim_parser.lua"] = "c25f144947bceed6036e3d40b70bdef0" + }, + ["path.lua"] = "3767ba134238fa42469cfcbcfdf16147", + ["pattern_tokenizer.lua"] = "f4f99d27e6a6fb5385f583abc70beaab", + ["select.lua"] = "b0a8180922f7995a86ea9df7eabb162e", + ["str.lua"] = "06645f5bc876c73af9c4fd3296d620e0", + ["table.lua"] = "f4a54a5775133c776d65643be728cfdb", + ["time.lua"] = "54483e160266c85209e4399fbfc43e1b", + ["types.lua"] = "6605cc2d2293f7080d104c63663c0dac", + ["util.lua"] = "ac9ec42f0d014d908ff24c5af8172335" + } + } + }, + ["luasnip-2.3.0-1.rockspec"] = "51f9eecc66d3003eb668326f1e4a0e68", + plugin = { + ["luasnip.lua"] = "189a598faa80a372be83a52dc57fb491", + ["luasnip.vim"] = "e3d30107f8659679f6766d579ce5bf56" + }, + syntax = { + ["snippets.vim"] = "5ea760f9406519fc824e2c941ef4d858" + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/syntax/snippets.vim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/syntax/snippets.vim new file mode 100644 index 00000000..dcb39dd9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/luasnip/2.3.0-1/syntax/snippets.vim @@ -0,0 +1,29 @@ +" Syntax highlighting for .snippets files +" Hopefully this should make snippets a bit nicer to write! +syn match snipComment '^#.*' +syn match placeHolder '\${\d\+\(:.\{-}\)\=}' contains=snipCommand +syn match tabStop '\$\d\+' +syn match snipEscape '\\\\\|\\`' +syn match snipCommand '\%(\\\@ cover common +" cases with \t and " ". +syn match snipError "^[^#vsaep\t ].*$" + +hi link snippet Identifier +hi link snipComment Comment +hi link multiSnipText String +hi link snipKeyword Keyword +hi link snipEscape SpecialChar +hi link placeHolder Special +hi link tabStop Special +hi link snipCommand String +hi link snipError Error +hi link priority Number diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/go/workflows/format.yml b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/go/workflows/format.yml new file mode 100644 index 00000000..be20031f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/go/workflows/format.yml @@ -0,0 +1,30 @@ +name: Format JSON files + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "main" branch + push: + branches: [ "main" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + prettier: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + # Make sure the actual branch is checked out when running on pull requests + ref: ${{ github.head_ref }} + + - name: Format JSON + uses: creyD/prettier_action@v4.3 + with: + commit_message: "CI: format JSON files" + only_changed: true + # This part is also where you can pass other options, for example: + prettier_options: --tab-width 4 --parser json --write **/*.json diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/xml/plugin/cmp_path.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/xml/plugin/cmp_path.lua new file mode 100644 index 00000000..1d5de278 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/xml/plugin/cmp_path.lua @@ -0,0 +1 @@ +require('cmp').register_source('path', require('cmp_path').new()) diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/git-files/git-file-1 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/git-files/git-file-1 new file mode 100644 index 00000000..c139d4ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/git-files/git-file-1 @@ -0,0 +1,21 @@ +Line 1-1 +Line 1-2 +Line 1-3 +Line 1-4 +Line 1-5 +Line 1-6 +Line 1-7 +Line 1-8 +Line 1-9 +Line 1-10 +Line 1-11 +Line 1-12 +Line 1-13 +Line 1-14 +Line 1-15 +Line 1-16 +Line 1-17 +Line 1-18 +Line 1-19 +Line 1-20 +Line 1-21 diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/git-files/git-file-2 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/git-files/git-file-2 new file mode 100644 index 00000000..e45e3de1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/git-files/git-file-2 @@ -0,0 +1,21 @@ +Line 2-1 +Line 2-2 +Line 2-3 +Line 2-4 +Line 2-5 +Line 2-6 +Line 2-7 +Line 2-8 +Line 2-9 +Line 2-10 +Line 2-11 +Line 2-12 +Line 2-13 +Line 2-14 +Line 2-15 +Line 2-16 +Line 2-17 +Line 2-18 +Line 2-19 +Line 2-20 +Line 2-21 diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/lua/nvim-web-devicons.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/lua/nvim-web-devicons.lua new file mode 100644 index 00000000..eeb1bb70 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/lua/nvim-web-devicons.lua @@ -0,0 +1,17 @@ +vim.cmd('hi DevIconLicense guifg=#111111') +vim.cmd('hi DevIconMakefile guifg=#222222') +vim.cmd('hi DevIconGif guifg=#333333') +vim.cmd('hi DevIconLua guifg=#444444') +vim.cmd('hi DevIconTxt guifg=#555555') +vim.cmd('hi DevIconDefault guifg=#666666') + +return { + get_icon = function(filename, _, options) + if filename == 'LICENSE' then return '', 'DevIconLicense' end + if filename == 'Makefile' then return '', 'DevIconMakefile' end + if vim.endswith(filename, 'gif') then return '', 'DevIconGif' end + if vim.endswith(filename, 'lua') then return '', 'DevIconLua' end + if vim.endswith(filename, 'txt') then return '', 'DevIconTxt' end + if (options or {}).default then return '', 'DevIconDefault' end + end, +} diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic-file-1 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic-file-1 new file mode 100644 index 00000000..1c25b397 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic-file-1 @@ -0,0 +1,5 @@ +Error Warning Info Hint + Error + Warning + Info + Hint diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic-file-2 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic-file-2 new file mode 100644 index 00000000..c21ac383 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic-file-2 @@ -0,0 +1,4 @@ +Error +Warning +Info +Hint diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic.lua new file mode 100644 index 00000000..3dd7186a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/diagnostic.lua @@ -0,0 +1,34 @@ +local severity = vim.diagnostic.severity +_G.diag_ns = vim.api.nvim_create_namespace('mock-diagnostics') + +-- Open files +vim.cmd('edit tests/dir-extra/mocks/diagnostic-file-1') +_G.buf_id_1 = vim.api.nvim_get_current_buf() +vim.cmd('edit tests/dir-extra/mocks/diagnostic-file-2') +_G.buf_id_2 = vim.api.nvim_get_current_buf() + +-- Define diagnostic +--stylua: ignore +_G.diagnostic_arr = { + -- Several entries on one line + { bufnr = buf_id_1, lnum = 0, end_lnum = 0, col = 0, end_col = 5, message = 'Error 1', severity = severity.ERROR }, + { bufnr = buf_id_1, lnum = 0, end_lnum = 0, col = 6, end_col = 13, message = 'Warning 1', severity = severity.WARN }, + { bufnr = buf_id_1, lnum = 0, end_lnum = 0, col = 14, end_col = 18, message = 'Info 1', severity = severity.INFO }, + { bufnr = buf_id_1, lnum = 0, end_lnum = 0, col = 19, end_col = 23, message = 'Hint 1', severity = severity.HINT }, + + -- Entries on separate lines not at line start + { bufnr = buf_id_1, lnum = 1, end_lnum = 1, col = 2, end_col = 7, message = 'Error 2', severity = severity.ERROR }, + { bufnr = buf_id_1, lnum = 2, end_lnum = 2, col = 2, end_col = 9, message = 'Warning 2', severity = severity.WARN }, + { bufnr = buf_id_1, lnum = 3, end_lnum = 3, col = 2, end_col = 6, message = 'Info 2', severity = severity.INFO }, + { bufnr = buf_id_1, lnum = 4, end_lnum = 4, col = 2, end_col = 6, message = 'Hint 2', severity = severity.HINT }, + + -- Another buffer + { bufnr = buf_id_2, lnum = 0, end_lnum = 0, col = 0, end_col = 5, message = 'Error 3', severity = severity.ERROR }, + { bufnr = buf_id_2, lnum = 1, end_lnum = 1, col = 0, end_col = 7, message = 'Warning 3', severity = severity.WARN }, + { bufnr = buf_id_2, lnum = 2, end_lnum = 2, col = 0, end_col = 4, message = 'Info 3', severity = severity.INFO }, + { bufnr = buf_id_2, lnum = 3, end_lnum = 3, col = 0, end_col = 4, message = 'Hint 3', severity = severity.HINT }, +} + +-- Set diagnostic +vim.diagnostic.set(diag_ns, buf_id_1, vim.tbl_filter(function(x) return x.bufnr == buf_id_1 end, _G.diagnostic_arr), {}) +vim.diagnostic.set(diag_ns, buf_id_2, vim.tbl_filter(function(x) return x.bufnr == buf_id_2 end, _G.diagnostic_arr), {}) diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-commit b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-commit new file mode 100644 index 00000000..ffaea3bc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-commit @@ -0,0 +1,18 @@ +commit 1111111111111111111111111111111111111111 +Author: Mini Nvim +Date: Thu Jan 1 11:11:11 1970 +0300 + + Initial commit. + +diff --git a/git-files/git-file-1 b/git-files/git-file-1 +index 1111111..0123456 100644 +--- a/git-files/git-file-1 ++++ b/git-files/git-file-1 +@@ -1,5 +1,5 @@ + Line 1-1 +-Line 1-2 + Line 1-3 + Line 1-4 + Line 1-5 ++Line 1-6 + diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff new file mode 100644 index 00000000..97d1d1fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff @@ -0,0 +1,47 @@ +diff --git a/git-files/git-file-1 b/git-files/git-file-1 +index c139d4e..234d24d 100644 +--- a/git-files/git-file-1 ++++ b/git-files/git-file-1 +@@ -1,4 +1,3 @@ +-Line 1-1 + Line 1-2 + Line 1-3 + Line 1-4 +@@ -9,6 +8,7 @@ Line 1-8 + Line 1-9 + Line 1-10 + Line 1-11 ++Line new + Line 1-12 + Line 1-13 + Line 1-14 +@@ -18,4 +18,4 @@ Line 1-17 + Line 1-18 + Line 1-19 + Line 1-20 +-Line 1-21 ++Line changed +diff --git a/git-files/git-file-2 b/git-files/git-file-2 +index e45e3de..bd830d0 100644 +--- a/git-files/git-file-2 ++++ b/git-files/git-file-2 +@@ -1,5 +1,4 @@ + Line 2-1 +-Line 2-2 + Line 2-3 + Line 2-4 + Line 2-5 +@@ -10,10 +9,11 @@ Line 2-9 + Line 2-10 + Line 2-11 + Line 2-12 ++Line new + Line 2-13 + Line 2-14 + Line 2-15 +-Line 2-16 ++Line changed + Line 2-17 + Line 2-18 + Line 2-19 + diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff-unified-0 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff-unified-0 new file mode 100644 index 00000000..a3eac8dd --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff-unified-0 @@ -0,0 +1,23 @@ +diff --git a/git-files/git-file-1 b/git-files/git-file-1 +index c139d4e..234d24d 100644 +--- a/git-files/git-file-1 ++++ b/git-files/git-file-1 +@@ -1 +0,0 @@ +-Line 1-1 +@@ -11,0 +11 @@ Line 1-11 ++Line new +@@ -21 +21 @@ Line 1-20 +-Line 1-21 ++Line changed +diff --git a/git-files/git-file-2 b/git-files/git-file-2 +index e45e3de..bd830d0 100644 +--- a/git-files/git-file-2 ++++ b/git-files/git-file-2 +@@ -2 +1,0 @@ Line 2-1 +-Line 2-2 +@@ -12,0 +12 @@ Line 2-12 ++Line new +@@ -16 +16 @@ Line 2-15 +-Line 2-16 ++Line changed + diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff-unified-20 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff-unified-20 new file mode 100644 index 00000000..6a644d33 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/git-diff-unified-20 @@ -0,0 +1,57 @@ +diff --git a/git-files/git-file-1 b/git-files/git-file-1 +index c139d4e..234d24d 100644 +--- a/git-files/git-file-1 ++++ b/git-files/git-file-1 +@@ -1,21 +1,21 @@ +-Line 1-1 + Line 1-2 + Line 1-3 + Line 1-4 + Line 1-5 + Line 1-6 + Line 1-7 + Line 1-8 + Line 1-9 + Line 1-10 + Line 1-11 ++Line new + Line 1-12 + Line 1-13 + Line 1-14 + Line 1-15 + Line 1-16 + Line 1-17 + Line 1-18 + Line 1-19 + Line 1-20 +-Line 1-21 ++Line changed +diff --git a/git-files/git-file-2 b/git-files/git-file-2 +index e45e3de..bd830d0 100644 +--- a/git-files/git-file-2 ++++ b/git-files/git-file-2 +@@ -1,21 +1,21 @@ + Line 2-1 +-Line 2-2 + Line 2-3 + Line 2-4 + Line 2-5 + Line 2-6 + Line 2-7 + Line 2-8 + Line 2-9 + Line 2-10 + Line 2-11 + Line 2-12 ++Line new + Line 2-13 + Line 2-14 + Line 2-15 +-Line 2-16 ++Line changed + Line 2-17 + Line 2-18 + Line 2-19 + Line 2-20 + Line 2-21 + diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/keymaps.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/keymaps.lua new file mode 100644 index 00000000..94885298 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/keymaps.lua @@ -0,0 +1,12 @@ +local tmp = 1 + +local keymap_rhs = function() + -- Comment + _G.been_here = true +end + +--stylua: ignore +vim.api.nvim_set_keymap( + 'n', 'ga', '', + { callback = keymap_rhs, desc = 'Keymap with callback' } +) diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/lsp.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/lsp.lua new file mode 100644 index 00000000..bc964277 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/lsp.lua @@ -0,0 +1,96 @@ +local filename = vim.fn.fnamemodify('tests/dir-extra/real-files/a.lua', ':p') + +_G.lsp_buf_calls = {} + +local make_context = function(lsp_method) + return { + bufnr = vim.api.nvim_get_current_buf(), + method = lsp_method, + -- There are more fields, but none are relevant + } +end + +vim.lsp.buf.declaration = function(opts) + table.insert(_G.lsp_buf_calls, 'declaration') + local data = { + context = make_context('textDocument/declaration'), + items = { { col = 16, filename = filename, lnum = 3, text = ' x = math.max(a, 2),' } }, + title = 'Declaration', + } + opts.on_list(data) +end + +vim.lsp.buf.definition = function(opts) + table.insert(_G.lsp_buf_calls, 'definition') + local data = { + context = make_context('textDocument/definition'), + items = { { col = 16, filename = filename, lnum = 3, text = ' x = math.max(a, 2),' } }, + title = 'Definition', + } + opts.on_list(data) +end + +vim.lsp.buf.document_symbol = function(opts) + table.insert(_G.lsp_buf_calls, 'document_symbol') + local data = { + context = make_context('textDocument/documentSymbol'), + items = { + { col = 7, filename = filename, kind = 'Number', lnum = 1, text = '[Number] a' }, + { col = 7, filename = filename, kind = 'Object', lnum = 2, text = '[Object] t' }, + { col = 3, filename = filename, kind = 'Variable', lnum = 3, text = '[Variable] x' }, + { col = 3, filename = filename, kind = 'Variable', lnum = 4, text = '[Variable] y' }, + }, + title = 'Symbols in a.lua', + } + opts.on_list(data) +end + +vim.lsp.buf.implementation = function(opts) + table.insert(_G.lsp_buf_calls, 'implementation') + local data = { + context = make_context('textDocument/implementation'), + items = { { col = 16, filename = filename, lnum = 3, text = ' x = math.max(a, 2),' } }, + title = 'Implementation', + } + opts.on_list(data) +end + +vim.lsp.buf.references = function(_, opts) + table.insert(_G.lsp_buf_calls, 'references') + local data = { + context = make_context('textDocument/references'), + items = { + { col = 7, filename = filename, lnum = 1, text = 'local a = 1' }, + { col = 16, filename = filename, lnum = 3, text = ' x = math.max(a, 2),' }, + { col = 16, filename = filename, lnum = 4, text = ' y = math.min(a, 2),' }, + }, + title = 'References', + } + opts.on_list(data) +end + +vim.lsp.buf.type_definition = function(opts) + table.insert(_G.lsp_buf_calls, 'type_definition') + local data = { + context = make_context('textDocument/typeDefinition'), + items = { { col = 16, filename = filename, lnum = 3, text = ' x = math.max(a, 2),' } }, + title = 'Type definition', + } + opts.on_list(data) +end + +vim.lsp.buf.workspace_symbol = function(query, opts) + table.insert(_G.lsp_buf_calls, 'workspace_symbol') + _G.workspace_symbol_query = query + local data = { + context = make_context('textDocument/workspaceSymbol'), + items = { + { col = 7, filename = filename, kind = 'Number', lnum = 1, text = '[Number] a' }, + { col = 7, filename = filename, kind = 'Object', lnum = 2, text = '[Object] t' }, + { col = 3, filename = filename, kind = 'Variable', lnum = 3, text = '[Variable] x' }, + { col = 3, filename = filename, kind = 'Variable', lnum = 4, text = '[Variable] y' }, + }, + title = "Symbols matching ''", + } + opts.on_list(data) +end diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/spawn.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/spawn.lua new file mode 100644 index 00000000..dfd41b41 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/mocks/spawn.lua @@ -0,0 +1,55 @@ +_G.process_log = {} + +local n_pid, n_stream = 0, 0 +local new_process = function(pid) + return { + pid = pid, + close = function(_) table.insert(_G.process_log, 'Process ' .. pid .. ' was closed.') end, + } +end + +-- Mock streams by using global `_G.stdout_data_feed` and `_G.stderr_data_feed` +-- arrays as source. Each feed's element should be either string (for usable +-- data) or a table with `err` field (for error). +local stream_counts = {} +vim.loop.new_pipe = function() + -- NOTE: Use `_G.stream_type_queue` to determine which stream type to create + -- (for log purposes). This is to account for `vim.loop.spawn` creating + -- different sets of streams. Assume 'stdout' by default. + if _G.stream_type_queue == nil or #_G.stream_type_queue == 0 then _G.stream_type_queue = { 'stdout' } end + local stream_type = _G.stream_type_queue[1] + table.remove(_G.stream_type_queue, 1) + + local new_count = (stream_counts[stream_type] or 0) + 1 + stream_counts[stream_type] = new_count + local cur_stream_id = stream_type .. '_' .. new_count + + return { + read_start = function(_, callback) + -- It is not possible in Neovim<=0.9 to execute `vim.fn` functions during + -- `pipe:read_start()` + local data_feed = stream_type == 'stdout' and _G.stdout_data_feed or _G.stderr_data_feed + for _, x in ipairs(data_feed or {}) do + if type(x) == 'table' then callback(x.err, nil) end + if type(x) == 'string' then callback(nil, x) end + end + callback(nil, nil) + end, + close = function() table.insert(_G.process_log, string.format('%s was closed.', cur_stream_id)) end, + } +end + +_G.spawn_log = {} +vim.loop.spawn = function(path, options, on_exit) + local options_without_callables = vim.deepcopy(options) + options_without_callables.stdio = nil + table.insert(_G.spawn_log, { executable = path, options = options_without_callables }) + + vim.schedule(function() on_exit() end) + + n_pid = n_pid + 1 + local pid = 'Pid_' .. n_pid + return new_process(pid), pid +end + +vim.loop.process_kill = function(process) table.insert(_G.process_log, 'Process ' .. process.pid .. ' was killed.') end diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/LICENSE b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/LICENSE new file mode 100644 index 00000000..ac3dbebb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/LICENSE @@ -0,0 +1 @@ +MIT (c) diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/Makefile b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/Makefile new file mode 100644 index 00000000..eeecfe5c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/Makefile @@ -0,0 +1,3 @@ +VAR ?= 1 + +all: test diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/a.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/a.lua new file mode 100644 index 00000000..442d2203 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/a.lua @@ -0,0 +1,5 @@ +local a = 1 +local t = { + x = math.max(a, 2), + y = math.min(a, 2), +} diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/b.txt b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/b.txt new file mode 100644 index 00000000..ae951ea8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/b.txt @@ -0,0 +1,26 @@ +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +Line 7 +Line 8 +Line 9 +Line 10 +Line 11 +Line 12 +Line 13 +Line 14 +Line 15 +Line 16 +Line 17 +Line 18 +Line 19 +Line 20 +Line 21 +Line 22 +Line 23 +Line 24 +Line 25 +Line 26 diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/c.gif b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/c.gif new file mode 100644 index 00000000..ccdd7b74 Binary files /dev/null and b/config/neovim/store/lazy-plugins/rustaceanvim/lua/real-files/c.gif differ diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/Dir2/file2-1 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/Dir2/file2-1 new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dir1/file1-1 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dir1/file1-1 new file mode 100644 index 00000000..a6371918 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dir1/file1-1 @@ -0,0 +1 @@ +File 1-1 diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dir1/subdir/file1-1-1 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dir1/subdir/file1-1-1 new file mode 100644 index 00000000..1687dac5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dir1/subdir/file1-1-1 @@ -0,0 +1 @@ +File 1-1-1 diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dir3/file3-1 b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dir3/file3-1 new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/nvim-treesitter/doc/builtin-tests/dir1/file1-1 b/config/neovim/store/nvim-treesitter/doc/builtin-tests/dir1/file1-1 new file mode 100644 index 00000000..1a2e58ae --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/builtin-tests/dir1/file1-1 @@ -0,0 +1,5 @@ +dir1/file1-1 + +aaa +bbb +ccc diff --git a/config/neovim/store/nvim-treesitter/doc/builtin-tests/dir1/file1-2 b/config/neovim/store/nvim-treesitter/doc/builtin-tests/dir1/file1-2 new file mode 100644 index 00000000..33ab8b74 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/builtin-tests/dir1/file1-2 @@ -0,0 +1,5 @@ +dir1/file1-2 + +aaa +bbb +ccc diff --git a/config/neovim/store/nvim-treesitter/doc/builtin-tests/file b/config/neovim/store/nvim-treesitter/doc/builtin-tests/file new file mode 100644 index 00000000..1611a526 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/builtin-tests/file @@ -0,0 +1,5 @@ +file + +aaa +bbb +ccc diff --git a/config/neovim/store/nvim-treesitter/doc/lua/nvim-web-devicons.lua b/config/neovim/store/nvim-treesitter/doc/lua/nvim-web-devicons.lua new file mode 100644 index 00000000..eeb1bb70 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/lua/nvim-web-devicons.lua @@ -0,0 +1,17 @@ +vim.cmd('hi DevIconLicense guifg=#111111') +vim.cmd('hi DevIconMakefile guifg=#222222') +vim.cmd('hi DevIconGif guifg=#333333') +vim.cmd('hi DevIconLua guifg=#444444') +vim.cmd('hi DevIconTxt guifg=#555555') +vim.cmd('hi DevIconDefault guifg=#666666') + +return { + get_icon = function(filename, _, options) + if filename == 'LICENSE' then return '', 'DevIconLicense' end + if filename == 'Makefile' then return '', 'DevIconMakefile' end + if vim.endswith(filename, 'gif') then return '', 'DevIconGif' end + if vim.endswith(filename, 'lua') then return '', 'DevIconLua' end + if vim.endswith(filename, 'txt') then return '', 'DevIconTxt' end + if (options or {}).default then return '', 'DevIconDefault' end + end, +} diff --git a/config/neovim/store/nvim-treesitter/doc/mocks/spawn.lua b/config/neovim/store/nvim-treesitter/doc/mocks/spawn.lua new file mode 100644 index 00000000..6086b166 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/mocks/spawn.lua @@ -0,0 +1,50 @@ +_G.process_log = {} + +local n_pid, n_stdout = 0, 0 +local new_process = function(pid) + return { + pid = pid, + close = function(_) table.insert(_G.process_log, 'Process ' .. pid .. ' was closed.') end, + } +end + +-- Mock `stdout` by using global `_G.stdout_data_feed` array as source. +-- Each feed's element should be either string (for usable data) or a table +-- with `err` field (for error). +vim.loop.new_pipe = function() + n_stdout = n_stdout + 1 + local cur_stdout_id = 'Stdout_' .. n_stdout + + return { + read_start = function(_, callback) + -- It is not possible in Neovim<=0.9 to execute `vim.fn` functions during + -- `pipe:read_start()` + local vim_fn_orig = vim.deepcopy(vim.fn) + vim.fn = setmetatable({}, { __index = function() error('Can not use `vim.fn` during `read_start`.') end }) + + for _, x in ipairs(_G.stdout_data_feed or {}) do + if type(x) == 'table' then callback(x.err, nil) end + if type(x) == 'string' then callback(nil, x) end + end + callback(nil, nil) + + vim.fn = vim_fn_orig + end, + close = function() table.insert(_G.process_log, 'Stdout ' .. cur_stdout_id .. ' was closed.') end, + } +end + +_G.spawn_log = {} +vim.loop.spawn = function(path, options, on_exit) + local options_without_callables = vim.deepcopy(options) + options_without_callables.stdio = nil + table.insert(_G.spawn_log, { executable = path, options = options_without_callables }) + + vim.schedule(function() on_exit() end) + + n_pid = n_pid + 1 + local pid = 'Pid_' .. n_pid + return new_process(pid), pid +end + +vim.loop.process_kill = function(process) table.insert(_G.process_log, 'Process ' .. process.pid .. ' was killed.') end diff --git a/config/neovim/store/nvim-treesitter/doc/real-files/LICENSE b/config/neovim/store/nvim-treesitter/doc/real-files/LICENSE new file mode 100644 index 00000000..ac3dbebb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/real-files/LICENSE @@ -0,0 +1 @@ +MIT (c) diff --git a/config/neovim/store/nvim-treesitter/doc/real-files/Makefile b/config/neovim/store/nvim-treesitter/doc/real-files/Makefile new file mode 100644 index 00000000..eeecfe5c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/real-files/Makefile @@ -0,0 +1,3 @@ +VAR ?= 1 + +all: test diff --git a/config/neovim/store/nvim-treesitter/doc/real-files/a.lua b/config/neovim/store/nvim-treesitter/doc/real-files/a.lua new file mode 100644 index 00000000..140a9a09 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/real-files/a.lua @@ -0,0 +1,5 @@ +local a = 1 +local t = { + x = math.max(1, 2), + y = math.min(1, 2), +} diff --git a/config/neovim/store/nvim-treesitter/doc/real-files/b.txt b/config/neovim/store/nvim-treesitter/doc/real-files/b.txt new file mode 100644 index 00000000..ae951ea8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/real-files/b.txt @@ -0,0 +1,26 @@ +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +Line 7 +Line 8 +Line 9 +Line 10 +Line 11 +Line 12 +Line 13 +Line 14 +Line 15 +Line 16 +Line 17 +Line 18 +Line 19 +Line 20 +Line 21 +Line 22 +Line 23 +Line 24 +Line 25 +Line 26 diff --git a/config/neovim/store/nvim-treesitter/doc/real-files/c.gif b/config/neovim/store/nvim-treesitter/doc/real-files/c.gif new file mode 100644 index 00000000..ccdd7b74 Binary files /dev/null and b/config/neovim/store/nvim-treesitter/doc/real-files/c.gif differ diff --git a/config/neovim/store/nvim-treesitter/queries/ada/init-files/test-init.lua b/config/neovim/store/nvim-treesitter/queries/ada/init-files/test-init.lua new file mode 100644 index 00000000..cf11b7e4 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ada/init-files/test-init.lua @@ -0,0 +1,9 @@ +-- Avoid hit-enter-prompt +vim.o.cmdheight = 2 +-- Avoid storing unnecessary data (also sometimes avoid hit-enter-prompt) +vim.o.swapfile = false + +vim.cmd('set rtp+=.') +_G.n_event = 0 +vim.cmd('autocmd User MiniStarterOpened lua _G.n_event = _G.n_event + 1') +require('mini.starter').setup({ autoopen = true }) diff --git a/config/neovim/store/nvim-treesitter/queries/ada/sessions/Session.vim b/config/neovim/store/nvim-treesitter/queries/ada/sessions/Session.vim new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/nvim-treesitter/queries/ada/sessions/session_global.lua b/config/neovim/store/nvim-treesitter/queries/ada/sessions/session_global.lua new file mode 100644 index 00000000..e69de29b