mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	refactor(lsp): move completion logic into _completion module
To reduce cross-chatter between modules and for https://github.com/neovim/neovim/issues/25272 Also preparing for https://github.com/neovim/neovim/issues/25714
This commit is contained in:
		 Mathias Fussenegger
					Mathias Fussenegger
				
			
				
					committed by
					
						 Mathias Fußenegger
						Mathias Fußenegger
					
				
			
			
				
	
			
			
			 Mathias Fußenegger
						Mathias Fußenegger
					
				
			
						parent
						
							9971bea6f1
						
					
				
				
					commit
					1e10310f4c
				
			| @@ -1719,7 +1719,7 @@ extract_completion_items({result}) | |||||||
|       • {result}  (table) The result of a `textDocument/completion` request |       • {result}  (table) The result of a `textDocument/completion` request | ||||||
|  |  | ||||||
|     Return: ~ |     Return: ~ | ||||||
|         (table) List of completion items |         lsp.CompletionItem[] List of completion items | ||||||
|  |  | ||||||
|     See also: ~ |     See also: ~ | ||||||
|       • https://microsoft.github.io/language-server-protocol/specification#textDocument_completion |       • https://microsoft.github.io/language-server-protocol/specification#textDocument_completion | ||||||
| @@ -2014,7 +2014,7 @@ text_document_completion_list_to_complete_items({result}, {prefix}) | |||||||
|       • {prefix}  (string) the prefix to filter the completion items |       • {prefix}  (string) the prefix to filter the completion items | ||||||
|  |  | ||||||
|     Return: ~ |     Return: ~ | ||||||
|         (table) { matches = complete-items table, incomplete = bool } |         table[] items | ||||||
|  |  | ||||||
|     See also: ~ |     See also: ~ | ||||||
|       • complete-items |       • complete-items | ||||||
|   | |||||||
| @@ -2273,24 +2273,6 @@ function lsp.buf_notify(bufnr, method, params) | |||||||
|   return resp |   return resp | ||||||
| end | end | ||||||
|  |  | ||||||
| ---@private |  | ||||||
| local function adjust_start_col(lnum, line, items, encoding) |  | ||||||
|   local min_start_char = nil |  | ||||||
|   for _, item in pairs(items) do |  | ||||||
|     if item.textEdit and item.textEdit.range.start.line == lnum - 1 then |  | ||||||
|       if min_start_char and min_start_char ~= item.textEdit.range.start.character then |  | ||||||
|         return nil |  | ||||||
|       end |  | ||||||
|       min_start_char = item.textEdit.range.start.character |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
|   if min_start_char then |  | ||||||
|     return util._str_byteindex_enc(line, min_start_char, encoding) |  | ||||||
|   else |  | ||||||
|     return nil |  | ||||||
|   end |  | ||||||
| end |  | ||||||
|  |  | ||||||
| --- Implements 'omnifunc' compatible LSP completion. | --- Implements 'omnifunc' compatible LSP completion. | ||||||
| --- | --- | ||||||
| ---@see |complete-functions| | ---@see |complete-functions| | ||||||
| @@ -2307,82 +2289,7 @@ function lsp.omnifunc(findstart, base) | |||||||
|   if log.debug() then |   if log.debug() then | ||||||
|     log.debug('omnifunc.findstart', { findstart = findstart, base = base }) |     log.debug('omnifunc.findstart', { findstart = findstart, base = base }) | ||||||
|   end |   end | ||||||
|  |   return require('vim.lsp._completion').omnifunc(findstart, base) | ||||||
|   local bufnr = resolve_bufnr() |  | ||||||
|   local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion }) |  | ||||||
|   local remaining = #clients |  | ||||||
|   if remaining == 0 then |  | ||||||
|     return findstart == 1 and -1 or {} |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   -- Then, perform standard completion request |  | ||||||
|   if log.info() then |  | ||||||
|     log.info('base ', base) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   local win = api.nvim_get_current_win() |  | ||||||
|   local pos = api.nvim_win_get_cursor(win) |  | ||||||
|   local line = api.nvim_get_current_line() |  | ||||||
|   local line_to_cursor = line:sub(1, pos[2]) |  | ||||||
|   local _ = log.trace() and log.trace('omnifunc.line', pos, line) |  | ||||||
|  |  | ||||||
|   -- Get the start position of the current keyword |  | ||||||
|   local match_pos = vim.fn.match(line_to_cursor, '\\k*$') + 1 |  | ||||||
|   local items = {} |  | ||||||
|  |  | ||||||
|   local startbyte |  | ||||||
|  |  | ||||||
|   local function on_done() |  | ||||||
|     local mode = api.nvim_get_mode()['mode'] |  | ||||||
|     if mode == 'i' or mode == 'ic' then |  | ||||||
|       vim.fn.complete(startbyte or match_pos, items) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   for _, client in ipairs(clients) do |  | ||||||
|     local params = util.make_position_params(win, client.offset_encoding) |  | ||||||
|     client.request(ms.textDocument_completion, params, function(err, result) |  | ||||||
|       if err then |  | ||||||
|         log.warn(err.message) |  | ||||||
|       end |  | ||||||
|       if result and vim.fn.mode() == 'i' then |  | ||||||
|         -- Completion response items may be relative to a position different than `textMatch`. |  | ||||||
|         -- Concrete example, with sumneko/lua-language-server: |  | ||||||
|         -- |  | ||||||
|         -- require('plenary.asy| |  | ||||||
|         --         ▲       ▲   ▲ |  | ||||||
|         --         │       │   └── cursor_pos: 20 |  | ||||||
|         --         │       └────── textMatch: 17 |  | ||||||
|         --         └────────────── textEdit.range.start.character: 9 |  | ||||||
|         --                                 .newText = 'plenary.async' |  | ||||||
|         --                  ^^^ |  | ||||||
|         --                  prefix (We'd remove everything not starting with `asy`, |  | ||||||
|         --                  so we'd eliminate the `plenary.async` result |  | ||||||
|         -- |  | ||||||
|         -- `adjust_start_col` is used to prefer the language server boundary. |  | ||||||
|         -- |  | ||||||
|         local encoding = client.offset_encoding |  | ||||||
|         local candidates = util.extract_completion_items(result) |  | ||||||
|         local curstartbyte = adjust_start_col(pos[1], line, candidates, encoding) |  | ||||||
|         if startbyte == nil then |  | ||||||
|           startbyte = curstartbyte |  | ||||||
|         elseif curstartbyte ~= nil and curstartbyte ~= startbyte then |  | ||||||
|           startbyte = match_pos |  | ||||||
|         end |  | ||||||
|         local prefix = startbyte and line:sub(startbyte + 1) or line_to_cursor:sub(match_pos) |  | ||||||
|         local matches = util.text_document_completion_list_to_complete_items(result, prefix) |  | ||||||
|         vim.list_extend(items, matches) |  | ||||||
|       end |  | ||||||
|       remaining = remaining - 1 |  | ||||||
|       if remaining == 0 then |  | ||||||
|         vim.schedule(on_done) |  | ||||||
|       end |  | ||||||
|     end, bufnr) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   -- Return -2 to signal that we should continue completion so that we can |  | ||||||
|   -- async complete. |  | ||||||
|   return -2 |  | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Provides an interface between the built-in client and a `formatexpr` function. | --- Provides an interface between the built-in client and a `formatexpr` function. | ||||||
|   | |||||||
							
								
								
									
										210
									
								
								runtime/lua/vim/lsp/_completion.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								runtime/lua/vim/lsp/_completion.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | |||||||
|  | local M = {} | ||||||
|  | local api = vim.api | ||||||
|  | local lsp = vim.lsp | ||||||
|  | local protocol = lsp.protocol | ||||||
|  | local ms = protocol.Methods | ||||||
|  |  | ||||||
|  | ---@param input string unparsed snippet | ||||||
|  | ---@return string parsed snippet | ||||||
|  | local function parse_snippet(input) | ||||||
|  |   local ok, parsed = pcall(function() | ||||||
|  |     return require('vim.lsp._snippet_grammar').parse(input) | ||||||
|  |   end) | ||||||
|  |   return ok and tostring(parsed) or input | ||||||
|  | end | ||||||
|  |  | ||||||
|  | --- Returns text that should be inserted when selecting completion item. The | ||||||
|  | --- precedence is as follows: textEdit.newText > insertText > label | ||||||
|  | --- | ||||||
|  | --- See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||||
|  | --- | ||||||
|  | ---@param item lsp.CompletionItem | ||||||
|  | ---@return string | ||||||
|  | local function get_completion_word(item) | ||||||
|  |   if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= '' then | ||||||
|  |     if item.insertTextFormat == protocol.InsertTextFormat.PlainText then | ||||||
|  |       return item.textEdit.newText | ||||||
|  |     else | ||||||
|  |       return parse_snippet(item.textEdit.newText) | ||||||
|  |     end | ||||||
|  |   elseif item.insertText ~= nil and item.insertText ~= '' then | ||||||
|  |     if item.insertTextFormat == protocol.InsertTextFormat.PlainText then | ||||||
|  |       return item.insertText | ||||||
|  |     else | ||||||
|  |       return parse_snippet(item.insertText) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   return item.label | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ---@param result lsp.CompletionList|lsp.CompletionItem[] | ||||||
|  | ---@return lsp.CompletionItem[] | ||||||
|  | local function get_items(result) | ||||||
|  |   if result.items then | ||||||
|  |     return result.items | ||||||
|  |   end | ||||||
|  |   return result | ||||||
|  | end | ||||||
|  |  | ||||||
|  | --- Turns the result of a `textDocument/completion` request into vim-compatible | ||||||
|  | --- |complete-items|. | ||||||
|  | --- | ||||||
|  | ---@param result lsp.CompletionList|lsp.CompletionItem[] Result of `textDocument/completion` | ||||||
|  | ---@param prefix string prefix to filter the completion items | ||||||
|  | ---@return table[] | ||||||
|  | ---@see complete-items | ||||||
|  | function M._lsp_to_complete_items(result, prefix) | ||||||
|  |   local items = get_items(result) | ||||||
|  |   if vim.tbl_isempty(items) then | ||||||
|  |     return {} | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local function matches_prefix(item) | ||||||
|  |     return vim.startswith(get_completion_word(item), prefix) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   items = vim.tbl_filter(matches_prefix, items) --[[@as lsp.CompletionItem[]|]] | ||||||
|  |   table.sort(items, function(a, b) | ||||||
|  |     return (a.sortText or a.label) < (b.sortText or b.label) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   local matches = {} | ||||||
|  |   for _, item in ipairs(items) do | ||||||
|  |     local info = '' | ||||||
|  |     local documentation = item.documentation | ||||||
|  |     if documentation then | ||||||
|  |       if type(documentation) == 'string' and documentation ~= '' then | ||||||
|  |         info = documentation | ||||||
|  |       elseif type(documentation) == 'table' and type(documentation.value) == 'string' then | ||||||
|  |         info = documentation.value | ||||||
|  |       else | ||||||
|  |         vim.notify( | ||||||
|  |           ('invalid documentation value %s'):format(vim.inspect(documentation)), | ||||||
|  |           vim.log.levels.WARN | ||||||
|  |         ) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |     local word = get_completion_word(item) | ||||||
|  |     table.insert(matches, { | ||||||
|  |       word = word, | ||||||
|  |       abbr = item.label, | ||||||
|  |       kind = protocol.CompletionItemKind[item.kind] or 'Unknown', | ||||||
|  |       menu = item.detail or '', | ||||||
|  |       info = #info > 0 and info or nil, | ||||||
|  |       icase = 1, | ||||||
|  |       dup = 1, | ||||||
|  |       empty = 1, | ||||||
|  |       user_data = { | ||||||
|  |         nvim = { | ||||||
|  |           lsp = { | ||||||
|  |             completion_item = item, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }) | ||||||
|  |   end | ||||||
|  |   return matches | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ---@param items lsp.CompletionItem[] | ||||||
|  | local function adjust_start_col(lnum, line, items, encoding) | ||||||
|  |   local min_start_char = nil | ||||||
|  |   for _, item in pairs(items) do | ||||||
|  |     if item.textEdit and item.textEdit.range.start.line == lnum - 1 then | ||||||
|  |       if min_start_char and min_start_char ~= item.textEdit.range.start.character then | ||||||
|  |         return nil | ||||||
|  |       end | ||||||
|  |       min_start_char = item.textEdit.range.start.character | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   if min_start_char then | ||||||
|  |     return vim.lsp.util._str_byteindex_enc(line, min_start_char, encoding) | ||||||
|  |   else | ||||||
|  |     return nil | ||||||
|  |   end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ---@param findstart integer 0 or 1, decides behavior | ||||||
|  | ---@param base integer findstart=0, text to match against | ||||||
|  | ---@return integer|table Decided by {findstart}: | ||||||
|  | --- - findstart=0: column where the completion starts, or -2 or -3 | ||||||
|  | --- - findstart=1: list of matches (actually just calls |complete()|) | ||||||
|  | function M.omnifunc(findstart, base) | ||||||
|  |   local bufnr = api.nvim_get_current_buf() | ||||||
|  |   local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion }) | ||||||
|  |   local remaining = #clients | ||||||
|  |   if remaining == 0 then | ||||||
|  |     return findstart == 1 and -1 or {} | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local log = require('vim.lsp.log') | ||||||
|  |   -- Then, perform standard completion request | ||||||
|  |   if log.info() then | ||||||
|  |     log.info('base ', base) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local win = api.nvim_get_current_win() | ||||||
|  |   local pos = api.nvim_win_get_cursor(win) | ||||||
|  |   local line = api.nvim_get_current_line() | ||||||
|  |   local line_to_cursor = line:sub(1, pos[2]) | ||||||
|  |   log.trace('omnifunc.line', pos, line) | ||||||
|  |  | ||||||
|  |   local word_boundary = vim.fn.match(line_to_cursor, '\\k*$') + 1 --[[@as integer]] | ||||||
|  |   local items = {} | ||||||
|  |   local startbyte = nil | ||||||
|  |  | ||||||
|  |   local function on_done() | ||||||
|  |     local mode = api.nvim_get_mode()['mode'] | ||||||
|  |     if mode == 'i' or mode == 'ic' then | ||||||
|  |       vim.fn.complete(startbyte or word_boundary, items) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local util = vim.lsp.util | ||||||
|  |   for _, client in ipairs(clients) do | ||||||
|  |     local params = util.make_position_params(win, client.offset_encoding) | ||||||
|  |     client.request(ms.textDocument_completion, params, function(err, result) | ||||||
|  |       if err then | ||||||
|  |         log.warn(err.message) | ||||||
|  |       end | ||||||
|  |       if result and vim.fn.mode() == 'i' then | ||||||
|  |         -- Completion response items may be relative to a position different than `textMatch`. | ||||||
|  |         -- Concrete example, with sumneko/lua-language-server: | ||||||
|  |         -- | ||||||
|  |         -- require('plenary.asy| | ||||||
|  |         --         ▲       ▲   ▲ | ||||||
|  |         --         │       │   └── cursor_pos: 20 | ||||||
|  |         --         │       └────── textMatch: 17 | ||||||
|  |         --         └────────────── textEdit.range.start.character: 9 | ||||||
|  |         --                                 .newText = 'plenary.async' | ||||||
|  |         --                  ^^^ | ||||||
|  |         --                  prefix (We'd remove everything not starting with `asy`, | ||||||
|  |         --                  so we'd eliminate the `plenary.async` result | ||||||
|  |         -- | ||||||
|  |         -- `adjust_start_col` is used to prefer the language server boundary. | ||||||
|  |         -- | ||||||
|  |         local encoding = client.offset_encoding | ||||||
|  |         local candidates = get_items(result) | ||||||
|  |         local curstartbyte = adjust_start_col(pos[1], line, candidates, encoding) | ||||||
|  |         if startbyte == nil then | ||||||
|  |           startbyte = curstartbyte | ||||||
|  |         elseif curstartbyte ~= nil and curstartbyte ~= startbyte then | ||||||
|  |           startbyte = word_boundary | ||||||
|  |         end | ||||||
|  |         local prefix = startbyte and line:sub(startbyte + 1) or line_to_cursor:sub(word_boundary) | ||||||
|  |         local matches = M._lsp_to_complete_items(result, prefix) | ||||||
|  |         vim.list_extend(items, matches) | ||||||
|  |       end | ||||||
|  |       remaining = remaining - 1 | ||||||
|  |       if remaining == 0 then | ||||||
|  |         vim.schedule(on_done) | ||||||
|  |       end | ||||||
|  |     end, bufnr) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- Return -2 to signal that we should continue completion so that we can | ||||||
|  |   -- async complete. | ||||||
|  |   return -2 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | return M | ||||||
| @@ -242,6 +242,7 @@ local constants = { | |||||||
|  |  | ||||||
|   -- Defines whether the insert text in a completion item should be interpreted as |   -- Defines whether the insert text in a completion item should be interpreted as | ||||||
|   -- plain text or a snippet. |   -- plain text or a snippet. | ||||||
|  |   --- @enum lsp.InsertTextFormat | ||||||
|   InsertTextFormat = { |   InsertTextFormat = { | ||||||
|     -- The primary text to be inserted is treated as a plain string. |     -- The primary text to be inserted is treated as a plain string. | ||||||
|     PlainText = 1, |     PlainText = 1, | ||||||
|   | |||||||
| @@ -548,7 +548,7 @@ end | |||||||
| --- `textDocument/completion` request, which may return one of | --- `textDocument/completion` request, which may return one of | ||||||
| --- `CompletionItem[]`, `CompletionList` or null. | --- `CompletionItem[]`, `CompletionList` or null. | ||||||
| ---@param result table The result of a `textDocument/completion` request | ---@param result table The result of a `textDocument/completion` request | ||||||
| ---@return table List of completion items | ---@return lsp.CompletionItem[] List of completion items | ||||||
| ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion | ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion | ||||||
| function M.extract_completion_items(result) | function M.extract_completion_items(result) | ||||||
|   if type(result) == 'table' and result.items then |   if type(result) == 'table' and result.items then | ||||||
| @@ -619,47 +619,6 @@ function M.parse_snippet(input) | |||||||
|   return tostring(parsed) |   return tostring(parsed) | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Sorts by CompletionItem.sortText. |  | ||||||
| --- |  | ||||||
| --see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion |  | ||||||
| local function sort_completion_items(items) |  | ||||||
|   table.sort(items, function(a, b) |  | ||||||
|     return (a.sortText or a.label) < (b.sortText or b.label) |  | ||||||
|   end) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| --- Returns text that should be inserted when selecting completion item. The |  | ||||||
| --- precedence is as follows: textEdit.newText > insertText > label |  | ||||||
| --see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion |  | ||||||
| local function get_completion_word(item) |  | ||||||
|   if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= '' then |  | ||||||
|     local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat] |  | ||||||
|     if insert_text_format == 'PlainText' or insert_text_format == nil then |  | ||||||
|       return item.textEdit.newText |  | ||||||
|     else |  | ||||||
|       return M.parse_snippet(item.textEdit.newText) |  | ||||||
|     end |  | ||||||
|   elseif item.insertText ~= nil and item.insertText ~= '' then |  | ||||||
|     local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat] |  | ||||||
|     if insert_text_format == 'PlainText' or insert_text_format == nil then |  | ||||||
|       return item.insertText |  | ||||||
|     else |  | ||||||
|       return M.parse_snippet(item.insertText) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
|   return item.label |  | ||||||
| end |  | ||||||
|  |  | ||||||
| --- Some language servers return complementary candidates whose prefixes do not |  | ||||||
| --- match are also returned. So we exclude completion candidates whose prefix |  | ||||||
| --- does not match. |  | ||||||
| local function remove_unmatch_completion_items(items, prefix) |  | ||||||
|   return vim.tbl_filter(function(item) |  | ||||||
|     local word = get_completion_word(item) |  | ||||||
|     return vim.startswith(word, prefix) |  | ||||||
|   end, items) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| --- According to LSP spec, if the client set `completionItemKind.valueSet`, | --- According to LSP spec, if the client set `completionItemKind.valueSet`, | ||||||
| --- the client must handle it properly even if it receives a value outside the | --- the client must handle it properly even if it receives a value outside the | ||||||
| --- specification. | --- specification. | ||||||
| @@ -678,56 +637,10 @@ end | |||||||
| --- from |vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`, | --- from |vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`, | ||||||
| --- `CompletionList` or `null` | --- `CompletionList` or `null` | ||||||
| ---@param prefix (string) the prefix to filter the completion items | ---@param prefix (string) the prefix to filter the completion items | ||||||
| ---@return table { matches = complete-items table, incomplete = bool } | ---@return table[] items | ||||||
| ---@see complete-items | ---@see complete-items | ||||||
| function M.text_document_completion_list_to_complete_items(result, prefix) | function M.text_document_completion_list_to_complete_items(result, prefix) | ||||||
|   local items = M.extract_completion_items(result) |   return require('vim.lsp._completion')._lsp_to_complete_items(result, prefix) | ||||||
|   if vim.tbl_isempty(items) then |  | ||||||
|     return {} |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   items = remove_unmatch_completion_items(items, prefix) |  | ||||||
|   sort_completion_items(items) |  | ||||||
|  |  | ||||||
|   local matches = {} |  | ||||||
|  |  | ||||||
|   for _, completion_item in ipairs(items) do |  | ||||||
|     local info = '' |  | ||||||
|     local documentation = completion_item.documentation |  | ||||||
|     if documentation then |  | ||||||
|       if type(documentation) == 'string' and documentation ~= '' then |  | ||||||
|         info = documentation |  | ||||||
|       elseif type(documentation) == 'table' and type(documentation.value) == 'string' then |  | ||||||
|         info = documentation.value |  | ||||||
|       else |  | ||||||
|         vim.notify( |  | ||||||
|           ('invalid documentation value %s'):format(vim.inspect(documentation)), |  | ||||||
|           vim.log.levels.WARN |  | ||||||
|         ) |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     local word = get_completion_word(completion_item) |  | ||||||
|     table.insert(matches, { |  | ||||||
|       word = word, |  | ||||||
|       abbr = completion_item.label, |  | ||||||
|       kind = M._get_completion_item_kind_name(completion_item.kind), |  | ||||||
|       menu = completion_item.detail or '', |  | ||||||
|       info = #info > 0 and info or nil, |  | ||||||
|       icase = 1, |  | ||||||
|       dup = 1, |  | ||||||
|       empty = 1, |  | ||||||
|       user_data = { |  | ||||||
|         nvim = { |  | ||||||
|           lsp = { |  | ||||||
|             completion_item = completion_item, |  | ||||||
|           }, |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|     }) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   return matches |  | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Like vim.fn.bufwinid except it works across tabpages. | --- Like vim.fn.bufwinid except it works across tabpages. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user