11--- In-process LSP server for opencode completion
22--- Provides completion for files, subagents, commands, and context items
33--- Works with any LSP-compatible completion plugin (blink.cmp, nvim-cmp, etc.)
4+ local M = { _completion_done_handled = false }
45
5- local M = {}
66--- @type table<vim.lsp.protocol.Method , fun ( params : table , callback : fun ( err : lsp.ResponseError ?, result : any ))>
77local handlers = {}
88local ms = vim .lsp .protocol .Methods
3333
3434handlers [ms .workspace_executeCommand ] = function (params , callback )
3535 if params .command == ' opencode.completion_done' then
36+ if M ._completion_done_handled then
37+ callback (nil , nil )
38+ M ._completion_done_handled = false
39+ return
40+ end
3641 local item = params .arguments and params .arguments [1 ]
3742 if item then
3843 require (' opencode.ui.completion' ).on_completion_done (item )
8792--- @param item CompletionItem
8893--- @param index integer
8994--- @return OpencodeLspItem
90- local function to_lsp_item (item , index )
95+ local function to_lsp_item (item , index , params )
9196 local source = require (' opencode.ui.completion' ).get_source_by_name (item .source_name )
9297 local kind = (source and source .custom_kind ) or vim .lsp .protocol .CompletionItemKind .Function --- @type lsp.CompletionItemKind
9398 local priority = source and source .priority or 999
99+ local line = params .position .line
100+ local col = params .position .character
101+
94102 --- @type OpencodeLspItem
95103 local lsp_item = {
96104 label = (M .supports_kind_icons () and ' ' or item .kind_icon .. ' ' ) .. item .label ,
@@ -102,10 +110,16 @@ local function to_lsp_item(item, index)
102110 kind = ' plaintext' ,
103111 value = item .documentation ,
104112 } or nil ,
105- insertText = item .insert_text or item .label ,
106- insertTextFormat = vim .lsp .protocol .InsertTextFormat .PlainText ,
113+ insertText = item .label ,
107114 filterText = item .label ,
108115 sortText = string.format (' %02d_%02d_%02d_%s' , priority , item .priority or 999 , index , item .label ),
116+ textEdit = {
117+ range = {
118+ start = { line = line , character = col },
119+ [' end' ] = { line = line , character = col + (item .insert_text and # item .insert_text or 0 ) },
120+ },
121+ newText = item .insert_text ,
122+ },
109123 command = {
110124 title = ' opencode.completion_done' ,
111125 command = ' opencode.completion_done' ,
@@ -159,7 +173,7 @@ handlers[ms.textDocument_completion] = function(params, callback)
159173 is_incomplete = true
160174 end
161175
162- table.insert (all_items , to_lsp_item (item , i ))
176+ table.insert (all_items , to_lsp_item (item , i , params ))
163177 end
164178 end
165179
207221--- @return integer ? client_id
208222function M .start (bufnr )
209223 local config = M .create_config ()
224+ local augroup = vim .api .nvim_create_augroup (' OpencodeLspCompletion' , { clear = true })
225+
226+ -- Handle completion done to trigger the action, some completion plugins do not trigger the command callback, so we use this as a fallback
227+ vim .api .nvim_create_autocmd (' CompleteDonePre' , {
228+ group = augroup ,
229+ buffer = bufnr ,
230+ callback = function ()
231+ M ._completion_done_handled = true
232+ local completed_item = vim .v .completed_item
233+ if completed_item and completed_item .user_data then
234+ local data = vim .tbl_get (completed_item , ' user_data' , ' nvim' , ' lsp' , ' completion_item' , ' data' )
235+ or vim .tbl_get (completed_item , ' user_data' , ' lsp' , ' item' , ' data' )
236+
237+ local item = data and data ._opencode_item
238+
239+ if item then
240+ require (' opencode.ui.completion' ).on_completion_done (item )
241+ end
242+ end
243+ end ,
244+ })
210245 return vim .lsp .start (config , { bufnr = bufnr , silent = false })
211246end
212247
0 commit comments