Skip to content

Commit 451dd9e

Browse files
committed
feat: basic snippet expansion and text edit support
1 parent b101fc1 commit 451dd9e

File tree

2 files changed

+61
-14
lines changed

2 files changed

+61
-14
lines changed

lua/blink/cmp/cmp.lua

+55-11
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,70 @@ cmp.kind_icons = {
3333
TypeParameter = '󰬛',
3434
}
3535
cmp.filtered_items = {}
36+
-- the column of the cursor when the items were fetched
37+
cmp.items_column = -1
3638

3739
cmp.fuzzy = require('blink.fuzzy').fuzzy
3840
cmp.lsp = require('blink.cmp.lsp')
3941

4042
cmp.accept = function(cmp_win)
41-
local bufnr = vim.api.nvim_get_current_buf()
42-
local current_line, start_col, end_col = cmp.get_query_to_replace(bufnr)
43-
4443
-- Get the item from the filtered items based on the cursorline position
4544
local item = cmp.filtered_items[vim.api.nvim_win_get_cursor(cmp_win.id)[1]]
45+
local text_edit = item.item.textEdit
46+
47+
if text_edit ~= nil then
48+
-- Adjust the position of the text edit to be the current cursor position
49+
-- since the data might be outdated. We compare the cursor column position
50+
-- from when the items were fetched versus the current.
51+
-- HACK: need to figure out a better way
52+
local offset = vim.api.nvim_win_get_cursor(0)[2] - cmp.items_cursor_column
53+
text_edit.range['end'].character = text_edit.range['end'].character + offset
54+
55+
-- Delete the old text
56+
vim.api.nvim_buf_set_text(
57+
vim.api.nvim_get_current_buf(),
58+
text_edit.range.start.line,
59+
text_edit.range.start.character,
60+
text_edit.range['end'].line,
61+
text_edit.range['end'].character,
62+
{}
63+
)
4664

47-
-- Apply text edit
48-
vim.api.nvim_buf_set_text(bufnr, current_line, start_col, current_line, end_col, { item.word })
49-
vim.api.nvim_win_set_cursor(0, { current_line + 1, start_col + #item.word })
65+
-- HACK: some LSPs (typescript-language-server) include snippets in non-snippet
66+
-- completions because fuck a spec so we expand all text as a snippet
67+
vim.snippet.expand(text_edit.newText)
68+
69+
-- Apply the text edit and move the cursor
70+
-- cmp.apply_text_edits(item.client_id, { text_edit })
71+
-- vim.api.nvim_win_set_cursor(
72+
-- 0,
73+
-- { text_edit.range.start.line + 1, text_edit.range.start.character + #text_edit.newText }
74+
-- )
75+
else
76+
-- No text edit so we fallback to our own resolution
77+
-- TODO: LSP provides info on what chars should be replaced in general
78+
-- and the get_query_to_replace function should take advantage of it
79+
local current_line, start_col, end_col = cmp.get_query_to_replace(vim.api.nvim_get_current_buf())
80+
vim.api.nvim_buf_set_text(
81+
vim.api.nvim_get_current_buf(),
82+
current_line,
83+
start_col,
84+
current_line,
85+
end_col,
86+
{ item.word }
87+
)
88+
vim.api.nvim_win_set_cursor(0, { current_line + 1, start_col + #item.word })
89+
end
5090

5191
-- Apply additional text edits
5292
-- LSPs can either include these in the initial response or require a resolve
5393
-- These are used for things like auto-imports
54-
-- todo: check capabilities to know ahead of time
94+
-- TODO: check capabilities to know ahead of time
5595
if item.additionalTextEdits ~= nil then
5696
cmp.apply_additional_text_edits(item.client_id, item)
5797
else
5898
cmp.lsp.resolve(
59-
item,
99+
item.item,
60100
function(client_id, resolved_item) cmp.apply_additional_text_edits(client_id, resolved_item) end
61101
)
62102
end
@@ -95,11 +135,12 @@ cmp.select_prev = function(cmp_win, doc_win)
95135
cmp.update_doc(cmp_win, doc_win)
96136
end
97137

98-
cmp.update = function(cmp_win, doc_win, items, opts)
138+
cmp.update = function(cmp_win, doc_win, items, cursor_column, opts)
99139
local query = cmp.get_query()
100140

101141
-- get the items based on the user's query
102142
local filtered_items = cmp.filter_items(query, items)
143+
cmp.items_cursor_column = cursor_column
103144

104145
-- guards for cases where we shouldn't show the completion window
105146
local no_items = #filtered_items == 0
@@ -220,8 +261,11 @@ cmp.get_query_to_replace = function(bufnr)
220261
-- Search forward/backward for the start/end of the word
221262
local start_col = current_col
222263
while start_col > 1 do
223-
local char = line:sub(start_col - 1, start_col - 1)
224-
if char:match('[%w_\\-]') == nil then break end
264+
local char = line:sub(start_col, start_col)
265+
if char:match('[%w_\\-]') == nil then
266+
start_col = start_col + 1
267+
break
268+
end
225269
start_col = start_col - 1
226270
end
227271

lua/blink/cmp/init.lua

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ local m = {}
22

33
m.items = {}
44

5-
m.setup = function(config)
5+
m.setup = function(opts)
6+
require('blink.cmp.config').setup(opts)
7+
68
m.cmp_win = require('blink.cmp.win').new({
79
cursorline = true,
810
winhighlight = 'Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None',
@@ -82,13 +84,14 @@ m.update = function(opts)
8284
m.doc_win:temp_buf_hack()
8385

8486
-- immediately update the results
85-
m.cmp.update(m.cmp_win, m.doc_win, m.items, opts)
87+
m.cmp.update(m.cmp_win, m.doc_win, m.items, m.cmp.items_column, opts)
8688
m.cmp_win:update()
8789
m.doc_win:update()
8890
-- trigger the lsp and update the results after retrieving
91+
local cursor_column = vim.api.nvim_win_get_cursor(0)[2]
8992
m.lsp.completions(function(items)
9093
m.items = items
91-
m.cmp.update(m.cmp_win, m.doc_win, m.items, opts)
94+
m.cmp.update(m.cmp_win, m.doc_win, m.items, cursor_column, opts)
9295
m.cmp_win:update()
9396
m.doc_win:update()
9497
end)

0 commit comments

Comments
 (0)