Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 126 additions & 89 deletions lua/cheatsheet/telescope/init.lua
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
local actions = require('telescope.actions')
local actions_state = require('telescope.actions.state')
local finders = require('telescope.finders')
local pickers = require('telescope.pickers')
local actions = require("telescope.actions")
local actions_state = require("telescope.actions.state")
local finders = require("telescope.finders")
local pickers = require("telescope.pickers")

local config = require('telescope.config').values
local entry_display = require('telescope.pickers.entry_display')
local config = require("telescope.config").values
local entry_display = require("telescope.pickers.entry_display")

local cheatsheet = require('cheatsheet')
local utils = require('cheatsheet.utils')
local cheatsheet = require("cheatsheet")
local utils = require("cheatsheet.utils")

local M = {}

Expand All @@ -18,87 +18,124 @@ local M = {}
-- <C-E> - Edit user cheatsheet in new buffer
-- <C-Y> - Yank the cheatcode
M.pick_cheat = function(telescope_opts, opts)
telescope_opts = telescope_opts or {}

pickers.new(
telescope_opts, {
prompt_title = 'Cheat',
finder = finders.new_table {
results = cheatsheet.get_cheats(opts),
entry_maker = function(entry)
-- Calculate the width of each column dynamically so that both
-- the description and cheatcode is readable on small terminals too.
-- This whole logic can be avoided if the cheatcode is shown first and
-- a small width for the respective cheatcode column is used.
-- But the cheatcode is what we *don't* know and the description is
-- what we already know. So show description first for better UX.

-- * config.width was deprecated in favor of config.layout_config.width
-- https://github.com/nvim-telescope/telescope.nvim/commit/5a53ec5c2fdab10ca8775d3979b1a85e63d57953
-- * config.layout_config was changed to move width and height to individual layout_strategy configs
-- https://github.com/nvim-telescope/telescope.nvim/pull/1039/files#diff-4936325bfc521d7cffc09fe0156becd4a4ba2ed169431c94bd63669fe0cc1a2aL79-L80
local cols = vim.o.columns
local width = config.width
or config.layout_config.width
or config.layout_config[config.layout_strategy].width
or cols
local tel_win_width
-- width = 80 -> column width, width = 0.7 -> ratio
if width > 1 then
tel_win_width = width
else
tel_win_width = math.floor(cols * width)
end
local cheatcode_width = math.floor(cols * 0.25)
local section_width = 10

-- NOTE: the width calculating logic is not exact, but approx enough
local displayer = entry_display.create {
separator = " ▏",
items = {
{ width = section_width }, -- section
{
width = tel_win_width - cheatcode_width
- section_width,
}, -- description
{ remaining = true }, -- cheatcode
},
}

local function make_display(ent)
return displayer {
-- text, highlight group
{ ent.value.section, "cheatMetadataSection" },
{ ent.value.description, "cheatDescription" },
{ ent.value.cheatcode, "cheatCode" },
}
end

local tags = table.concat(entry.tags, ' ')

return {
value = entry,
-- generate the string that user sees as an item
display = make_display,
-- queries are matched against ordinal
ordinal = string.format(
'%s %s %s %s', entry.section, entry.description,
tags, entry.cheatcode
),
}
end,
},
attach_mappings = function(prompt_bufnr, map)
local mappings = require('cheatsheet.config').options.telescope_mappings
for keybind, action in pairs(mappings) do
map('i', keybind, function() action(prompt_bufnr) end)
end

return true
end,
sorter = config.generic_sorter(telescope_opts),
}
):find()
telescope_opts = telescope_opts or {}

pickers
.new(telescope_opts, {
prompt_title = "Cheat",
finder = finders.new_table({
results = cheatsheet.get_cheats(opts),
entry_maker = function(entry)
-- Calculate the width of each column dynamically so that both
-- the description and cheatcode is readable on small terminals too.
-- This whole logic can be avoided if the cheatcode is shown first and
-- a small width for the respective cheatcode column is used.
-- But the cheatcode is what we *don't* know and the description is
-- what we already know. So show description first for better UX.

-- * config.width was deprecated in favor of config.layout_config.width
-- https://github.com/nvim-telescope/telescope.nvim/commit/5a53ec5c2fdab10ca8775d3979b1a85e63d57953
-- * config.layout_config was changed to move width and height to individual layout_strategy configs
-- https://github.com/nvim-telescope/telescope.nvim/pull/1039/files#diff-4936325bfc521d7cffc09fe0156becd4a4ba2ed169431c94bd63669fe0cc1a2aL79-L80
--
-- PR NOTE (Issue 1 - nil index crash):
-- On newer Telescope versions, `config.layout_config[config.layout_strategy]`
-- may be nil unless the user explicitly configures that strategy. Indexing `.width`
-- on nil raised "attempt to index a nil value" here. We now resolve width using a
-- defensive precedence order and only index per-strategy tables when they exist.

-- columns in the current Neovim UI
local cols = vim.o.columns

-- Resolve layout strategy (opts → global → default). Keep current behavior but add fallback.
local strategy = telescope_opts.layout_strategy or config.layout_strategy or "horizontal"

-- Pull per-strategy layout tables safely (opts first, then global).
local per_strategy = (telescope_opts.layout_config and telescope_opts.layout_config[strategy])
or (config.layout_config and config.layout_config[strategy])

-- Accept width from (newest → oldest) with sane default.
-- We keep compatibility with deprecated fields (`config.width`, `layout_config.width`)
-- and also honor per-strategy `width` when present.
local width = telescope_opts.width
or (telescope_opts.layout_config and telescope_opts.layout_config.width)
or config.width
or (config.layout_config and config.layout_config.width)
or (per_strategy and per_strategy.width)
or 0.8 -- default ratio if nothing set

local tel_win_width
-- width = 80 -> absolute columns, width = 0.7 -> ratio of UI columns
if type(width) == "number" and width > 1 then
-- PR NOTE (Issue 2 - oversize windows on small terminals):
-- Clamp absolute widths to available columns to avoid overflow on narrow terminals.
tel_win_width = math.min(math.floor(width), cols)
else
tel_win_width = math.floor(cols * width)
end

-- PR NOTE (Issue 3 - sizing by vim.o.columns instead of actual picker width):
-- Previously column widths were derived from `vim.o.columns` while the picker might be narrower.
-- We now size columns from `tel_win_width` (the picker width) for more accurate layout.
local cheatcode_width = math.floor(tel_win_width * 0.25)
local section_width = 10

-- NOTE: the width calculating logic is not exact, but approx enough
local displayer = entry_display.create({
separator = " ▏",
items = {
{ width = section_width }, -- section
{
width = tel_win_width - cheatcode_width - section_width,
}, -- description
{ remaining = true }, -- cheatcode
},
})

local function make_display(ent)
-- PR NOTE (Issue 4 - defensive rendering):
-- Guard against missing fields so a bad entry never crashes rendering.
local e = ent.value or {}
return displayer({
-- text, highlight group
{ e.section or "", "cheatMetadataSection" },
{ e.description or "", "cheatDescription" },
{ e.cheatcode or "", "cheatCode" },
})
end

-- PR NOTE (Issue 5 - entry.tags may be nil):
-- Some sources may omit tags. Guard before concatenation.
local tags = (entry.tags and #entry.tags > 0) and table.concat(entry.tags, " ") or ""

return {
value = entry,
-- generate the string that user sees as an item
display = make_display,
-- queries are matched against ordinal
ordinal = string.format(
"%s %s %s %s",
entry.section or "",
entry.description or "",
tags,
entry.cheatcode or ""
),
}
end,
}),
attach_mappings = function(prompt_bufnr, map)
local mappings = require("cheatsheet.config").options.telescope_mappings
for keybind, action in pairs(mappings) do
map("i", keybind, function()
action(prompt_bufnr)
end)
end

return true
end,
sorter = config.generic_sorter(telescope_opts),
})
:find()
end

return M