summaryrefslogtreecommitdiffstats
path: root/.config
diff options
context:
space:
mode:
Diffstat (limited to '.config')
-rw-r--r--.config/nvim/after/ftplugin/lua.lua11
-rw-r--r--.config/nvim/plugin/50-completion.lua80
2 files changed, 91 insertions, 0 deletions
diff --git a/.config/nvim/after/ftplugin/lua.lua b/.config/nvim/after/ftplugin/lua.lua
index 05e4978..b7d29c1 100644
--- a/.config/nvim/after/ftplugin/lua.lua
+++ b/.config/nvim/after/ftplugin/lua.lua
@@ -28,7 +28,18 @@ vim.opt_local.textwidth = vim.g.dotfiles.textwidth.lua -- gw wraps at this value
vim.opt_local.colorcolumn = "+1" -- Highlight one column after 'textwidth'
vim.opt_local.complete = {
"o", -- 'omnifunc'
+ "F", -- 'completefunc' (snippet source, see plugin/50-completion.lua)
}
vim.b.autoformat = true
vim.b.format_func = format
+
+------------------------------------------------------------------------------------------------------------------------
+-- Snippets
+------------------------------------------------------------------------------------------------------------------------
+-- Trigger word -> LSP snippet body. `$1` mirrors the `${1:X}` placeholder, so the variable is typed once.
+-- Surfaced in the completion menu via the "F" source in 'complete' above (see plugin/50-completion.lua).
+vim.b.snippets = {
+ ["debug_print"] = 'vim.print("${1:X} = " .. vim.inspect($1))',
+}
+vim.bo.completefunc = "v:lua.dotfiles_snippet_source"
diff --git a/.config/nvim/plugin/50-completion.lua b/.config/nvim/plugin/50-completion.lua
index 56c99d7..f21b1f4 100644
--- a/.config/nvim/plugin/50-completion.lua
+++ b/.config/nvim/plugin/50-completion.lua
@@ -92,6 +92,86 @@ snippet.expand = function(input)
end
------------------------------------------------------------------------------------------------------------------------
+-- Snippet completion source
+------------------------------------------------------------------------------------------------------------------------
+-- Exposes `vim.b.snippets` (trigger word -> LSP snippet body) as a 'complete' function source. A filetype opts in from
+-- its ftplugin by setting `vim.b.snippets`, pointing 'completefunc' at this function, and appending "F" to 'complete'.
+-- Must be a global so `v:lua` can reach it; see `:help complete-functions` and `:help v:lua-call`.
+
+-- Render a snippet to preview text: resolve placeholders and their mirrors to the placeholder's default value.
+local function render_snippet(s)
+ local defaults = {}
+ for n, d in s:gmatch("%${(%d+):([^{}]*)}") do
+ defaults[n] = d
+ end
+ 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 defaults[body] or "" -- ${n} / ${VAR}
+ end)
+ until s == prev
+ s = s:gsub("%$(%d+)", function(n)
+ return defaults[n] or "" -- $n mirror
+ end)
+ s = s:gsub("%$[%w_]+", "") -- bare $VAR
+ s = s:gsub("\\([%$}{|,\\])", "%1") -- unescape
+ return s
+end
+
+function _G.dotfiles_snippet_source(findstart, base)
+ local snippets = vim.b.snippets or {}
+ local line = vim.api.nvim_get_current_line()
+ local col = vim.api.nvim_win_get_cursor(0)[2]
+ if findstart == 1 then
+ -- Walk back over trigger characters to find the range the match replaces.
+ local start = col
+ while start > 0 and line:sub(start, start):match("[%w_]") do
+ start = start - 1
+ end
+ return start
+ end
+ local items = {}
+ for trigger, body in pairs(snippets) do
+ if vim.startswith(trigger, base) then
+ items[#items + 1] = {
+ word = trigger,
+ kind = "Snippet",
+ menu = "[snippet]",
+ info = render_snippet(body),
+ user_data = { snippet = body },
+ }
+ end
+ end
+ return items
+end
+
+-- Expand the snippet once one of the items above is accepted (its trigger word is inserted first, so remove it).
+vim.api.nvim_create_autocmd("CompleteDone", {
+ desc = "Expand accepted snippet completion",
+ group = vim.g.dotfiles.augroup,
+ callback = function()
+ local item = vim.v.completed_item
+ local data = item.user_data
+ if type(data) ~= "table" or not data.snippet then
+ return
+ end
+ local row, col = unpack(vim.api.nvim_win_get_cursor(0))
+ vim.api.nvim_buf_set_text(0, row - 1, col - #item.word, row - 1, col, { "" })
+ vim.snippet.expand(data.snippet)
+ end,
+})
+
+------------------------------------------------------------------------------------------------------------------------
-- Command-line mode completion
------------------------------------------------------------------------------------------------------------------------
-- See `:help cmdline-completion` and `:help cmdline-autocompletion`