mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	feat(lsp): add support for completionItem.command resolving
`command` was already resolved via a `completionItem/resolve` request but only if `additionalTextEdits` were also present, and the `resolveSupport` capability wasn't listed. Closes https://github.com/neovim/neovim/issues/32406
This commit is contained in:
		 Mathias Fussenegger
					Mathias Fussenegger
				
			
				
					committed by
					
						 Mathias Fußenegger
						Mathias Fußenegger
					
				
			
			
				
	
			
			
			 Mathias Fußenegger
						Mathias Fußenegger
					
				
			
						parent
						
							c091bc3b9a
						
					
				
				
					commit
					f20335a54c
				
			| @@ -610,13 +610,14 @@ local function on_complete_done() | |||||||
|       clear_word() |       clear_word() | ||||||
|       if err then |       if err then | ||||||
|         vim.notify_once(err.message, vim.log.levels.WARN) |         vim.notify_once(err.message, vim.log.levels.WARN) | ||||||
|       elseif result and result.additionalTextEdits then |       elseif result then | ||||||
|  |         if result.additionalTextEdits then | ||||||
|           lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, position_encoding) |           lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, position_encoding) | ||||||
|  |         end | ||||||
|         if result.command then |         if result.command then | ||||||
|           completion_item.command = result.command |           completion_item.command = result.command | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
|  |  | ||||||
|       apply_snippet_and_command() |       apply_snippet_and_command() | ||||||
|     end, bufnr) |     end, bufnr) | ||||||
|   else |   else | ||||||
|   | |||||||
| @@ -458,6 +458,7 @@ function protocol.make_client_capabilities() | |||||||
|           resolveSupport = { |           resolveSupport = { | ||||||
|             properties = { |             properties = { | ||||||
|               'additionalTextEdits', |               'additionalTextEdits', | ||||||
|  |               'command', | ||||||
|             }, |             }, | ||||||
|           }, |           }, | ||||||
|           tagSupport = { |           tagSupport = { | ||||||
|   | |||||||
| @@ -770,20 +770,25 @@ end) | |||||||
|  |  | ||||||
| --- @param name string | --- @param name string | ||||||
| --- @param completion_result lsp.CompletionList | --- @param completion_result lsp.CompletionList | ||||||
| --- @param trigger_chars? string[] | --- @param opts? {trigger_chars?: string[], resolve_result?: lsp.CompletionItem} | ||||||
| --- @return integer | --- @return integer | ||||||
| local function create_server(name, completion_result, trigger_chars) | local function create_server(name, completion_result, opts) | ||||||
|  |   opts = opts or {} | ||||||
|   return exec_lua(function() |   return exec_lua(function() | ||||||
|     local server = _G._create_server({ |     local server = _G._create_server({ | ||||||
|       capabilities = { |       capabilities = { | ||||||
|         completionProvider = { |         completionProvider = { | ||||||
|           triggerCharacters = trigger_chars or { '.' }, |           triggerCharacters = opts.trigger_chars or { '.' }, | ||||||
|  |           resolveProvider = opts.resolve_result ~= nil, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|       handlers = { |       handlers = { | ||||||
|         ['textDocument/completion'] = function(_, _, callback) |         ['textDocument/completion'] = function(_, _, callback) | ||||||
|           callback(nil, completion_result) |           callback(nil, completion_result) | ||||||
|         end, |         end, | ||||||
|  |         ['completionItem/resolve'] = function(_, _, callback) | ||||||
|  |           callback(nil, opts.resolve_result) | ||||||
|  |         end, | ||||||
|       }, |       }, | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
| @@ -794,7 +799,7 @@ local function create_server(name, completion_result, trigger_chars) | |||||||
|       cmd = server.cmd, |       cmd = server.cmd, | ||||||
|       on_attach = function(client, bufnr0) |       on_attach = function(client, bufnr0) | ||||||
|         vim.lsp.completion.enable(true, client.id, bufnr0, { |         vim.lsp.completion.enable(true, client.id, bufnr0, { | ||||||
|           autotrigger = trigger_chars ~= nil, |           autotrigger = opts.trigger_chars ~= nil, | ||||||
|           convert = function(item) |           convert = function(item) | ||||||
|             return { abbr = item.label:gsub('%b()', '') } |             return { abbr = item.label:gsub('%b()', '') } | ||||||
|           end, |           end, | ||||||
| @@ -968,7 +973,7 @@ describe('vim.lsp.completion: protocol', function() | |||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|     } |     } | ||||||
|     create_server('dummy1', results1, { 'e' }) |     create_server('dummy1', results1, { trigger_chars = { 'e' } }) | ||||||
|     local results2 = { |     local results2 = { | ||||||
|       isIncomplete = false, |       isIncomplete = false, | ||||||
|       items = { |       items = { | ||||||
| @@ -977,7 +982,7 @@ describe('vim.lsp.completion: protocol', function() | |||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|     } |     } | ||||||
|     create_server('dummy2', results2, { 'h' }) |     create_server('dummy2', results2, { trigger_chars = { 'h' } }) | ||||||
|  |  | ||||||
|     feed('h') |     feed('h') | ||||||
|     exec_lua(function() |     exec_lua(function() | ||||||
| @@ -1042,6 +1047,59 @@ describe('vim.lsp.completion: protocol', function() | |||||||
|     end) |     end) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   it('resolves and executes commands', function() | ||||||
|  |     local completion_list = { | ||||||
|  |       isIncomplete = false, | ||||||
|  |       items = { | ||||||
|  |         { | ||||||
|  |           label = 'hello', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     } | ||||||
|  |     local client_id = create_server('dummy', completion_list, { | ||||||
|  |       resolve_result = { | ||||||
|  |         label = 'hello', | ||||||
|  |         command = { | ||||||
|  |           arguments = { '1', '0' }, | ||||||
|  |           command = 'dummy', | ||||||
|  |           title = '', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }) | ||||||
|  |     exec_lua(function() | ||||||
|  |       _G.called = false | ||||||
|  |       local client = assert(vim.lsp.get_client_by_id(client_id)) | ||||||
|  |       client.commands.dummy = function() | ||||||
|  |         _G.called = true | ||||||
|  |       end | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     feed('ih') | ||||||
|  |     trigger_at_pos({ 1, 1 }) | ||||||
|  |  | ||||||
|  |     local item = completion_list.items[1] | ||||||
|  |     exec_lua(function() | ||||||
|  |       vim.v.completed_item = { | ||||||
|  |         user_data = { | ||||||
|  |           nvim = { | ||||||
|  |             lsp = { | ||||||
|  |               client_id = client_id, | ||||||
|  |               completion_item = item, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       } | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     feed('<C-x><C-o><C-y>') | ||||||
|  |  | ||||||
|  |     assert_matches(function(matches) | ||||||
|  |       eq(1, #matches) | ||||||
|  |       eq('hello', matches[1].word) | ||||||
|  |       eq(true, exec_lua('return _G.called')) | ||||||
|  |     end) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|   it('enable(…,{convert=fn}) custom word/abbr format', function() |   it('enable(…,{convert=fn}) custom word/abbr format', function() | ||||||
|     create_server('dummy', { |     create_server('dummy', { | ||||||
|       isIncomplete = false, |       isIncomplete = false, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user