From 58968a111d3a98abc2ea8838b9fe3b4f93215dda Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Sun, 7 Sep 2025 13:05:49 +0530 Subject: [PATCH] feat: `nvim-mini/mini.pick` support Signed-off-by: Amit Singh --- README.md | 2 + lua/leetcode/picker/init.lua | 13 +++++- lua/leetcode/picker/language/mini.lua | 55 ++++++++++++++++++++++++ lua/leetcode/picker/mini_pick_utils.lua | 52 +++++++++++++++++++++++ lua/leetcode/picker/question/mini.lua | 56 +++++++++++++++++++++++++ lua/leetcode/picker/tabs/mini.lua | 56 +++++++++++++++++++++++++ 6 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 lua/leetcode/picker/language/mini.lua create mode 100644 lua/leetcode/picker/mini_pick_utils.lua create mode 100644 lua/leetcode/picker/question/mini.lua create mode 100644 lua/leetcode/picker/tabs/mini.lua diff --git a/README.md b/README.md index 4119806..efd575c 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,7 @@ Supported picker providers are: - [`snacks-picker`][snacks.nvim] - [`fzf-lua`][fzf-lua] - [`telescope`][telescope.nvim] +- [`mini-picker`][mini-picker] If `provider` is `nil`, [leetcode.nvim] will try to resolve the first available one in the order above. @@ -545,3 +546,4 @@ You can then exit [leetcode.nvim] using `:Leet exit` command [snacks.nvim]: https://github.com/folke/snacks.nvim [tree-sitter-html]: https://github.com/tree-sitter/tree-sitter-html [plenary.nvim]: https://github.com/nvim-lua/plenary.nvim +[mini-picker]: https://github.com/nvim-mini/mini.pick diff --git a/lua/leetcode/picker/init.lua b/lua/leetcode/picker/init.lua index 65547ba..9af5ec9 100644 --- a/lua/leetcode/picker/init.lua +++ b/lua/leetcode/picker/init.lua @@ -1,7 +1,7 @@ local log = require("leetcode.logger") local config = require("leetcode.config") -local provider_order = { "snacks-picker", "fzf-lua", "telescope" } +local provider_order = { "snacks-picker", "fzf-lua", "telescope", "mini-picker" } local providers = { ["fzf-lua"] = { name = "fzf", @@ -23,6 +23,15 @@ local providers = { return pcall(require, "telescope") end, }, + ["mini-picker"] = { + name = "mini", + is_available = function() + return pcall(function() + -- If MiniPick is set up correctly, :Pick command should be available + return assert(vim.api.nvim_get_commands({})["Pick"]) + end) + end, + }, } local available_pickers = table.concat( @@ -32,7 +41,7 @@ local available_pickers = table.concat( ", " ) ----@return "fzf" | "telescope" | "snacks" +---@return "fzf" | "telescope" | "snacks" | "mini" local function resolve_provider() ---@type string local provider = config.user.picker.provider diff --git a/lua/leetcode/picker/language/mini.lua b/lua/leetcode/picker/language/mini.lua new file mode 100644 index 0000000..0a3d9f0 --- /dev/null +++ b/lua/leetcode/picker/language/mini.lua @@ -0,0 +1,55 @@ +local log = require("leetcode.logger") +local t = require("leetcode.translator") +local picker = require("mini.pick") + +local langage_picker = require("leetcode.picker.language") + +---@param question lc.ui.Question +return function(question, cb) + local language_items = langage_picker.items(question.q.code_snippets) + local ns_id = vim.api.nvim_create_namespace("MiniPick LeetCode Language Picker") + local finder_items = {} + local completed = false + + for _, item in ipairs(language_items) do + local text = langage_picker.ordinal(item.value) + table.insert(finder_items, { + entry = item.entry, + text = text, + item = item, + }) + end + + local res = picker.start({ + source = { + items = finder_items, + name = t("Available Languages"), + choose = function(item) + if completed then + return + end + completed = true + vim.schedule(function() + langage_picker.select(item.item.value.t, question, cb) + end) + end, + show = function(buf_id, items) + require("leetcode.picker.mini_pick_utils").show_items(buf_id, ns_id, items) + end, + }, + window = { + config = { + width = langage_picker.width, + height = langage_picker.height, + }, + }, + }) + + if res == nil then + if completed then + return + end + completed = true + log.warn("No selection") + end +end diff --git a/lua/leetcode/picker/mini_pick_utils.lua b/lua/leetcode/picker/mini_pick_utils.lua new file mode 100644 index 0000000..f806bda --- /dev/null +++ b/lua/leetcode/picker/mini_pick_utils.lua @@ -0,0 +1,52 @@ +local M = {} + +---@class MiniPickItem +---@field entry (string|string[])[] + +---Render items to be shown in the `mini.pick` picker menu +---@param buf_id integer Buffer associated with the `mini.pick` picker +---@param ns_id integer Namespace associated with the `mini.pick` picker +---@param items MiniPickItem[] Items to be rendered into the picker menu +M.show_items = function(buf_id, ns_id, items) + local lines, highlights = {}, {} + + for _, item in ipairs(items) do + local line, col = {}, 0 + local hl_spans = {} + + for _, part in ipairs(item.entry) do + if type(part) == "string" then + part = { part } + end + local text, hl = part[1], part[2] + table.insert(line, text) + + local start_col, end_col = col, col + #text + if hl then + table.insert(hl_spans, { hl = hl, start_col = start_col, end_col = end_col }) + end + + col = end_col + 1 -- account for space + table.insert(line, " ") + end + + table.insert(lines, table.concat(line)) + table.insert(highlights, hl_spans) + end + + -- Set lines + vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines) + vim.api.nvim_buf_clear_namespace(buf_id, ns_id, 0, -1) + + -- Apply highlights on each line chunk + local opts = { hl_mode = "combine", priority = 200 } + for row, hl_spans in ipairs(highlights) do + for _, span in ipairs(hl_spans) do + opts.hl_group = span.hl + opts.end_row, opts.end_col = row - 1, span.end_col + vim.api.nvim_buf_set_extmark(buf_id, ns_id, row - 1, span.start_col, opts) + end + end +end + +return M diff --git a/lua/leetcode/picker/question/mini.lua b/lua/leetcode/picker/question/mini.lua new file mode 100644 index 0000000..10171dd --- /dev/null +++ b/lua/leetcode/picker/question/mini.lua @@ -0,0 +1,56 @@ +local log = require("leetcode.logger") +local t = require("leetcode.translator") +local question_picker = require("leetcode.picker.question") + +local picker = require("mini.pick") + +---@param questions lc.cache.Question[] +return function(questions, opts) + local question_items = question_picker.items(questions, opts) + local ns_id = vim.api.nvim_create_namespace("MiniPick LeetCode Questions Picker") + local finder_items = {} + local completed = false + + for _, item in ipairs(question_items) do + local text = question_picker.ordinal(item.value) + table.insert(finder_items, { + entry = item.entry, + text = text, + item = item, + }) + end + + local res = picker.start({ + source = { + items = finder_items, + name = t("Select a Question"), + + choose = function(item) + if completed then + return + end + completed = true + vim.schedule(function() + question_picker.select(item.item.value) + end) + end, + show = function(buf_id, items) + require("leetcode.picker.mini_pick_utils").show_items(buf_id, ns_id, items) + end, + }, + window = { + config = { + width = question_picker.width, + height = math.floor(vim.o.lines * question_picker.height), + }, + }, + }) + + if res == nil then + if completed then + return + end + completed = true + log.warn("No selection") + end +end diff --git a/lua/leetcode/picker/tabs/mini.lua b/lua/leetcode/picker/tabs/mini.lua new file mode 100644 index 0000000..4a31f44 --- /dev/null +++ b/lua/leetcode/picker/tabs/mini.lua @@ -0,0 +1,56 @@ +local log = require("leetcode.logger") +local t = require("leetcode.translator") +local tabs_picker = require("leetcode.picker.tabs") + +local picker = require("mini.pick") + +return function(tabs) + local tab_items = tabs_picker.items(tabs) + local ns_id = vim.api.nvim_create_namespace("MiniPick LeetCode Tabs Picker") + local finder_items = {} + local completed = false + local items_reflect = {} + + for _, item in ipairs(tab_items) do + items_reflect[item.value.question.q.frontend_id] = item.value + local text = tabs_picker.ordinal(item.value.question.q) + table.insert(finder_items, { + entry = item.entry, + item = item.value.question.q.frontend_id, + text = text, + }) + end + + local res = picker.start({ + source = { + items = finder_items, + name = t("Select a Question"), + choose = function(item) + if completed then + return + end + completed = true + vim.schedule(function() + tabs_picker.select(items_reflect[item.item]) + end) + end, + show = function(buf_id, items) + require("leetcode.picker.mini_pick_utils").show_items(buf_id, ns_id, items) + end, + }, + window = { + config = { + width = tabs_picker.width, + height = tabs_picker.height, + }, + }, + }) + + if res == nil then + if completed then + return + end + completed = true + log.warn("No selection") + end +end