Skip to content

Commit 2408f14

Browse files
authored
fix(snippets): provide textEdit field for builtin snippet (#2233)
Completion items didn't include the `textEdit` field before, so blink.cmp had to guess where to insert them. That guess was sometimes off, e.g., when the character before the cursor is the same as the first character of the expanded snippet, leading to incorrect insertion. Closes #2159
1 parent afc4f4d commit 2408f14

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

lua/blink/cmp/sources/snippets/default/init.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function snippets:get_completions(context, callback)
3838
end
3939

4040
local items = vim.tbl_map(
41-
function(item) return self.registry:snippet_to_completion_item(item, context.id) end,
41+
function(item) return self.registry:snippet_to_completion_item(item, context) end,
4242
self.cache[filetype]
4343
)
4444
callback({

lua/blink/cmp/sources/snippets/default/registry.lua

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,42 @@ function registry:get_global_snippets()
107107
end
108108

109109
--- @param snippet blink.cmp.Snippet
110-
--- @param cache_key number
110+
--- @param context blink.cmp.Context
111111
--- @return blink.cmp.CompletionItem
112-
function registry:snippet_to_completion_item(snippet, cache_key)
113-
local body = type(snippet.body) == 'string' and snippet.body or table.concat(snippet.body, '\n')
112+
function registry:snippet_to_completion_item(snippet, context)
113+
local body = type(snippet.body) == 'string' and snippet.body --[[@as string]]
114+
or table.concat(snippet.body --[[@as table]], '\n')
115+
116+
local new_text = self:expand_vars(body, context.id)
117+
local cur_line, cur_col = unpack(context.cursor)
118+
119+
-- Find the position of the (longest partial) prefix just before the cursor
120+
local start_col
121+
local line = context.get_line():sub(1, cur_col)
122+
for i = #snippet.prefix, 1, -1 do
123+
local pos = cur_col - i + 1
124+
if line:sub(pos, cur_col) == snippet.prefix:sub(1, i) then
125+
start_col = pos
126+
break
127+
end
128+
end
114129

115130
---@type blink.cmp.CompletionItem
116131
return {
117132
kind = require('blink.cmp.types').CompletionItemKind.Snippet,
118133
label = snippet.prefix,
119134
insertTextFormat = vim.lsp.protocol.InsertTextFormat.Snippet,
120-
insertText = self:expand_vars(body, cache_key),
135+
insertText = new_text,
121136
description = snippet.description,
122137
labelDetails = snippet.description and self.config.use_label_description and { description = snippet.description }
123138
or nil,
139+
textEdit = {
140+
range = {
141+
start = { line = cur_line - 1, character = (start_col or context.bounds.start_col) - 1 },
142+
['end'] = { line = cur_line - 1, character = cur_col },
143+
},
144+
newText = new_text,
145+
},
124146
}
125147
end
126148

0 commit comments

Comments
 (0)