11--- @type LuaSnip.API
22local luasnip
3+ local cmp = require (' blink.cmp' )
34local utils = require (' blink.cmp.lib.utils' )
45local text_edits = require (' blink.cmp.lib.text_edits' )
56local 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
2728end
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 ?
3172local 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
197247function 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