From df534c3042572fb958586facd02841e10186707c Mon Sep 17 00:00:00 2001 From: James Trew <66286082+jamestrew@users.noreply.github.com> Date: Thu, 10 Oct 2024 00:08:12 +0000 Subject: [PATCH 1/5] fix(git_status): correctly count result on_complete (#3321) The previous implementation with `self.manager:num_results()` could return 0 despite having results due to suspected async/event loop issues (#3316). This change counts valid entries manually to ensure accurate result count. --- lua/telescope/builtin/__git.lua | 13 +++++++++++-- lua/telescope/make_entry.lua | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lua/telescope/builtin/__git.lua b/lua/telescope/builtin/__git.lua index 1b6e9ed2dd..b24267067d 100644 --- a/lua/telescope/builtin/__git.lua +++ b/lua/telescope/builtin/__git.lua @@ -393,9 +393,18 @@ git.status = function(opts) sorter = conf.file_sorter(opts), on_complete = { function(self) - local lines = self.manager:num_results() local prompt = action_state.get_current_line() - if lines == 0 and prompt == "" then + + -- HACK: self.manager:num_results() can return 0 despite having results + -- due to some async/event loop shenanigans (#3316) + local count = 0 + for _, entry in pairs(self.finder.results) do + if entry and entry.valid ~= false then + count = count + 1 + end + end + + if count == 0 and prompt == "" then utils.notify("builtin.git_status", { msg = "No changes found", level = "ERROR", diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua index 3920e2623d..e629eeaecc 100644 --- a/lua/telescope/make_entry.lua +++ b/lua/telescope/make_entry.lua @@ -1387,7 +1387,7 @@ function make_entry.gen_from_git_status(opts) return nil end - return setmetatable({ + return make_entry.set_default_entry_mt({ value = file, status = mod, ordinal = entry, From 37dc9233a473dd6c3f54456ef9994d8f77c80211 Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Sat, 26 Oct 2024 15:33:10 +0100 Subject: [PATCH 2/5] fix: add shim for vim.lsp.util._str_byteindex() (#3338) Will be removed in Nvim 0.11: https://github.com/neovim/neovim/pull/30915 --- lua/telescope/builtin/__lsp.lua | 2 +- lua/telescope/utils.lua | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lua/telescope/builtin/__lsp.lua b/lua/telescope/builtin/__lsp.lua index 662a6af9b9..af79bcb282 100644 --- a/lua/telescope/builtin/__lsp.lua +++ b/lua/telescope/builtin/__lsp.lua @@ -105,7 +105,7 @@ end ---@return lsp.Location local function item_to_location(item, offset_encoding) local line = item.lnum - 1 - local character = vim.lsp.util._str_utfindex_enc(item.text, item.col, offset_encoding) - 1 + local character = utils.str_byteindex(item.text, item.col, offset_encoding or "utf-16") - 1 local uri if utils.is_uri(item.filename) then uri = item.filename diff --git a/lua/telescope/utils.lua b/lua/telescope/utils.lua index 145b7f3aae..d20dbbe514 100644 --- a/lua/telescope/utils.lua +++ b/lua/telescope/utils.lua @@ -17,6 +17,18 @@ local utils = {} utils.iswin = vim.loop.os_uname().sysname == "Windows_NT" +---@param s string +---@param i number +---@param encoding "utf-8" | "utf-16" | "utf-32" +---@return integer +utils.str_byteindex = function(s, i, encoding) + if vim.fn.has "nvim-0.11" == 1 then + return vim.str_byteindex(s, encoding, i, false) + else + return vim.lsp.util._str_byteindex_enc(s, i, encoding) + end +end + --TODO(clason): Remove when dropping support for Nvim 0.9 utils.islist = vim.fn.has "nvim-0.10" == 1 and vim.islist or vim.tbl_islist local flatten = function(t) From 6c468ff9ecdc7fd10dd741c40a363ec9829bd92c Mon Sep 17 00:00:00 2001 From: James Trew <66286082+jamestrew@users.noreply.github.com> Date: Tue, 29 Oct 2024 01:03:54 +0000 Subject: [PATCH 3/5] feat(lsp): use native lsp handlers for some pickers (#3335) Switch the following pickers to use the native neovim lsp handler functions using the `on_list` option: - `references` -> `vim.lsp.buf.references` - `definitions` -> `vim.lsp.buf.definition` - `type_definitions` -> `vim.lsp.buf.type_definition` - `implementations` -> `vim.lsp.buf.implementation` This offloads the request and response handling off the neovim. This also let's responses pass through any custom handlers registered via `vim.lsp.handlers`. This only affects users on neovim 0.11+. The `on_list` option was introduced in nvim 0.10 but only recently in 0.11, has neovim's `vim.lsp.buf.*` functions added improved support for responses from multiple language servers. --- lua/telescope/builtin/__lsp.lua | 113 ++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 4 deletions(-) diff --git a/lua/telescope/builtin/__lsp.lua b/lua/telescope/builtin/__lsp.lua index af79bcb282..5b2363c91a 100644 --- a/lua/telescope/builtin/__lsp.lua +++ b/lua/telescope/builtin/__lsp.lua @@ -261,19 +261,107 @@ local function list_or_jump(action, title, funname, params, opts) end) end -lsp.references = function(opts) +---@param item table +---@param opts table +local function jump(item, opts) + if opts.curr_filepath ~= item.filename then + local cmd + if opts.jump_type == "tab" then + cmd = "tabedit" + elseif opts.jump_type == "split" then + cmd = "new" + elseif opts.jump_type == "vsplit" then + cmd = "vnew" + elseif opts.jump_type == "tab drop" then + cmd = "tab drop" + end + + if cmd then + vim.cmd(string.format("%s %s", cmd, item.filename)) + end + end + + local b = item.bufnr or vim.fn.bufadd(item.filename) + vim.bo[b].buflisted = true + local w = opts.reuse_win and vim.fn.win_findbuf(b)[1] or opts.winnr + vim.api.nvim_win_set_buf(w, b) + vim.api.nvim_win_set_cursor(w, { item.lnum, item.col - 1 }) + vim._with({ win = w }, function() + -- Open folds under the cursor + vim.cmd "normal! zv" + end) +end + +local function on_list_pick_or_jump(opts) + opts.reuse_win = vim.F.if_nil(opts.reuse_win, false) + opts.curr_filepath = vim.api.nvim_buf_get_name(opts.bufnr) + + ---@param res vim.lsp.LocationOpts.OnList + return function(res) + if opts.action_handler then + res.items = opts.action_handler(res.items, opts) + end + + if #res.items == 1 and opts.jump_type ~= "never" then + jump(res.items[1], opts) + return + end + + pickers + .new(opts, { + finder = finders.new_table { + results = res.items, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() + end +end + +local function references(opts) + opts.prompt_title = vim.F.if_nil(opts.prompt_title, "LSP References") + + if not opts.include_current_line then + opts.action_handler = function(items, ctx) + local lnum = vim.api.nvim_win_get_cursor(ctx.winnr)[1] + items = vim.tbl_filter(function(v) + return not (v.filename == ctx.curr_filepath and v.lnum == lnum) + end, items) + return items + end + end + + local ctx = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) } + vim.lsp.buf.references(ctx, { on_list = on_list_pick_or_jump(opts) }) +end + +local function references_legacy(opts) opts.include_current_line = vim.F.if_nil(opts.include_current_line, false) local params = vim.lsp.util.make_position_params(opts.winnr) params.context = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) } return list_or_jump("textDocument/references", "LSP References", "builtin.lsp_references", params, opts) end -lsp.definitions = function(opts) +local function definitions(opts) + opts.prompt_title = vim.F.if_nil(opts.prompt_title, "LSP Definitions") + vim.lsp.buf.definition { on_list = on_list_pick_or_jump(opts) } +end + +local function definitions_legacy(opts) local params = vim.lsp.util.make_position_params(opts.winnr) return list_or_jump("textDocument/definition", "LSP Definitions", "builtin.lsp_definitions", params, opts) end -lsp.type_definitions = function(opts) +local function type_definitions(opts) + opts.prompt_title = vim.F.if_nil(opts.prompt_title, "LSP Type Definitions") + vim.lsp.buf.type_definition { on_list = on_list_pick_or_jump(opts) } +end + +local function type_definitions_legacy(opts) local params = vim.lsp.util.make_position_params(opts.winnr) return list_or_jump( "textDocument/typeDefinition", @@ -284,11 +372,28 @@ lsp.type_definitions = function(opts) ) end -lsp.implementations = function(opts) +local function implementations(opts) + opts.prompt_title = vim.F.if_nil(opts.prompt_title, "LSP Implementations") + vim.lsp.buf.implementation { on_list = on_list_pick_or_jump(opts) } +end + +local function implementations_legacy(opts) local params = vim.lsp.util.make_position_params(opts.winnr) return list_or_jump("textDocument/implementation", "LSP Implementations", "builtin.lsp_implementations", params, opts) end +if vim.fn.has "0.11" then + lsp.references = references + lsp.definitions = definitions + lsp.type_definitions = type_definitions + lsp.implementations = implementations +else + lsp.references = references_legacy + lsp.definitions = definitions_legacy + lsp.type_definitions = type_definitions_legacy + lsp.implementations = implementations_legacy +end + local symbols_sorter = function(symbols) if vim.tbl_isempty(symbols) then return symbols From f06e7a887185796e0d37088237572f8ce03a1553 Mon Sep 17 00:00:00 2001 From: James Trew <66286082+jamestrew@users.noreply.github.com> Date: Tue, 29 Oct 2024 01:11:39 +0000 Subject: [PATCH 4/5] Revert "feat(lsp): use native lsp handlers for some pickers" (#3349) * Revert "feat(lsp): use native lsp handlers for some pickers (#3335)" This reverts commit 6c468ff9ecdc7fd10dd741c40a363ec9829bd92c. * [docgen] Update doc/telescope.txt skip-checks: true --------- Co-authored-by: Github Actions --- doc/telescope.txt | 7 ++ lua/telescope/builtin/__lsp.lua | 113 ++------------------------------ 2 files changed, 11 insertions(+), 109 deletions(-) diff --git a/doc/telescope.txt b/doc/telescope.txt index 3aad0466ab..bb43329500 100644 --- a/doc/telescope.txt +++ b/doc/telescope.txt @@ -2602,6 +2602,13 @@ UTILS *telescope.utils* Utilities for writing telescope pickers +utils.str_byteindex() *telescope.utils.str_byteindex()* + + + Return: ~ + integer + + utils.path_expand({path}) *telescope.utils.path_expand()* Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` diff --git a/lua/telescope/builtin/__lsp.lua b/lua/telescope/builtin/__lsp.lua index 5b2363c91a..af79bcb282 100644 --- a/lua/telescope/builtin/__lsp.lua +++ b/lua/telescope/builtin/__lsp.lua @@ -261,107 +261,19 @@ local function list_or_jump(action, title, funname, params, opts) end) end ----@param item table ----@param opts table -local function jump(item, opts) - if opts.curr_filepath ~= item.filename then - local cmd - if opts.jump_type == "tab" then - cmd = "tabedit" - elseif opts.jump_type == "split" then - cmd = "new" - elseif opts.jump_type == "vsplit" then - cmd = "vnew" - elseif opts.jump_type == "tab drop" then - cmd = "tab drop" - end - - if cmd then - vim.cmd(string.format("%s %s", cmd, item.filename)) - end - end - - local b = item.bufnr or vim.fn.bufadd(item.filename) - vim.bo[b].buflisted = true - local w = opts.reuse_win and vim.fn.win_findbuf(b)[1] or opts.winnr - vim.api.nvim_win_set_buf(w, b) - vim.api.nvim_win_set_cursor(w, { item.lnum, item.col - 1 }) - vim._with({ win = w }, function() - -- Open folds under the cursor - vim.cmd "normal! zv" - end) -end - -local function on_list_pick_or_jump(opts) - opts.reuse_win = vim.F.if_nil(opts.reuse_win, false) - opts.curr_filepath = vim.api.nvim_buf_get_name(opts.bufnr) - - ---@param res vim.lsp.LocationOpts.OnList - return function(res) - if opts.action_handler then - res.items = opts.action_handler(res.items, opts) - end - - if #res.items == 1 and opts.jump_type ~= "never" then - jump(res.items[1], opts) - return - end - - pickers - .new(opts, { - finder = finders.new_table { - results = res.items, - entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), - }, - previewer = conf.qflist_previewer(opts), - sorter = conf.generic_sorter(opts), - push_cursor_on_edit = true, - push_tagstack_on_edit = true, - }) - :find() - end -end - -local function references(opts) - opts.prompt_title = vim.F.if_nil(opts.prompt_title, "LSP References") - - if not opts.include_current_line then - opts.action_handler = function(items, ctx) - local lnum = vim.api.nvim_win_get_cursor(ctx.winnr)[1] - items = vim.tbl_filter(function(v) - return not (v.filename == ctx.curr_filepath and v.lnum == lnum) - end, items) - return items - end - end - - local ctx = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) } - vim.lsp.buf.references(ctx, { on_list = on_list_pick_or_jump(opts) }) -end - -local function references_legacy(opts) +lsp.references = function(opts) opts.include_current_line = vim.F.if_nil(opts.include_current_line, false) local params = vim.lsp.util.make_position_params(opts.winnr) params.context = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) } return list_or_jump("textDocument/references", "LSP References", "builtin.lsp_references", params, opts) end -local function definitions(opts) - opts.prompt_title = vim.F.if_nil(opts.prompt_title, "LSP Definitions") - vim.lsp.buf.definition { on_list = on_list_pick_or_jump(opts) } -end - -local function definitions_legacy(opts) +lsp.definitions = function(opts) local params = vim.lsp.util.make_position_params(opts.winnr) return list_or_jump("textDocument/definition", "LSP Definitions", "builtin.lsp_definitions", params, opts) end -local function type_definitions(opts) - opts.prompt_title = vim.F.if_nil(opts.prompt_title, "LSP Type Definitions") - vim.lsp.buf.type_definition { on_list = on_list_pick_or_jump(opts) } -end - -local function type_definitions_legacy(opts) +lsp.type_definitions = function(opts) local params = vim.lsp.util.make_position_params(opts.winnr) return list_or_jump( "textDocument/typeDefinition", @@ -372,28 +284,11 @@ local function type_definitions_legacy(opts) ) end -local function implementations(opts) - opts.prompt_title = vim.F.if_nil(opts.prompt_title, "LSP Implementations") - vim.lsp.buf.implementation { on_list = on_list_pick_or_jump(opts) } -end - -local function implementations_legacy(opts) +lsp.implementations = function(opts) local params = vim.lsp.util.make_position_params(opts.winnr) return list_or_jump("textDocument/implementation", "LSP Implementations", "builtin.lsp_implementations", params, opts) end -if vim.fn.has "0.11" then - lsp.references = references - lsp.definitions = definitions - lsp.type_definitions = type_definitions - lsp.implementations = implementations -else - lsp.references = references_legacy - lsp.definitions = definitions_legacy - lsp.type_definitions = type_definitions_legacy - lsp.implementations = implementations_legacy -end - local symbols_sorter = function(symbols) if vim.tbl_isempty(symbols) then return symbols From 85922dde3767e01d42a08e750a773effbffaea3e Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Tue, 29 Oct 2024 01:22:28 +0000 Subject: [PATCH 5/5] refactor: adapt to deprecated jump_to_location (#3344) --- lua/telescope/builtin/__files.lua | 2 +- lua/telescope/builtin/__lsp.lua | 19 ++++++++++--------- lua/telescope/utils.lua | 6 +++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lua/telescope/builtin/__files.lua b/lua/telescope/builtin/__files.lua index 97ee6a3def..27b8d1c927 100644 --- a/lua/telescope/builtin/__files.lua +++ b/lua/telescope/builtin/__files.lua @@ -442,7 +442,7 @@ files.treesitter = function(opts) end results = utils.filter_symbols(results, opts) - if results == nil then + if vim.tbl_isempty(results) then -- error message already printed in `utils.filter_symbols` return end diff --git a/lua/telescope/builtin/__lsp.lua b/lua/telescope/builtin/__lsp.lua index af79bcb282..4f37c58458 100644 --- a/lua/telescope/builtin/__lsp.lua +++ b/lua/telescope/builtin/__lsp.lua @@ -100,7 +100,7 @@ end --- convert `item` type back to something we can pass to `vim.lsp.util.jump_to_location` --- stopgap for pre-nvim 0.10 - after which we can simply use the `user_data` --- field on the items in `vim.lsp.util.locations_to_items` ----@param item vim.lsp.util.locations_to_items.ret +---@param item vim.quickfix.entry ---@param offset_encoding string|nil utf-8|utf-16|utf-32 ---@return lsp.Location local function item_to_location(item, offset_encoding) @@ -134,9 +134,9 @@ end ---| "textDocument/implementation" ---@param action telescope.lsp.list_or_jump_action ----@param items vim.lsp.util.locations_to_items.ret[] +---@param items vim.quickfix.entry[] ---@param opts table ----@return vim.lsp.util.locations_to_items.ret[] +---@return vim.quickfix.entry[] local apply_action_handler = function(action, items, opts) if action == "textDocument/references" and not opts.include_current_line then local lnum = vim.api.nvim_win_get_cursor(opts.winnr)[1] @@ -148,9 +148,9 @@ local apply_action_handler = function(action, items, opts) return items end ----@param items vim.lsp.util.locations_to_items.ret[] +---@param items vim.quickfix.entry[] ---@param opts table ----@return vim.lsp.util.locations_to_items.ret[] +---@return vim.quickfix.entry[] local function filter_file_ignore_patters(items, opts) local file_ignore_patterns = vim.F.if_nil(opts.file_ignore_patterns, conf.file_ignore_patterns) file_ignore_patterns = file_ignore_patterns or {} @@ -242,7 +242,7 @@ local function list_or_jump(action, title, funname, params, opts) end local location = item_to_location(item, first_encoding) - vim.lsp.util.jump_to_location(location, first_encoding, opts.reuse_win) + vim.lsp.util.show_document(location, first_encoding, { reuse_win = opts.reuse_win }) else pickers .new(opts, { @@ -263,6 +263,7 @@ end lsp.references = function(opts) opts.include_current_line = vim.F.if_nil(opts.include_current_line, false) + ---@class lsp.TextDocumentPositionParams local params = vim.lsp.util.make_position_params(opts.winnr) params.context = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) } return list_or_jump("textDocument/references", "LSP References", "builtin.lsp_references", params, opts) @@ -339,7 +340,7 @@ lsp.document_symbols = function(opts) local locations = vim.lsp.util.symbols_to_items(result or {}, opts.bufnr) or {} locations = utils.filter_symbols(locations, opts, symbols_sorter) - if locations == nil then + if vim.tbl_isempty(locations) then -- error message already printed in `utils.filter_symbols` return end @@ -382,7 +383,7 @@ lsp.workspace_symbols = function(opts) local locations = vim.lsp.util.symbols_to_items(server_result or {}, opts.bufnr) or {} locations = utils.filter_symbols(locations, opts, symbols_sorter) - if locations == nil then + if vim.tbl_isempty(locations) then -- error message already printed in `utils.filter_symbols` return end @@ -424,7 +425,7 @@ local function get_workspace_symbols_requester(bufnr, opts) cancel = vim.lsp.buf_request_all(bufnr, "workspace/symbol", { query = prompt }, tx) local results = rx() ---@type table - local locations = {} ---@type vim.lsp.util.locations_to_items.ret[] + local locations = {} ---@type vim.quickfix.entry[] for _, client_res in pairs(results) do if client_res.error then diff --git a/lua/telescope/utils.lua b/lua/telescope/utils.lua index d20dbbe514..4953e9dc8c 100644 --- a/lua/telescope/utils.lua +++ b/lua/telescope/utils.lua @@ -121,7 +121,7 @@ utils.filter_symbols = function(results, opts, post_filter) msg = "Either opts.symbols or opts.ignore_symbols, can't process opposing options at the same time!", level = "ERROR", }) - return + return {} elseif not (has_ignore or has_symbols) then return results elseif has_ignore then @@ -133,7 +133,7 @@ utils.filter_symbols = function(results, opts, post_filter) msg = "Please pass ignore_symbols as either a string or a list of strings", level = "ERROR", }) - return + return {} end opts.ignore_symbols = vim.tbl_map(string.lower, opts.ignore_symbols) @@ -149,7 +149,7 @@ utils.filter_symbols = function(results, opts, post_filter) msg = "Please pass filtering symbols as either a string or a list of strings", level = "ERROR", }) - return + return {} end opts.symbols = vim.tbl_map(string.lower, opts.symbols)