summaryrefslogtreecommitdiffstats
path: root/.config/nvim/plugin/50-completion.lua
diff options
context:
space:
mode:
Diffstat (limited to '.config/nvim/plugin/50-completion.lua')
-rw-r--r--.config/nvim/plugin/50-completion.lua56
1 files changed, 56 insertions, 0 deletions
diff --git a/.config/nvim/plugin/50-completion.lua b/.config/nvim/plugin/50-completion.lua
index caf749d..56c99d7 100644
--- a/.config/nvim/plugin/50-completion.lua
+++ b/.config/nvim/plugin/50-completion.lua
@@ -36,6 +36,62 @@ vim.keymap.set({ "i", "s" }, "<C-p>", function()
end, { expr = true })
------------------------------------------------------------------------------------------------------------------------
+-- Snippet expansion fallback
+------------------------------------------------------------------------------------------------------------------------
+-- Work around Neovim's snippet grammar rejecting placeholder defaults that mix text with nested tabstops (e.g. lua_ls's
+-- `${1:pairs(${2:t})}`): the `any_or_text` rule matches a single node, not a `(any + text)^1` sequence. On parse failure,
+-- strip the snippet markup and insert it as plain text instead of throwing. Remove once fixed upstream.
+-- The `@as table` cast retypes the module here so overriding `expand` isn't flagged as a duplicate field set.
+local snippet = vim.snippet --[[@as table]]
+local orig_expand = snippet.expand
+
+-- Reduce LSP snippet syntax to plain text: keep placeholder/variable defaults, the first choice, drop bare tabstops.
+local function strip_snippet(s)
+ local prev
+ repeat
+ prev = s
+ s = s:gsub("%$(%b{})", function(group)
+ local body = group:sub(2, -2)
+ local choice = body:match("^%d+|(.*)|$")
+ if choice then
+ return (choice:gsub(",.*$", "")) -- first choice
+ end
+ local default = body:match("^[%w_]+:(.*)$")
+ if default then
+ return default -- ${n:default} / ${VAR:default}
+ end
+ return "" -- bare ${n} / ${VAR}
+ end)
+ until s == prev
+ s = s:gsub("%$[%w_]+", "") -- bare $n / $VAR
+ s = s:gsub("\\([%$}{|,\\])", "%1") -- unescape
+ return s
+end
+
+local function insert_plain(text)
+ local win = vim.api.nvim_get_current_win()
+ local row, col = unpack(vim.api.nvim_win_get_cursor(win))
+ local indent = vim.api.nvim_get_current_line():match("^%s*") or ""
+ local lines = vim.split(text, "\n", { plain = true })
+ for i = 2, #lines do
+ lines[i] = indent .. lines[i]
+ end
+ vim.api.nvim_buf_set_text(0, row - 1, col, row - 1, col, lines)
+ local last = #lines
+ local new_row = row - 1 + (last - 1)
+ local new_col = last == 1 and col + #lines[1] or #lines[last]
+ vim.api.nvim_win_set_cursor(win, { new_row + 1, new_col })
+end
+
+snippet.expand = function(input)
+ local ok = pcall(orig_expand, input)
+ if not ok then
+ insert_plain(strip_snippet(input))
+ vim.notify("Snippet parse failed, inserted as plain text: " .. vim.inspect(input), vim.log.levels.WARN)
+ end
+end
+
+------------------------------------------------------------------------------------------------------------------------
-- Command-line mode completion
------------------------------------------------------------------------------------------------------------------------
-- See `:help cmdline-completion` and `:help cmdline-autocompletion`