From f487e5af019c7cd0f15ab9beb522c9358e8013e2 Mon Sep 17 00:00:00 2001 From: Jongwook Choi Date: Sat, 3 Feb 2024 17:47:56 -0500 Subject: fix(lsp): fix infinite loop on vim.lsp.tagfunc Problem: vim.lsp.tagfunc() causes an infinite loop. This is a bug happened while introducing deferred loading. Solution: Rename the private module to `vim.lsp._tagfunc`. --- runtime/lua/vim/lsp.lua | 4 +- runtime/lua/vim/lsp/_tagfunc.lua | 83 ++++++++++++++++++++++++++++++++++++++++ runtime/lua/vim/lsp/tagfunc.lua | 83 ---------------------------------------- 3 files changed, 85 insertions(+), 85 deletions(-) create mode 100644 runtime/lua/vim/lsp/_tagfunc.lua delete mode 100644 runtime/lua/vim/lsp/tagfunc.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5fa5a1db29..d8d47a8464 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -12,6 +12,7 @@ local lsp = vim._defer_require('vim.lsp', { _completion = ..., --- @module 'vim.lsp._completion' _dynamic = ..., --- @module 'vim.lsp._dynamic' _snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar' + _tagfunc = ..., --- @module 'vim.lsp._tagfunc' _watchfiles = ..., --- @module 'vim.lsp._watchfiles' buf = ..., --- @module 'vim.lsp.buf' codelens = ..., --- @module 'vim.lsp.codelens' @@ -22,7 +23,6 @@ local lsp = vim._defer_require('vim.lsp', { protocol = ..., --- @module 'vim.lsp.protocol' rpc = ..., --- @module 'vim.lsp.rpc' semantic_tokens = ..., --- @module 'vim.lsp.semantic_tokens' - tagfunc = ..., --- @module 'vim.lsp.tagfunc' util = ..., --- @module 'vim.lsp.util' }) @@ -2040,7 +2040,7 @@ end --- ---@return table[] tags A list of matching tags function lsp.tagfunc(pattern, flags) - return vim.lsp.tagfunc(pattern, flags) + return vim.lsp._tagfunc(pattern, flags) end ---Checks whether a client is stopped. diff --git a/runtime/lua/vim/lsp/_tagfunc.lua b/runtime/lua/vim/lsp/_tagfunc.lua new file mode 100644 index 0000000000..4ad50e4a58 --- /dev/null +++ b/runtime/lua/vim/lsp/_tagfunc.lua @@ -0,0 +1,83 @@ +local lsp = vim.lsp +local util = lsp.util +local ms = lsp.protocol.Methods + +---@param name string +---@param range lsp.Range +---@param uri string +---@param offset_encoding string +---@return {name: string, filename: string, cmd: string, kind?: string} +local function mk_tag_item(name, range, uri, offset_encoding) + local bufnr = vim.uri_to_bufnr(uri) + -- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position + local byte = util._get_line_byte_from_position(bufnr, range.start, offset_encoding) + 1 + return { + name = name, + filename = vim.uri_to_fname(uri), + cmd = string.format([[/\%%%dl\%%%dc/]], range.start.line + 1, byte), + } +end + +---@param pattern string +---@return table[] +local function query_definition(pattern) + local params = util.make_position_params() + local results_by_client, err = lsp.buf_request_sync(0, ms.textDocument_definition, params, 1000) + if err then + return {} + end + local results = {} + local add = function(range, uri, offset_encoding) + table.insert(results, mk_tag_item(pattern, range, uri, offset_encoding)) + end + for client_id, lsp_results in pairs(assert(results_by_client)) do + local client = lsp.get_client_by_id(client_id) + local offset_encoding = client and client.offset_encoding or 'utf-16' + local result = lsp_results.result or {} + if result.range then -- Location + add(result.range, result.uri) + else + result = result --[[@as (lsp.Location[]|lsp.LocationLink[])]] + for _, item in pairs(result) do + if item.range then -- Location + add(item.range, item.uri, offset_encoding) + else -- LocationLink + add(item.targetSelectionRange, item.targetUri, offset_encoding) + end + end + end + end + return results +end + +---@param pattern string +---@return table[] +local function query_workspace_symbols(pattern) + local results_by_client, err = + lsp.buf_request_sync(0, ms.workspace_symbol, { query = pattern }, 1000) + if err then + return {} + end + local results = {} + for client_id, responses in pairs(assert(results_by_client)) do + local client = lsp.get_client_by_id(client_id) + local offset_encoding = client and client.offset_encoding or 'utf-16' + local symbols = responses.result --[[@as lsp.SymbolInformation[]|nil]] + for _, symbol in pairs(symbols or {}) do + local loc = symbol.location + local item = mk_tag_item(symbol.name, loc.range, loc.uri, offset_encoding) + item.kind = lsp.protocol.SymbolKind[symbol.kind] or 'Unknown' + table.insert(results, item) + end + end + return results +end + +local function tagfunc(pattern, flags) + local matches = string.match(flags, 'c') and query_definition(pattern) + or query_workspace_symbols(pattern) + -- fall back to tags if no matches + return #matches > 0 and matches or vim.NIL +end + +return tagfunc diff --git a/runtime/lua/vim/lsp/tagfunc.lua b/runtime/lua/vim/lsp/tagfunc.lua deleted file mode 100644 index 4ad50e4a58..0000000000 --- a/runtime/lua/vim/lsp/tagfunc.lua +++ /dev/null @@ -1,83 +0,0 @@ -local lsp = vim.lsp -local util = lsp.util -local ms = lsp.protocol.Methods - ----@param name string ----@param range lsp.Range ----@param uri string ----@param offset_encoding string ----@return {name: string, filename: string, cmd: string, kind?: string} -local function mk_tag_item(name, range, uri, offset_encoding) - local bufnr = vim.uri_to_bufnr(uri) - -- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position - local byte = util._get_line_byte_from_position(bufnr, range.start, offset_encoding) + 1 - return { - name = name, - filename = vim.uri_to_fname(uri), - cmd = string.format([[/\%%%dl\%%%dc/]], range.start.line + 1, byte), - } -end - ----@param pattern string ----@return table[] -local function query_definition(pattern) - local params = util.make_position_params() - local results_by_client, err = lsp.buf_request_sync(0, ms.textDocument_definition, params, 1000) - if err then - return {} - end - local results = {} - local add = function(range, uri, offset_encoding) - table.insert(results, mk_tag_item(pattern, range, uri, offset_encoding)) - end - for client_id, lsp_results in pairs(assert(results_by_client)) do - local client = lsp.get_client_by_id(client_id) - local offset_encoding = client and client.offset_encoding or 'utf-16' - local result = lsp_results.result or {} - if result.range then -- Location - add(result.range, result.uri) - else - result = result --[[@as (lsp.Location[]|lsp.LocationLink[])]] - for _, item in pairs(result) do - if item.range then -- Location - add(item.range, item.uri, offset_encoding) - else -- LocationLink - add(item.targetSelectionRange, item.targetUri, offset_encoding) - end - end - end - end - return results -end - ----@param pattern string ----@return table[] -local function query_workspace_symbols(pattern) - local results_by_client, err = - lsp.buf_request_sync(0, ms.workspace_symbol, { query = pattern }, 1000) - if err then - return {} - end - local results = {} - for client_id, responses in pairs(assert(results_by_client)) do - local client = lsp.get_client_by_id(client_id) - local offset_encoding = client and client.offset_encoding or 'utf-16' - local symbols = responses.result --[[@as lsp.SymbolInformation[]|nil]] - for _, symbol in pairs(symbols or {}) do - local loc = symbol.location - local item = mk_tag_item(symbol.name, loc.range, loc.uri, offset_encoding) - item.kind = lsp.protocol.SymbolKind[symbol.kind] or 'Unknown' - table.insert(results, item) - end - end - return results -end - -local function tagfunc(pattern, flags) - local matches = string.match(flags, 'c') and query_definition(pattern) - or query_workspace_symbols(pattern) - -- fall back to tags if no matches - return #matches > 0 and matches or vim.NIL -end - -return tagfunc -- cgit