Skip to content

Commit e5e0c99

Browse files
committed
refactor(luasnip): wip
Update lua annotations using refactor branch Fix `docTrig` replacement using Luasnip v2.4.1 Add initial support for choice nodes
1 parent 43dfb0a commit e5e0c99

File tree

1 file changed

+72
-33
lines changed

1 file changed

+72
-33
lines changed

lua/blink/cmp/sources/snippets/luasnip.lua

Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---@type LuaSnip.API
22
local luasnip
3+
local cmp = require('blink.cmp')
34
local utils = require('blink.cmp.lib.utils')
45
local text_edits = require('blink.cmp.lib.text_edits')
56
local kind_snippet = require('blink.cmp.types').CompletionItemKind.Snippet
@@ -26,21 +27,55 @@ local function add_luasnip_callback(snippet, event, callback)
2627
snippet.callbacks[-1][events[event]] = callback
2728
end
2829

30+
---@param snippet LuaSnip.Snippet
31+
local function regex_callback(snippet, docTrig)
32+
if #snippet.insert_nodes == 0 then
33+
snippet.insert_nodes[0].static_text[1] = docTrig
34+
return
35+
end
36+
37+
local matches = { string.match(docTrig, snippet.trigger) }
38+
for i, match in ipairs(matches) do
39+
local idx = i ~= #matches and i or 0
40+
snippet.insert_nodes[idx].static_text[1] = match
41+
end
42+
end
43+
44+
---@param snippet LuaSnip.Snippet
45+
local function choice_callback(snippet)
46+
local events = require('luasnip.util.events')
47+
local types = require('luasnip.util.types')
48+
49+
for _, node in ipairs(snippet.insert_nodes) do
50+
if node.type == types.choiceNode then
51+
node.node_callbacks = {
52+
[events.enter] = function(
53+
n --[[@cast n LuaSnip.ChoiceNode]]
54+
)
55+
vim.schedule(function()
56+
local index = utils.find_idx(n.choices, function(choice) return choice == n.active_choice end)
57+
n:set_text_raw({ '' }) -- NOTE: Available since v2.4.1
58+
cmp.show({ initial_selected_item_idx = index, providers = { 'snippets' } })
59+
end)
60+
end,
61+
[events.change_choice] = function()
62+
vim.schedule(function() luasnip.jump(1) end)
63+
end,
64+
[events.leave] = function() vim.schedule(cmp.hide) end,
65+
}
66+
end
67+
end
68+
end
69+
2970
---@param snippet LuaSnip.Snippet
3071
---@return string?
3172
local function get_insert_text(snippet)
3273
local res = {}
3374
for _, node in ipairs(snippet.nodes) do
34-
---@cast node LuaSnip.Node
35-
-- TODO: How to know the node type? Would be nice to handle the others as well
36-
-- textNodes
37-
if type(node.static_text) == 'table' then res[#res + 1] = table.concat(node.static_text, '\n') end
75+
if node.static_text then res[#res + 1] = table.concat(node:get_static_text(), '\n') end
3876
end
39-
4077
-- Fallback
4178
if #res == 1 then
42-
-- Prefer docTrig over trigger
43-
---@diagnostic disable-next-line: undefined-field
4479
if snippet.docTrig then return snippet.docTrig end
4580
return snippet.trigger
4681
end
@@ -97,7 +132,24 @@ function source:get_completions(ctx, callback)
97132
--- @type blink.cmp.CompletionItem[]
98133
local items = {}
99134

100-
-- Gather snippets from relevant filetypes, including extensions
135+
if luasnip.choice_active() then
136+
---@type LuaSnip.ChoiceNode
137+
local active_choice = require('luasnip.session').active_choice_nodes[ctx.bufnr]
138+
for i, choice in ipairs(active_choice.choices) do
139+
local text = choice:get_static_text()[1]
140+
table.insert(items, {
141+
label = text,
142+
kind = kind_snippet,
143+
insertText = text,
144+
insertTextFormat = vim.lsp.protocol.InsertTextFormat.PlainText,
145+
data = { snip_id = active_choice.parent.snippet.id, choice_index = i },
146+
})
147+
end
148+
callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = items })
149+
return
150+
end
151+
152+
-- Else, gather snippets from relevant filetypes, including extensions
101153
for _, ft in ipairs(require('luasnip.util.util').get_snippet_filetypes()) do
102154
if self.items_cache[ft] and #self.items_cache[ft] > 0 then
103155
for _, item in ipairs(self.items_cache[ft]) do
@@ -114,7 +166,7 @@ function source:get_completions(ctx, callback)
114166
if self.opts.show_autosnippets then
115167
local autosnippets = luasnip.get_snippets(ft, { type = 'autosnippets' })
116168
for _, s in ipairs(autosnippets) do
117-
add_luasnip_callback(s, 'enter', require('blink.cmp').hide)
169+
add_luasnip_callback(s, 'enter', cmp.hide)
118170
end
119171
snippets = utils.shallow_copy(snippets)
120172
vim.list_extend(snippets, autosnippets)
@@ -180,11 +232,9 @@ function source:resolve(item, callback)
180232
if type(detail) == 'table' then detail = table.concat(detail, '\n') end
181233
resolved_item.detail = detail
182234

183-
---@diagnostic disable-next-line: undefined-field
184235
if snip.dscr then
185236
resolved_item.documentation = {
186237
kind = 'markdown',
187-
---@diagnostic disable-next-line: undefined-field
188238
value = table.concat(vim.lsp.util.convert_input_to_markdown_lines(snip.dscr), '\n'),
189239
}
190240
end
@@ -195,34 +245,26 @@ end
195245
---@param ctx blink.cmp.Context
196246
---@param item blink.cmp.CompletionItem
197247
function source:execute(ctx, item)
248+
if item.data.choice_index then
249+
luasnip.set_choice(item.data.choice_index)
250+
return
251+
end
252+
198253
local snip = luasnip.get_id_snippet(item.data.snip_id)
199254

200-
-- if trigger is a pattern, expand "pattern" instead of actual snippet
201-
---@diagnostic disable-next-line: undefined-field
202255
if snip.regTrig then
203-
---@diagnostic disable-next-line: undefined-field
204256
local docTrig = self.opts.prefer_doc_trig and snip.docTrig
205-
snip = snip:get_pattern_expand_helper() --[[@as LuaSnip.Snippet]]
206-
207-
if docTrig then
208-
add_luasnip_callback(snip, 'pre_expand', function(snip, _)
209-
if #snip.insert_nodes == 0 then
210-
snip.insert_nodes[0].static_text = { docTrig }
211-
else
212-
local matches = { string.match(docTrig, snip.trigger) }
213-
for i, match in ipairs(matches) do
214-
local idx = i ~= #matches and i or 0
215-
snip.insert_nodes[idx].static_text = { match }
216-
end
217-
end
218-
end)
219-
end
257+
snip = snip:get_pattern_expand_helper()
258+
if docTrig then add_luasnip_callback(snip, 'pre_expand', function(s) regex_callback(s, docTrig) end) end
259+
else
260+
add_luasnip_callback(snip, 'pre_expand', choice_callback)
220261
end
221262

222263
local cursor = ctx.get_cursor() --[[@as LuaSnip.BytecolBufferPosition]]
223264
cursor[1] = cursor[1] - 1
224265

225266
local range = text_edits.get_from_item(item).range
267+
226268
---@type LuaSnip.BufferRegion
227269
local clear_region = {
228270
from = { range.start.line, range.start.character },
@@ -233,15 +275,12 @@ function source:execute(ctx, item)
233275
local line_to_cursor = line:sub(1, cursor[2])
234276
local range_text = line:sub(range.start.character + 1, cursor[2])
235277

236-
---@type LuaSnip.Opts.SnipExpandExpandParams?
237278
local expand_params = snip:matches(line_to_cursor, {
238-
fallback_match = range_text ~= line_to_cursor and range_text,
279+
fallback_match = range_text ~= line_to_cursor and range_text or nil,
239280
})
240281

241282
if expand_params ~= nil then
242-
---@diagnostic disable-next-line: undefined-field
243283
if expand_params.clear_region ~= nil then
244-
---@diagnostic disable-next-line: undefined-field
245284
clear_region = expand_params.clear_region
246285
elseif expand_params.trigger ~= nil then
247286
clear_region.from = { cursor[1], cursor[2] - #expand_params.trigger }

0 commit comments

Comments
 (0)