_tagfunc.lua (3216B)
1 local lsp = vim.lsp 2 local api = vim.api 3 local util = lsp.util 4 5 ---@param name string 6 ---@param range lsp.Range 7 ---@param uri string 8 ---@param position_encoding 'utf-8'|'utf-16'|'utf-32' 9 ---@return {name: string, filename: string, cmd: string, kind?: string} 10 local function mk_tag_item(name, range, uri, position_encoding) 11 local bufnr = vim.uri_to_bufnr(uri) 12 -- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position 13 local byte = util._get_line_byte_from_position(bufnr, range.start, position_encoding) + 1 14 return { 15 name = name, 16 filename = vim.uri_to_fname(uri), 17 cmd = string.format([[/\%%%dl\%%%dc/]], range.start.line + 1, byte), 18 } 19 end 20 21 ---@param pattern string 22 ---@return table[] 23 local function query_definition(pattern) 24 local bufnr = api.nvim_get_current_buf() 25 local win = api.nvim_get_current_win() 26 local results = {} 27 28 --- @param range lsp.Range 29 --- @param uri string 30 ---@param position_encoding 'utf-8'|'utf-16'|'utf-32' 31 local add = function(range, uri, position_encoding) 32 table.insert(results, mk_tag_item(pattern, range, uri, position_encoding)) 33 end 34 35 local request_results, _ = lsp.buf_request_sync(bufnr, 'textDocument/definition', function(client) 36 return util.make_position_params(win, client.offset_encoding) 37 end) 38 39 for client_id, res in pairs(request_results or {}) do 40 local client = assert(lsp.get_client_by_id(client_id)) 41 local result = res.result ---@type lsp.Location|lsp.Location[]|lsp.LocationLink[]|nil 42 43 if result then 44 local encoding = client.offset_encoding 45 -- single Location 46 if result.range then 47 add(result.range, result.uri, encoding) 48 else 49 for _, location in ipairs(result) do 50 if location.range then -- Location 51 add(location.range, location.uri, encoding) 52 else -- LocationLink 53 add(location.targetSelectionRange, location.targetUri, encoding) 54 end 55 end 56 end 57 end 58 end 59 60 return results 61 end 62 63 ---@param pattern string 64 ---@return table[] 65 local function query_workspace_symbols(pattern) 66 local results_by_client, err = 67 lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000) 68 if err then 69 return {} 70 end 71 local results = {} 72 for client_id, responses in pairs(assert(results_by_client)) do 73 local client = lsp.get_client_by_id(client_id) 74 local position_encoding = client and client.offset_encoding or 'utf-16' 75 local symbols = responses.result --[[@as lsp.SymbolInformation[]|nil]] 76 for _, symbol in pairs(symbols or {}) do 77 local loc = symbol.location 78 local item = mk_tag_item(symbol.name, loc.range, loc.uri, position_encoding) 79 item.kind = lsp.protocol.SymbolKind[symbol.kind] or 'Unknown' 80 table.insert(results, item) 81 end 82 end 83 return results 84 end 85 86 local function tagfunc(pattern, flags) 87 -- avoid definition/symbol queries for insert completion 88 if string.match(flags, 'i') then 89 return vim.NIL 90 end 91 local matches = string.match(flags, 'c') and query_definition(pattern) 92 or query_workspace_symbols(pattern) 93 -- fall back to tags if no matches 94 return #matches > 0 and matches or vim.NIL 95 end 96 97 return tagfunc