From 78f4560b8514ac4d2d874b857598d089898e9698 Mon Sep 17 00:00:00 2001 From: Masaaki Hirotsu Date: Sat, 3 Oct 2026 09:12:16 +0900 Subject: [PATCH 1/2] fix(telescope): show the full location in picker entries The single-column entry_display layout dropped every chunk after the first, so entries showed only the symbol, or only the directory when there was no symbol. Entries now return the joined text with byte-range highlights directly, which is the contract Telescope resolves for entry.display, and the spec no longer relies on a mock that concatenated chunks unconditionally. --- lua/peekstack/picker/telescope.lua | 28 ++++---- tests/picker_telescope_spec.lua | 102 ++++++++++++++++++++++------- 2 files changed, 94 insertions(+), 36 deletions(-) diff --git a/lua/peekstack/picker/telescope.lua b/lua/peekstack/picker/telescope.lua index 66b47db..d1fa699 100644 --- a/lua/peekstack/picker/telescope.lua +++ b/lua/peekstack/picker/telescope.lua @@ -13,10 +13,11 @@ local function hl(primary, fallback) return fallback end ----@param displayer fun(chunks: table): string +---Build the display string and its byte-range highlights in the shape Telescope +---expects from `entry.display`: `{ { { start, finish }, hl_group }, ... }`. ---@param item PeekstackPickerExternalItem ----@return string -local function display_entry(displayer, item) +---@return string, table +local function display_entry(item) local chunks = {} if type(item.symbol) == "string" and item.symbol ~= "" then chunks[#chunks + 1] = { item.symbol, hl("TelescopeResultsIdentifier", "Function") } @@ -41,7 +42,16 @@ local function display_entry(displayer, item) chunks[#chunks + 1] = { tostring(item.display_col), hl("TelescopeResultsNumber", "Number") } end - return displayer(chunks) + local texts = {} + local highlights = {} + local offset = 0 + for _, chunk in ipairs(chunks) do + local text = chunk[1] + texts[#texts + 1] = text + highlights[#highlights + 1] = { { offset, offset + #text }, chunk[2] } + offset = offset + #text + end + return table.concat(texts), highlights end ---Pick a location using Telescope @@ -55,23 +65,15 @@ function M.pick(locations, opts, cb) return end local finders = require("telescope.finders") - local entry_display = require("telescope.pickers.entry_display") local conf = require("telescope.config").values local telescope_opts = opts or {} - local displayer = entry_display.create({ - separator = "", - items = { - { remaining = true }, - }, - }) - local items = picker_util.build_external_items(locations, 1) local entries = {} for _, item in ipairs(items) do table.insert(entries, { value = item.value, display = function() - return display_entry(displayer, item) + return display_entry(item) end, ordinal = string.format("%s %s", item.label, item.file or ""), filename = item.file, diff --git a/tests/picker_telescope_spec.lua b/tests/picker_telescope_spec.lua index 7351e8d..56145d4 100644 --- a/tests/picker_telescope_spec.lua +++ b/tests/picker_telescope_spec.lua @@ -16,7 +16,6 @@ describe("peekstack.picker.telescope", function() before_each(function() save_module("telescope.pickers") save_module("telescope.finders") - save_module("telescope.pickers.entry_display") save_module("telescope.config") save_module("telescope.actions") save_module("telescope.actions.state") @@ -47,18 +46,6 @@ describe("peekstack.picker.telescope", function() return opts end, } - package.loaded["telescope.pickers.entry_display"] = { - create = function(_opts) - return function(chunks) - captured.display_chunks = chunks - local texts = {} - for _, chunk in ipairs(chunks) do - texts[#texts + 1] = chunk[1] - end - return table.concat(texts) - end - end, - } package.loaded["telescope.actions"] = { close = function(bufnr) captured.closed_bufnr = bufnr @@ -104,17 +91,18 @@ describe("peekstack.picker.telescope", function() assert.equals("sorter", captured.spec.sorter) assert.equals("previewer", captured.spec.previewer) - assert.equals("Alpha - /tmp/a.lua:2:3", captured.finder.results[1].display()) + local display, highlights = captured.finder.results[1].display() + assert.equals("Alpha - /tmp/a.lua:2:3", display) assert.same({ - { "Alpha", "Function" }, - { " - ", "Comment" }, - { "/tmp/", "Comment" }, - { "a.lua", "Directory" }, - { ":", "Comment" }, - { "2", "Number" }, - { ":", "Comment" }, - { "3", "Number" }, - }, captured.display_chunks) + { { 0, 5 }, "Function" }, + { { 5, 8 }, "Comment" }, + { { 8, 13 }, "Comment" }, + { { 13, 18 }, "Directory" }, + { { 18, 19 }, "Comment" }, + { { 19, 20 }, "Number" }, + { { 20, 21 }, "Comment" }, + { { 21, 22 }, "Number" }, + }, highlights) assert.equals("Alpha - /tmp/a.lua:2:3 /tmp/a.lua", captured.finder.results[1].ordinal) assert.equals("/tmp/a.lua", captured.finder.results[1].filename) assert.equals(2, captured.finder.results[1].lnum) @@ -124,4 +112,72 @@ describe("peekstack.picker.telescope", function() assert.equals(13, captured.closed_bufnr) assert.are.same(loc2, picked) end) + ---@param display string + ---@param highlights table + ---@return string[] + local function highlighted_texts(display, highlights) + local texts = {} + for _, block in ipairs(highlights) do + texts[#texts + 1] = display:sub(block[1][1] + 1, block[1][2]) + end + return texts + end + + ---@param locations PeekstackLocation[] + ---@return table[] + local function collect_entries(locations) + local results = nil + package.loaded["telescope.config"] = { + values = { + generic_sorter = function() end, + grep_previewer = function() end, + }, + } + package.loaded["telescope.finders"] = { + new_table = function(opts) + results = opts.results + return opts + end, + } + package.loaded["telescope.pickers"] = { + new = function() + return { find = function() end } + end, + } + picker.pick(locations, nil, function() end) + return results + end + + it("shows the file name, line and column without a symbol", function() + local entries = collect_entries({ + { + uri = "file:///tmp/dir/b.lua", + range = { start = { line = 4, character = 0 }, ["end"] = { line = 4, character = 0 } }, + provider = "test", + }, + }) + + local display, highlights = entries[1].display() + assert.equals("/tmp/dir/b.lua:5:1", display) + assert.same({ "/tmp/dir/", "b.lua", ":", "5", ":", "1" }, highlighted_texts(display, highlights)) + end) + + it("uses byte offsets for multibyte symbols and paths", function() + local entries = collect_entries({ + { + uri = "file:///tmp/日本/語.lua", + range = { start = { line = 0, character = 3 }, ["end"] = { line = 0, character = 3 } }, + text = "関数", + provider = "test", + }, + }) + + local display, highlights = entries[1].display() + assert.equals("関数 - /tmp/日本/語.lua:1:4", display) + assert.same( + { "関数", " - ", "/tmp/日本/", "語.lua", ":", "1", ":", "4" }, + highlighted_texts(display, highlights) + ) + assert.equals(#display, highlights[#highlights][1][2]) + end) end) From e7602bc27dda4343a078a526ef890f5e4021fad2 Mon Sep 17 00:00:00 2001 From: Masaaki Hirotsu Date: Sat, 3 Oct 2026 09:12:42 +0900 Subject: [PATCH 2/2] fix(telescope): confirm picker selections from normal mode Only the insert-mode was routed to peekstack, so confirming after switching to normal mode fell through to Telescope's default edit and opened the file instead of a popup. Both modes now share the same selection callback. --- lua/peekstack/picker/telescope.lua | 6 ++++-- tests/picker_telescope_spec.lua | 16 +++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lua/peekstack/picker/telescope.lua b/lua/peekstack/picker/telescope.lua index d1fa699..ea1891d 100644 --- a/lua/peekstack/picker/telescope.lua +++ b/lua/peekstack/picker/telescope.lua @@ -94,13 +94,15 @@ function M.pick(locations, opts, cb) sorter = conf.generic_sorter(telescope_opts), previewer = conf.grep_previewer(telescope_opts), attach_mappings = function(_, map) - map("i", "", function(bufnr) + local function on_select(bufnr) local selection = require("telescope.actions.state").get_selected_entry() require("telescope.actions").close(bufnr) if selection and selection.value then cb(selection.value) end - end) + end + map("i", "", on_select) + map("n", "", on_select) return true end, }) diff --git a/tests/picker_telescope_spec.lua b/tests/picker_telescope_spec.lua index 56145d4..a90c650 100644 --- a/tests/picker_telescope_spec.lua +++ b/tests/picker_telescope_spec.lua @@ -28,7 +28,6 @@ describe("peekstack.picker.telescope", function() it("sets preview metadata and returns selected location", function() local captured = {} local picked = nil - local mapped_confirm = nil package.loaded["telescope.config"] = { values = { @@ -61,13 +60,12 @@ describe("peekstack.picker.telescope", function() captured.spec = spec return { find = function() + captured.mappings = {} spec.attach_mappings(nil, function(mode, key, fn) - captured.mode = mode - captured.key = key - mapped_confirm = fn + captured.mappings[mode .. key] = fn end) captured.selected = captured.finder.results[2] - mapped_confirm(13) + captured.mappings["i"](13) end, } end, @@ -107,10 +105,14 @@ describe("peekstack.picker.telescope", function() assert.equals("/tmp/a.lua", captured.finder.results[1].filename) assert.equals(2, captured.finder.results[1].lnum) assert.equals(3, captured.finder.results[1].col) - assert.equals("i", captured.mode) - assert.equals("", captured.key) assert.equals(13, captured.closed_bufnr) assert.are.same(loc2, picked) + + picked = nil + captured.selected = captured.finder.results[1] + captured.mappings["n"](14) + assert.equals(14, captured.closed_bufnr) + assert.are.same(loc1, picked) end) ---@param display string ---@param highlights table