Skip to content

Commit b5c25cf

Browse files
committed
fix: accept completion once filtered
1 parent 54ed293 commit b5c25cf

3 files changed

Lines changed: 88 additions & 6 deletions

File tree

ftplugin/opencode.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,18 @@ if client_id then
3333
completion.on_text_changed()
3434
end,
3535
})
36+
37+
vim.api.nvim_create_autocmd('InsertLeave', {
38+
buffer = bufnr,
39+
callback = function()
40+
completion.clear_pending()
41+
end,
42+
})
43+
44+
vim.api.nvim_create_autocmd('CompleteDone', {
45+
buffer = bufnr,
46+
callback = function()
47+
completion.on_complete_done_event()
48+
end,
49+
})
3650
end

lua/opencode/lsp/opencode_ls.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ handlers[ms.textDocument_completion] = function(params, callback)
135135
end
136136
end
137137

138-
callback(nil, { isIncomplete = is_incomplete, items = all_items })
138+
callback(nil, { isIncomplete = true, items = all_items })
139139
completion.store_completion_items(all_items)
140140
end)
141141
:catch(function(err)

lua/opencode/ui/completion.lua

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ local M = {
33
_sources = {},
44
_last_line = '',
55
_last_col = 0,
6+
_trigger_col = 0,
67
_pending = {},
8+
_done_by_event = false,
79
}
810

911
function M.setup()
@@ -42,11 +44,15 @@ function M.on_text_changed()
4244
return
4345
end
4446

47+
if M._done_by_event then
48+
M._done_by_event = false
49+
return
50+
end
51+
4552
local line = vim.api.nvim_get_current_line()
4653
local col = vim.api.nvim_win_get_cursor(0)[2]
4754

48-
-- detect inserted text
49-
local inserted = line:sub(M._last_col + 1, col)
55+
local inserted = line:sub(M._trigger_col + 1, col)
5056

5157
if M._pending[inserted] then
5258
local item = M._pending[inserted]
@@ -62,9 +68,15 @@ function M.on_text_changed()
6268
end
6369

6470
function M.store_completion_items(items)
65-
M._pending = {}
71+
local col = vim.api.nvim_win_get_cursor(0)[2]
72+
73+
if not next(M._pending) then
74+
M._trigger_col = col
75+
end
76+
6677
M._last_line = vim.api.nvim_get_current_line()
67-
M._last_col = vim.api.nvim_win_get_cursor(0)[2]
78+
M._last_col = col
79+
M._pending = {}
6880

6981
for _, item in ipairs(items or {}) do
7082
local word = item.insertText
@@ -94,6 +106,52 @@ function M.get_source_by_name(name)
94106
return nil
95107
end
96108

109+
---Handle the CompleteDone autocmd event.
110+
---This is the primary completion-done path and works regardless of whether the
111+
---completion list was filtered. vim.v.completed_item.word is the text that was
112+
---actually inserted, so we look it up directly in _pending.
113+
function M.on_complete_done_event()
114+
if not next(M._pending) then
115+
return
116+
end
117+
118+
local completed = vim.v.completed_item
119+
if not completed or completed.word == nil or completed.word == '' then
120+
M.clear_pending()
121+
return
122+
end
123+
124+
-- completed_item.word is the raw inserted word. The full insertText we
125+
-- stored as the key may include more (e.g. the trigger char + word), so
126+
-- try both the word alone and with a leading trigger character.
127+
local function try(key)
128+
local lsp_item = M._pending[key]
129+
if lsp_item and lsp_item.data and lsp_item.data._opencode_item then
130+
M._done_by_event = true
131+
M._pending = {}
132+
M.on_completion_done(lsp_item.data._opencode_item)
133+
return true
134+
end
135+
return false
136+
end
137+
138+
if not try(completed.word) then
139+
-- Some completion plugins strip the trigger char from the inserted text
140+
-- but store it in user_data. Try reconstructing the full insertText by
141+
-- scanning _pending keys that end with completed.word.
142+
for key, lsp_item in pairs(M._pending) do
143+
if key:sub(-#completed.word) == completed.word then
144+
if lsp_item.data and lsp_item.data._opencode_item then
145+
M._done_by_event = true
146+
M._pending = {}
147+
M.on_completion_done(lsp_item.data._opencode_item)
148+
break
149+
end
150+
end
151+
end
152+
end
153+
end
154+
97155
---Call the on_completion_done method for a completion item
98156
---@param item CompletionItem
99157
function M.on_completion_done(item)
@@ -108,10 +166,20 @@ function M.on_completion_done(item)
108166
break
109167
end
110168
end
169+
M.clear_pending()
111170
end
112171

113172
function M.is_visible()
114-
return M._pending and next(M._pending) ~= nil
173+
if not M._pending or not next(M._pending) then
174+
return false
175+
end
176+
return true
177+
end
178+
179+
function M.clear_pending()
180+
M._pending = {}
181+
M._done_by_event = false
182+
M._trigger_col = 0
115183
end
116184

117185
return M

0 commit comments

Comments
 (0)