A Neovim plugin enabling you to solve LeetCode problems.
feat: `ibhagwan/fzf-lua` support (#150)
* feat: `ibhagwan/fzf-lua` support * feat(picker): close buffer * fix: telescope lang picker * feat: adjust picker sizes * docs: `picker.provider` config * feat: picker provider resolver
17 files changed, +669 -425
+22-7README.md
| @@ -26,7 +26,9 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/aee6584c-e099-4409-b114-1 | ||
| 26 | 26 | |
| 27 | 27 | - [Neovim] >= 0.9.0 |
| 28 | 28 | |
| 29 | -- [telescope.nvim] | |
| 29 | +- [telescope.nvim] or [fzf-lua] | |
| 30 | + | |
| 31 | +- [plenary.nvim] | |
| 30 | 32 | |
| 31 | 33 | - [nui.nvim] |
| 32 | 34 | |
| @@ -43,15 +45,12 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/aee6584c-e099-4409-b114-1 | ||
| 43 | 45 | ```lua |
| 44 | 46 | { |
| 45 | 47 | "kawre/leetcode.nvim", |
| 46 | - build = ":TSUpdate html", | |
| 48 | + build = ":TSUpdate html", -- if you have `nvim-treesitter` installed | |
| 47 | 49 | dependencies = { |
| 48 | 50 | "nvim-telescope/telescope.nvim", |
| 49 | - "nvim-lua/plenary.nvim", -- required by telescope | |
| 51 | + -- "ibhagwan/fzf-lua", | |
| 52 | + "nvim-lua/plenary.nvim", | |
| 50 | 53 | "MunifTanjim/nui.nvim", |
| 51 | - | |
| 52 | - -- optional | |
| 53 | - "nvim-treesitter/nvim-treesitter", | |
| 54 | - "nvim-tree/nvim-web-devicons", | |
| 55 | 54 | }, |
| 56 | 55 | opts = { |
| 57 | 56 | -- configuration goes here |
| @@ -128,6 +127,9 @@ To see full configuration types see [template.lua](./lua/leetcode/config/templat | ||
| 128 | 127 | show_stats = true, ---@type boolean |
| 129 | 128 | }, |
| 130 | 129 | |
| 130 | + ---@type lc.picker | |
| 131 | + picker = { provider = nil }, | |
| 132 | + | |
| 131 | 133 | hooks = { |
| 132 | 134 | ---@type fun()[] |
| 133 | 135 | ["enter"] = {}, |
| @@ -275,6 +277,17 @@ injector = { ---@type table<lc.lang, lc.inject> | ||
| 275 | 277 | } |
| 276 | 278 | ``` |
| 277 | 279 | |
| 280 | +### picker | |
| 281 | + | |
| 282 | +Supported picker providers are `telescope` and `fzf-lua`. | |
| 283 | +When provider is `nil`, [leetcode.nvim] will first try to use `fzf-lua`, | |
| 284 | +if not found it will fallback to `telescope`. | |
| 285 | + | |
| 286 | +```lua | |
| 287 | +---@type lc.picker | |
| 288 | +picker = { provider = nil }, | |
| 289 | +``` | |
| 290 | + | |
| 278 | 291 | ### hooks |
| 279 | 292 | |
| 280 | 293 | List of functions that get executed on specified event |
| @@ -493,4 +506,6 @@ You can then exit [leetcode.nvim] using `:Leet exit` command | ||
| 493 | 506 | [nvim-treesitter]: https://github.com/nvim-treesitter/nvim-treesitter |
| 494 | 507 | [nvim-web-devicons]: https://github.com/nvim-tree/nvim-web-devicons |
| 495 | 508 | [telescope.nvim]: https://github.com/nvim-telescope/telescope.nvim |
| 509 | +[fzf-lua]: https://github.com/ibhagwan/fzf-lua | |
| 496 | 510 | [tree-sitter-html]: https://github.com/tree-sitter/tree-sitter-html |
| 511 | +[plenary.nvim]: https://github.com/nvim-lua/plenary.nvim |
+3-2lua/leetcode-ui/question.lua
| @@ -228,8 +228,9 @@ function Question:mount() | ||
| 228 | 228 | local msg = ("Snippet for `%s` not found. Select a different language"):format(self.lang) |
| 229 | 229 | log.warn(msg) |
| 230 | 230 | |
| 231 | - require("leetcode.pickers.language").pick_lang(self, function(snippet) | |
| 232 | - self.lang = snippet.t.slug | |
| 231 | + local picker = require("leetcode.picker") | |
| 232 | + picker.language(self, function(slug) | |
| 233 | + self.lang = slug | |
| 233 | 234 | self:handle_mount() |
| 234 | 235 | end) |
| 235 | 236 | end |
+6-3lua/leetcode/command/init.lua
| @@ -26,7 +26,8 @@ function cmd.problems(options) | ||
| 26 | 26 | require("leetcode.utils").auth_guard() |
| 27 | 27 | |
| 28 | 28 | local p = require("leetcode.cache.problemlist").get() |
| 29 | - require("leetcode.pickers.question").pick(p, options) | |
| 29 | + local picker = require("leetcode.picker") | |
| 30 | + picker.question(p, options) | |
| 30 | 31 | end |
| 31 | 32 | |
| 32 | 33 | ---@param cb? function |
| @@ -219,14 +220,16 @@ function cmd.start_user_session() -- | ||
| 219 | 220 | end |
| 220 | 221 | |
| 221 | 222 | function cmd.question_tabs() |
| 222 | - require("leetcode.pickers.question-tabs").pick() | |
| 223 | + local picker = require("leetcode.picker") | |
| 224 | + picker.tabs() | |
| 223 | 225 | end |
| 224 | 226 | |
| 225 | 227 | function cmd.change_lang() |
| 226 | 228 | local utils = require("leetcode.utils") |
| 227 | 229 | local q = utils.curr_question() |
| 228 | 230 | if q then |
| 229 | - require("leetcode.pickers.language").pick(q) | |
| 231 | + local picker = require("leetcode.picker") | |
| 232 | + picker.language(q) | |
| 230 | 233 | end |
| 231 | 234 | end |
| 232 | 235 |
+5-0lua/leetcode/config/template.lua
| @@ -38,6 +38,8 @@ | ||
| 38 | 38 | |
| 39 | 39 | ---@alias lc.storage table<"cache"|"home", string> |
| 40 | 40 | |
| 41 | +---@alias lc.picker { provider?: "fzf-lua" | "telescope" } | |
| 42 | + | |
| 41 | 43 | ---@class lc.UserConfig |
| 42 | 44 | local M = { |
| 43 | 45 | ---@type string |
| @@ -101,6 +103,9 @@ local M = { | ||
| 101 | 103 | show_stats = true, ---@type boolean |
| 102 | 104 | }, |
| 103 | 105 | |
| 106 | + ---@type lc.picker | |
| 107 | + picker = { provider = nil }, | |
| 108 | + | |
| 104 | 109 | hooks = { |
| 105 | 110 | ---@type fun()[] |
| 106 | 111 | ["enter"] = {}, |
+93-0lua/leetcode/picker/init.lua
| @@ -0,0 +1,93 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local config = require("leetcode.config") | |
| 3 | + | |
| 4 | +---@return "fzf" | "telescope" | |
| 5 | +local function resolve_provider() | |
| 6 | + ---@type string | |
| 7 | + local provider = config.user.picker.provider | |
| 8 | + | |
| 9 | + if provider == nil then | |
| 10 | + local fzf_ok = pcall(require, "fzf-lua") | |
| 11 | + if fzf_ok then | |
| 12 | + return "fzf" | |
| 13 | + end | |
| 14 | + local telescope_ok = pcall(require, "telescope") | |
| 15 | + if telescope_ok then | |
| 16 | + return "telescope" | |
| 17 | + end | |
| 18 | + error("no supported picker provider found") | |
| 19 | + else | |
| 20 | + local provider_ok = pcall(require, provider) | |
| 21 | + assert(provider_ok, ("specified picker provider not found: `%s`"):format(provider)) | |
| 22 | + return provider == "fzf-lua" and "fzf" or provider | |
| 23 | + end | |
| 24 | +end | |
| 25 | + | |
| 26 | +---@class leet.Picker | |
| 27 | +local P = {} | |
| 28 | +P.provider = resolve_provider() | |
| 29 | + | |
| 30 | +function P.hl_to_ansi(hl_group) | |
| 31 | + local color = vim.api.nvim_get_hl(0, { name = hl_group }) | |
| 32 | + if color and color.fg then | |
| 33 | + return string.format( | |
| 34 | + "\x1b[38;2;%d;%d;%dm", | |
| 35 | + bit.rshift(color.fg, 16), | |
| 36 | + bit.band(bit.rshift(color.fg, 8), 0xFF), | |
| 37 | + bit.band(color.fg, 0xFF) | |
| 38 | + ) | |
| 39 | + end | |
| 40 | + return "" | |
| 41 | +end | |
| 42 | + | |
| 43 | +function P.apply_hl(text, hl_group) | |
| 44 | + if not hl_group then | |
| 45 | + return text | |
| 46 | + end | |
| 47 | + return P.hl_to_ansi(hl_group) .. text .. "\x1b[0m" | |
| 48 | +end | |
| 49 | + | |
| 50 | +function P.normalize(items) | |
| 51 | + return vim.tbl_map(function(item) | |
| 52 | + return table.concat( | |
| 53 | + vim.tbl_map(function(col) | |
| 54 | + if type(col) == "table" then | |
| 55 | + return P.apply_hl(col[1], col[2]) | |
| 56 | + else | |
| 57 | + return col | |
| 58 | + end | |
| 59 | + end, item.entry), | |
| 60 | + " " | |
| 61 | + ) | |
| 62 | + end, items) | |
| 63 | +end | |
| 64 | + | |
| 65 | +function P.pick(path, ...) | |
| 66 | + local rpath = table.concat({ "leetcode.picker", path, P.provider }, ".") | |
| 67 | + return require(rpath)(...) | |
| 68 | +end | |
| 69 | + | |
| 70 | +function P.language(...) | |
| 71 | + P.pick("language", ...) | |
| 72 | +end | |
| 73 | + | |
| 74 | +function P.question(...) | |
| 75 | + P.pick("question", ...) | |
| 76 | +end | |
| 77 | + | |
| 78 | +function P.tabs() | |
| 79 | + local utils = require("leetcode.utils") | |
| 80 | + local tabs = utils.question_tabs() | |
| 81 | + | |
| 82 | + if vim.tbl_isempty(tabs) then | |
| 83 | + return log.warn("No questions opened") | |
| 84 | + end | |
| 85 | + | |
| 86 | + P.pick("tabs", tabs) | |
| 87 | +end | |
| 88 | + | |
| 89 | +function P.hidden_field(text, deli) | |
| 90 | + return text:match(("([^%s]+)$"):format(deli)) | |
| 91 | +end | |
| 92 | + | |
| 93 | +return P |
+35-0lua/leetcode/picker/language/fzf.lua
| @@ -0,0 +1,35 @@ | ||
| 1 | +local fzf = require("fzf-lua") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | +local language_picker = require("leetcode.picker.language") | |
| 4 | +local Picker = require("leetcode.picker") | |
| 5 | + | |
| 6 | +local deli = "\t" | |
| 7 | + | |
| 8 | +return function(question, cb) | |
| 9 | + local items = language_picker.items(question.q.code_snippets) | |
| 10 | + | |
| 11 | + for i, item in ipairs(items) do | |
| 12 | + local md = vim.inspect({ slug = item.value.t.slug, lang = item.value.t.lang }) | |
| 13 | + :gsub("\n", "") | |
| 14 | + items[i] = table.concat({ Picker.normalize({ item })[1], md }, deli) | |
| 15 | + end | |
| 16 | + | |
| 17 | + fzf.fzf_exec(items, { | |
| 18 | + prompt = t("Available Languages") .. "> ", | |
| 19 | + winopts = { | |
| 20 | + win_height = language_picker.height, | |
| 21 | + win_width = language_picker.width, | |
| 22 | + }, | |
| 23 | + fzf_opts = { | |
| 24 | + ["--delimiter"] = deli, | |
| 25 | + ["--nth"] = "1", | |
| 26 | + ["--with-nth"] = "1", | |
| 27 | + }, | |
| 28 | + actions = { | |
| 29 | + ["default"] = function(selected) | |
| 30 | + local md = Picker.hidden_field(selected[1], deli) | |
| 31 | + language_picker.select(load("return " .. md)(), question, cb) | |
| 32 | + end, | |
| 33 | + }, | |
| 34 | + }) | |
| 35 | +end |
+64-0lua/leetcode/picker/language/init.lua
| @@ -0,0 +1,64 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | +local config = require("leetcode.config") | |
| 4 | +local utils = require("leetcode.utils") | |
| 5 | + | |
| 6 | +local L = {} | |
| 7 | + | |
| 8 | +L.width = 80 | |
| 9 | +L.height = 15 | |
| 10 | + | |
| 11 | +---@param snippet lc.QuestionCodeSnippet | |
| 12 | +local function dislay_icon(snippet) | |
| 13 | + local hl = "leetcode_lang_" .. snippet.t.slug | |
| 14 | + vim.api.nvim_set_hl(0, hl, { fg = snippet.t.color }) | |
| 15 | + | |
| 16 | + return { snippet.t.icon, hl } | |
| 17 | +end | |
| 18 | + | |
| 19 | +---@param snippet lc.QuestionCodeSnippet | |
| 20 | +local function display_lang(snippet) | |
| 21 | + return { snippet.lang } | |
| 22 | +end | |
| 23 | + | |
| 24 | +function L.entry(item) | |
| 25 | + return { | |
| 26 | + dislay_icon(item), | |
| 27 | + display_lang(item), | |
| 28 | + } | |
| 29 | +end | |
| 30 | + | |
| 31 | +---@param item lc.QuestionCodeSnippet | |
| 32 | +function L.ordinal(item) | |
| 33 | + return ("%s %s"):format(item.t.lang, item.t.slug) | |
| 34 | +end | |
| 35 | + | |
| 36 | +function L.items(content) | |
| 37 | + return vim.tbl_map(function(item) | |
| 38 | + ---@type lc.language | |
| 39 | + item.t = utils.get_lang(item.lang_slug) | |
| 40 | + if not item.t then | |
| 41 | + return | |
| 42 | + end | |
| 43 | + return { entry = L.entry(item), value = item } | |
| 44 | + end, content) | |
| 45 | +end | |
| 46 | + | |
| 47 | +function L.select(selection, question, cb, close) | |
| 48 | + if question.lang == selection.slug then | |
| 49 | + return log.warn(("%s: %s"):format(t("Language already set to"), selection.lang)) | |
| 50 | + end | |
| 51 | + | |
| 52 | + config.lang = selection.slug | |
| 53 | + if close then | |
| 54 | + close() | |
| 55 | + end | |
| 56 | + | |
| 57 | + if cb then | |
| 58 | + cb(selection.slug) | |
| 59 | + else | |
| 60 | + question:change_lang(selection.slug) | |
| 61 | + end | |
| 62 | +end | |
| 63 | + | |
| 64 | +return L |
+67-0lua/leetcode/picker/language/telescope.lua
| @@ -0,0 +1,67 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | + | |
| 4 | +local pickers = require("telescope.pickers") | |
| 5 | +local finders = require("telescope.finders") | |
| 6 | +local conf = require("telescope.config").values | |
| 7 | +local config = require("leetcode.config") | |
| 8 | + | |
| 9 | +local entry_display = require("telescope.pickers.entry_display") | |
| 10 | +local actions = require("telescope.actions") | |
| 11 | +local action_state = require("telescope.actions.state") | |
| 12 | +local language_picker = require("leetcode.picker.language") | |
| 13 | + | |
| 14 | +local displayer = entry_display.create({ | |
| 15 | + separator = " ", | |
| 16 | + items = { | |
| 17 | + { width = 1 }, | |
| 18 | + { remaining = true }, | |
| 19 | + }, | |
| 20 | +}) | |
| 21 | + | |
| 22 | +local function entry_maker(item) | |
| 23 | + return { | |
| 24 | + value = item.value, | |
| 25 | + display = function() | |
| 26 | + return displayer(item.entry) | |
| 27 | + end, | |
| 28 | + ordinal = language_picker.ordinal(item.value), | |
| 29 | + } | |
| 30 | +end | |
| 31 | + | |
| 32 | +local opts = require("telescope.themes").get_dropdown({ | |
| 33 | + layout_config = { | |
| 34 | + width = language_picker.width, | |
| 35 | + height = language_picker.height, | |
| 36 | + }, | |
| 37 | +}) | |
| 38 | + | |
| 39 | +---@param question lc.ui.Question | |
| 40 | +return function(question, cb) | |
| 41 | + local items = language_picker.items(question.q.code_snippets) | |
| 42 | + | |
| 43 | + pickers | |
| 44 | + .new(opts, { | |
| 45 | + prompt_title = t("Available Languages"), | |
| 46 | + finder = finders.new_table({ | |
| 47 | + results = items, | |
| 48 | + entry_maker = entry_maker, | |
| 49 | + }), | |
| 50 | + sorter = conf.generic_sorter(opts), | |
| 51 | + attach_mappings = function(prompt_bufnr) | |
| 52 | + actions.select_default:replace(function() | |
| 53 | + local selection = action_state.get_selected_entry() | |
| 54 | + if not selection then | |
| 55 | + log.warn("No selection") | |
| 56 | + return | |
| 57 | + end | |
| 58 | + language_picker.select(selection.value.t, question, cb, function() | |
| 59 | + actions.close(prompt_bufnr) | |
| 60 | + end) | |
| 61 | + end) | |
| 62 | + | |
| 63 | + return true | |
| 64 | + end, | |
| 65 | + }) | |
| 66 | + :find() | |
| 67 | +end |
+36-0lua/leetcode/picker/question/fzf.lua
| @@ -0,0 +1,36 @@ | ||
| 1 | +local fzf = require("fzf-lua") | |
| 2 | +local problemlist = require("leetcode.cache.problemlist") | |
| 3 | +local t = require("leetcode.translator") | |
| 4 | +local question_picker = require("leetcode.picker.question") | |
| 5 | +local Picker = require("leetcode.picker") | |
| 6 | + | |
| 7 | +local deli = " " | |
| 8 | + | |
| 9 | +return function(questions, opts) | |
| 10 | + local items = question_picker.items(questions, opts) | |
| 11 | + | |
| 12 | + for i, item in ipairs(items) do | |
| 13 | + items[i] = Picker.normalize({ item })[1] | |
| 14 | + .. deli | |
| 15 | + .. Picker.apply_hl(item.value.title_slug, "leetcode_alt") | |
| 16 | + end | |
| 17 | + | |
| 18 | + fzf.fzf_exec(items, { | |
| 19 | + prompt = t("Select a Question") .. "> ", | |
| 20 | + winopts = { | |
| 21 | + win_height = question_picker.height, | |
| 22 | + win_width = question_picker.width, | |
| 23 | + }, | |
| 24 | + fzf_opts = { | |
| 25 | + ["--delimiter"] = deli, | |
| 26 | + ["--nth"] = "3..-3", | |
| 27 | + }, | |
| 28 | + actions = { | |
| 29 | + ["default"] = function(selected) | |
| 30 | + local slug = Picker.hidden_field(selected[1], deli) | |
| 31 | + local question = problemlist.get_by_title_slug(slug) | |
| 32 | + question_picker.select(question) | |
| 33 | + end, | |
| 34 | + }, | |
| 35 | + }) | |
| 36 | +end |
+102-0lua/leetcode/picker/question/init.lua
| @@ -0,0 +1,102 @@ | ||
| 1 | +local config = require("leetcode.config") | |
| 2 | +local log = require("leetcode.logger") | |
| 3 | +local ui_utils = require("leetcode-ui.utils") | |
| 4 | +local utils = require("leetcode.utils") | |
| 5 | +local Question = require("leetcode-ui.question") | |
| 6 | +local Picker = require("leetcode.picker") | |
| 7 | + | |
| 8 | +---@class leet.Picker.Question: leet.Picker | |
| 9 | +local P = {} | |
| 10 | + | |
| 11 | +P.width = 100 | |
| 12 | +P.height = 20 | |
| 13 | + | |
| 14 | +---@param items lc.cache.Question[] | |
| 15 | +---@param opts table<string, string[]> | |
| 16 | +--- | |
| 17 | +---@return lc.cache.Question[] | |
| 18 | +function P.filter(items, opts) | |
| 19 | + if vim.tbl_isempty(opts or {}) then | |
| 20 | + return items | |
| 21 | + end | |
| 22 | + | |
| 23 | + ---@param q lc.cache.Question | |
| 24 | + return vim.tbl_filter(function(q) | |
| 25 | + local diff_flag = true | |
| 26 | + if opts.difficulty and not vim.tbl_contains(opts.difficulty, q.difficulty:lower()) then | |
| 27 | + diff_flag = false | |
| 28 | + end | |
| 29 | + | |
| 30 | + local status_flag = true | |
| 31 | + if opts.status and not vim.tbl_contains(opts.status, q.status) then | |
| 32 | + status_flag = false | |
| 33 | + end | |
| 34 | + | |
| 35 | + return diff_flag and status_flag | |
| 36 | + end, items) | |
| 37 | +end | |
| 38 | + | |
| 39 | +---@param content lc.cache.Question[] | |
| 40 | +---@param opts table<string, string[]> | |
| 41 | +--- | |
| 42 | +---@return { entry: any, value: lc.cache.Question }[] | |
| 43 | +function P.items(content, opts) | |
| 44 | + local filtered = P.filter(content, opts) | |
| 45 | + return vim.tbl_map(function(item) | |
| 46 | + return { entry = P.entry(item), value = item } | |
| 47 | + end, filtered) | |
| 48 | +end | |
| 49 | + | |
| 50 | +---@param question lc.cache.Question | |
| 51 | +local function display_user_status(question) | |
| 52 | + if question.paid_only then | |
| 53 | + return config.auth.is_premium and config.icons.hl.unlock or config.icons.hl.lock | |
| 54 | + end | |
| 55 | + | |
| 56 | + return config.icons.hl.status[question.status] or { " " } | |
| 57 | +end | |
| 58 | + | |
| 59 | +---@param question lc.cache.Question | |
| 60 | +local function display_difficulty(question) | |
| 61 | + local hl = ui_utils.diff_to_hl(question.difficulty) | |
| 62 | + return { config.icons.square, hl } | |
| 63 | +end | |
| 64 | + | |
| 65 | +---@param question lc.cache.Question | |
| 66 | +local function display_question(question) | |
| 67 | + local index = { question.frontend_id .. ".", "leetcode_normal" } | |
| 68 | + local title = { utils.translate(question.title, question.title_cn) } | |
| 69 | + local ac_rate = { ("(%.1f%%)"):format(question.ac_rate), "leetcode_ref" } | |
| 70 | + | |
| 71 | + return unpack({ index, title, ac_rate }) | |
| 72 | +end | |
| 73 | + | |
| 74 | +function P.entry(item) | |
| 75 | + return { | |
| 76 | + display_user_status(item), | |
| 77 | + display_difficulty(item), | |
| 78 | + display_question(item), | |
| 79 | + } | |
| 80 | +end | |
| 81 | + | |
| 82 | +---@param item lc.cache.Question | |
| 83 | +function P.ordinal(item) | |
| 84 | + return ("%s. %s %s %s"):format( | |
| 85 | + tostring(item.frontend_id), | |
| 86 | + item.title, | |
| 87 | + item.title_cn, | |
| 88 | + item.title_slug | |
| 89 | + ) | |
| 90 | +end | |
| 91 | + | |
| 92 | +function P.select(selection, close) | |
| 93 | + if selection.paid_only and not config.auth.is_premium then | |
| 94 | + return log.warn("Question is for premium users only") | |
| 95 | + end | |
| 96 | + if close then | |
| 97 | + close() | |
| 98 | + end | |
| 99 | + Question(selection):mount() | |
| 100 | +end | |
| 101 | + | |
| 102 | +return P |
+71-0lua/leetcode/picker/question/telescope.lua
| @@ -0,0 +1,71 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | +local question_picker = require("leetcode.picker.question") | |
| 4 | + | |
| 5 | +local Question = require("leetcode-ui.question") | |
| 6 | + | |
| 7 | +local pickers = require("telescope.pickers") | |
| 8 | +local finders = require("telescope.finders") | |
| 9 | +local conf = require("telescope.config").values | |
| 10 | +local config = require("leetcode.config") | |
| 11 | + | |
| 12 | +local entry_display = require("telescope.pickers.entry_display") | |
| 13 | +local actions = require("telescope.actions") | |
| 14 | +local action_state = require("telescope.actions.state") | |
| 15 | + | |
| 16 | +local displayer = entry_display.create({ | |
| 17 | + separator = " ", | |
| 18 | + items = { | |
| 19 | + { width = 1 }, | |
| 20 | + { width = 1 }, | |
| 21 | + { width = 5 }, | |
| 22 | + { remaining = true }, | |
| 23 | + { remaining = true }, | |
| 24 | + }, | |
| 25 | +}) | |
| 26 | + | |
| 27 | +local function entry_maker(item) | |
| 28 | + return { | |
| 29 | + value = item.value, | |
| 30 | + display = function() | |
| 31 | + return displayer(item.entry) | |
| 32 | + end, | |
| 33 | + ordinal = question_picker.ordinal(item.value), | |
| 34 | + } | |
| 35 | +end | |
| 36 | + | |
| 37 | +local theme = require("telescope.themes").get_dropdown({ | |
| 38 | + layout_config = { | |
| 39 | + width = question_picker.width, | |
| 40 | + height = question_picker.height, | |
| 41 | + }, | |
| 42 | +}) | |
| 43 | + | |
| 44 | +---@param questions lc.cache.Question[] | |
| 45 | +return function(questions, opts) | |
| 46 | + local items = question_picker.items(questions, opts) | |
| 47 | + | |
| 48 | + pickers | |
| 49 | + .new(theme, { | |
| 50 | + prompt_title = t("Select a Question"), | |
| 51 | + finder = finders.new_table({ | |
| 52 | + results = items, | |
| 53 | + entry_maker = entry_maker, | |
| 54 | + }), | |
| 55 | + sorter = conf.generic_sorter(theme), | |
| 56 | + attach_mappings = function(prompt_bufnr) | |
| 57 | + actions.select_default:replace(function() | |
| 58 | + local selection = action_state.get_selected_entry() | |
| 59 | + if not selection then | |
| 60 | + log.warn("No selection") | |
| 61 | + return | |
| 62 | + end | |
| 63 | + question_picker.select(selection.value, function() | |
| 64 | + actions.close(prompt_bufnr) | |
| 65 | + end) | |
| 66 | + end) | |
| 67 | + return true | |
| 68 | + end, | |
| 69 | + }) | |
| 70 | + :find() | |
| 71 | +end |
+33-0lua/leetcode/picker/tabs/fzf.lua
| @@ -0,0 +1,33 @@ | ||
| 1 | +local fzf = require("fzf-lua") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | +local tabs_picker = require("leetcode.picker.tabs") | |
| 4 | +local Picker = require("leetcode.picker") | |
| 5 | + | |
| 6 | +local deli = "\t" | |
| 7 | + | |
| 8 | +return function(tabs) | |
| 9 | + local items = tabs_picker.items(tabs) | |
| 10 | + | |
| 11 | + for i, item in ipairs(items) do | |
| 12 | + items[i] = Picker.normalize({ item })[1] .. deli .. item.value.tabpage | |
| 13 | + end | |
| 14 | + | |
| 15 | + fzf.fzf_exec(items, { | |
| 16 | + prompt = t("Select a Question") .. "> ", | |
| 17 | + winopts = { | |
| 18 | + height = tabs_picker.height, | |
| 19 | + width = tabs_picker.width, | |
| 20 | + }, | |
| 21 | + fzf_opts = { | |
| 22 | + ["--delimiter"] = deli, | |
| 23 | + ["--nth"] = "1", | |
| 24 | + ["--with-nth"] = "1", | |
| 25 | + }, | |
| 26 | + actions = { | |
| 27 | + ["default"] = function(selected) | |
| 28 | + local tabpage = Picker.hidden_field(selected[1], deli) | |
| 29 | + tabs_picker.select({ tabpage = tonumber(tabpage) }) | |
| 30 | + end, | |
| 31 | + }, | |
| 32 | + }) | |
| 33 | +end |
+67-0lua/leetcode/picker/tabs/init.lua
| @@ -0,0 +1,67 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local utils = require("leetcode.utils") | |
| 3 | +local ui_utils = require("leetcode-ui.utils") | |
| 4 | +local t = require("leetcode.translator") | |
| 5 | + | |
| 6 | +local config = require("leetcode.config") | |
| 7 | +local icons = config.icons | |
| 8 | + | |
| 9 | +local T = {} | |
| 10 | + | |
| 11 | +T.width = 80 | |
| 12 | +T.height = 15 | |
| 13 | + | |
| 14 | +---@param q lc.QuestionResponse | |
| 15 | +function T.ordinal(q) | |
| 16 | + return ("%s. %s %s"):format(q.frontend_id, q.title, q.translated_title) | |
| 17 | +end | |
| 18 | + | |
| 19 | +local function display_current(entry) | |
| 20 | + local tabp = vim.api.nvim_get_current_tabpage() | |
| 21 | + if tabp ~= entry.tabpage then | |
| 22 | + return " " | |
| 23 | + end | |
| 24 | + | |
| 25 | + return { icons.caret.right, "leetcode_ref" } | |
| 26 | +end | |
| 27 | + | |
| 28 | +local function display_difficulty(q) | |
| 29 | + local lang = utils.get_lang(q.lang) | |
| 30 | + if not lang then | |
| 31 | + return {} | |
| 32 | + end | |
| 33 | + return { lang.icon, "leetcode_lang_" .. lang.slug } | |
| 34 | +end | |
| 35 | + | |
| 36 | +---@param question lc.QuestionResponse | |
| 37 | +local function display_question(question) | |
| 38 | + local hl = ui_utils.diff_to_hl(question.difficulty) | |
| 39 | + | |
| 40 | + local index = { question.frontend_id .. ".", hl } | |
| 41 | + local title = { utils.translate(question.title, question.translated_title) } | |
| 42 | + | |
| 43 | + return unpack({ index, title }) | |
| 44 | +end | |
| 45 | + | |
| 46 | +function T.entry(item) | |
| 47 | + return { | |
| 48 | + display_current(item), | |
| 49 | + display_difficulty(item.question), | |
| 50 | + display_question(item.question.q), | |
| 51 | + } | |
| 52 | +end | |
| 53 | + | |
| 54 | +function T.items(content) | |
| 55 | + return vim.tbl_map(function(item) | |
| 56 | + return { entry = T.entry(item), value = item } | |
| 57 | + end, content) | |
| 58 | +end | |
| 59 | + | |
| 60 | +function T.select(selection) | |
| 61 | + local ok, err = pcall(vim.api.nvim_set_current_tabpage, selection.tabpage) | |
| 62 | + if not ok then | |
| 63 | + log.error(err) | |
| 64 | + end | |
| 65 | +end | |
| 66 | + | |
| 67 | +return T |
+65-0lua/leetcode/picker/tabs/telescope.lua
| @@ -0,0 +1,65 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | + | |
| 4 | +local pickers = require("telescope.pickers") | |
| 5 | +local finders = require("telescope.finders") | |
| 6 | +local conf = require("telescope.config").values | |
| 7 | + | |
| 8 | +local entry_display = require("telescope.pickers.entry_display") | |
| 9 | +local actions = require("telescope.actions") | |
| 10 | +local tabs_picker = require("leetcode.picker.tabs") | |
| 11 | +local action_state = require("telescope.actions.state") | |
| 12 | + | |
| 13 | +local displayer = entry_display.create({ | |
| 14 | + separator = " ", | |
| 15 | + items = { | |
| 16 | + { width = 1 }, | |
| 17 | + { width = 1 }, | |
| 18 | + { width = 5 }, | |
| 19 | + { remaining = true }, | |
| 20 | + }, | |
| 21 | +}) | |
| 22 | + | |
| 23 | +local function entry_maker(item) | |
| 24 | + return { | |
| 25 | + value = item.value, | |
| 26 | + display = function() | |
| 27 | + return displayer(item.entry) | |
| 28 | + end, | |
| 29 | + ordinal = tabs_picker.ordinal(item.value.question.q), | |
| 30 | + } | |
| 31 | +end | |
| 32 | + | |
| 33 | +local opts = require("telescope.themes").get_dropdown({ | |
| 34 | + layout_config = { | |
| 35 | + width = tabs_picker.width, | |
| 36 | + height = tabs_picker.height, | |
| 37 | + }, | |
| 38 | +}) | |
| 39 | + | |
| 40 | +return function(tabs) | |
| 41 | + local items = tabs_picker.items(tabs) | |
| 42 | + | |
| 43 | + pickers | |
| 44 | + .new(opts, { | |
| 45 | + prompt_title = t("Select a Question"), | |
| 46 | + finder = finders.new_table({ | |
| 47 | + results = items, | |
| 48 | + entry_maker = entry_maker, | |
| 49 | + }), | |
| 50 | + sorter = conf.generic_sorter(opts), | |
| 51 | + attach_mappings = function(prompt_bufnr) | |
| 52 | + actions.select_default:replace(function() | |
| 53 | + local selection = action_state.get_selected_entry() | |
| 54 | + if not selection then | |
| 55 | + log.warn("No selection") | |
| 56 | + return | |
| 57 | + end | |
| 58 | + actions.close(prompt_bufnr) | |
| 59 | + tabs_picker.select(selection.value) | |
| 60 | + end) | |
| 61 | + return true | |
| 62 | + end, | |
| 63 | + }) | |
| 64 | + :find() | |
| 65 | +end |
+0-143lua/leetcode/pickers/language.lua
| @@ -1,143 +0,0 @@ | ||
| 1 | -local log = require("leetcode.logger") | |
| 2 | -local Question = require("leetcode-ui.question") | |
| 3 | -local utils = require("leetcode.utils") | |
| 4 | -local t = require("leetcode.translator") | |
| 5 | - | |
| 6 | -local pickers = require("telescope.pickers") | |
| 7 | -local finders = require("telescope.finders") | |
| 8 | -local conf = require("telescope.config").values | |
| 9 | -local config = require("leetcode.config") | |
| 10 | - | |
| 11 | -local entry_display = require("telescope.pickers.entry_display") | |
| 12 | -local actions = require("telescope.actions") | |
| 13 | -local action_state = require("telescope.actions.state") | |
| 14 | - | |
| 15 | -local M = {} | |
| 16 | - | |
| 17 | ----@param snippet lc.QuestionCodeSnippet | |
| 18 | ---- | |
| 19 | ----@return string | |
| 20 | -local function lang_formatter(snippet) | |
| 21 | - return string.format("%s %s", snippet.t.lang, snippet.t.slug) | |
| 22 | -end | |
| 23 | - | |
| 24 | ----@param snippet lc.QuestionCodeSnippet | |
| 25 | -local function dislay_icon(snippet) | |
| 26 | - local hl = "leetcode_lang_" .. snippet.t.slug | |
| 27 | - vim.api.nvim_set_hl(0, hl, { fg = snippet.t.color }) | |
| 28 | - | |
| 29 | - return { snippet.t.icon, hl } | |
| 30 | -end | |
| 31 | - | |
| 32 | ----@param snippet lc.QuestionCodeSnippet | |
| 33 | -local function display_lang(snippet) | |
| 34 | - -- | |
| 35 | - return { snippet.lang } | |
| 36 | -end | |
| 37 | - | |
| 38 | -local displayer = entry_display.create({ | |
| 39 | - separator = " ", | |
| 40 | - items = { | |
| 41 | - { width = 1 }, | |
| 42 | - { remaining = true }, | |
| 43 | - }, | |
| 44 | -}) | |
| 45 | - | |
| 46 | -local function make_display(entry) | |
| 47 | - ---@type lc.QuestionCodeSnippet | |
| 48 | - local snippet = entry.value | |
| 49 | - | |
| 50 | - return displayer({ | |
| 51 | - dislay_icon(snippet), | |
| 52 | - display_lang(snippet), | |
| 53 | - }) | |
| 54 | -end | |
| 55 | - | |
| 56 | ----@param entry lc.QuestionCodeSnippet | |
| 57 | -local function entry_maker(entry) | |
| 58 | - ---@type lc.language | |
| 59 | - entry.t = utils.get_lang(entry.lang_slug) | |
| 60 | - if not entry.t then | |
| 61 | - return | |
| 62 | - end | |
| 63 | - | |
| 64 | - return { | |
| 65 | - value = entry, | |
| 66 | - display = make_display, | |
| 67 | - ordinal = lang_formatter(entry), | |
| 68 | - } | |
| 69 | -end | |
| 70 | - | |
| 71 | -local opts = require("telescope.themes").get_dropdown() | |
| 72 | - | |
| 73 | ----@param question lc.ui.Question | |
| 74 | -M.pick_lang = function(question, callback) | |
| 75 | - pickers | |
| 76 | - .new(opts, { | |
| 77 | - prompt_title = t("Available Languages"), | |
| 78 | - finder = finders.new_table({ | |
| 79 | - results = question.q.code_snippets, | |
| 80 | - entry_maker = entry_maker, | |
| 81 | - }), | |
| 82 | - sorter = conf.generic_sorter(opts), | |
| 83 | - attach_mappings = function(prompt_bufnr, map) | |
| 84 | - actions.select_default:replace(function() | |
| 85 | - local selection = action_state.get_selected_entry() | |
| 86 | - if not selection then | |
| 87 | - return | |
| 88 | - end | |
| 89 | - | |
| 90 | - local snippet = selection.value | |
| 91 | - if question.lang == snippet.t.slug then | |
| 92 | - return log.warn( | |
| 93 | - ("%s: %s"):format(t("Language already set to"), snippet.t.lang) | |
| 94 | - ) | |
| 95 | - end | |
| 96 | - | |
| 97 | - config.lang = snippet.t.slug | |
| 98 | - actions.close(prompt_bufnr) | |
| 99 | - callback(snippet) | |
| 100 | - end) | |
| 101 | - | |
| 102 | - return true | |
| 103 | - end, | |
| 104 | - }) | |
| 105 | - :find() | |
| 106 | -end | |
| 107 | - | |
| 108 | ----@param question lc.ui.Question | |
| 109 | -M.pick = function(question) | |
| 110 | - pickers | |
| 111 | - .new(opts, { | |
| 112 | - prompt_title = t("Available Languages"), | |
| 113 | - finder = finders.new_table({ | |
| 114 | - results = question.q.code_snippets, | |
| 115 | - entry_maker = entry_maker, | |
| 116 | - }), | |
| 117 | - sorter = conf.generic_sorter(opts), | |
| 118 | - attach_mappings = function(prompt_bufnr, map) | |
| 119 | - actions.select_default:replace(function() | |
| 120 | - local selection = action_state.get_selected_entry() | |
| 121 | - if not selection then | |
| 122 | - return | |
| 123 | - end | |
| 124 | - | |
| 125 | - local snippet = selection.value | |
| 126 | - if question.lang == snippet.t.slug then | |
| 127 | - return log.warn( | |
| 128 | - ("%s: %s"):format(t("Language already set to"), snippet.t.lang) | |
| 129 | - ) | |
| 130 | - end | |
| 131 | - | |
| 132 | - config.lang = snippet.t.slug | |
| 133 | - actions.close(prompt_bufnr) | |
| 134 | - question:change_lang(snippet.t.slug) | |
| 135 | - end) | |
| 136 | - | |
| 137 | - return true | |
| 138 | - end, | |
| 139 | - }) | |
| 140 | - :find() | |
| 141 | -end | |
| 142 | - | |
| 143 | -return M |
+0-122lua/leetcode/pickers/question-tabs.lua
| @@ -1,122 +0,0 @@ | ||
| 1 | -local log = require("leetcode.logger") | |
| 2 | -local utils = require("leetcode.utils") | |
| 3 | -local ui_utils = require("leetcode-ui.utils") | |
| 4 | -local t = require("leetcode.translator") | |
| 5 | - | |
| 6 | -local pickers = require("telescope.pickers") | |
| 7 | -local finders = require("telescope.finders") | |
| 8 | -local conf = require("telescope.config").values | |
| 9 | -local config = require("leetcode.config") | |
| 10 | -local icons = config.icons | |
| 11 | - | |
| 12 | -local entry_display = require("telescope.pickers.entry_display") | |
| 13 | -local actions = require("telescope.actions") | |
| 14 | -local action_state = require("telescope.actions.state") | |
| 15 | - | |
| 16 | ----@param q lc.QuestionResponse | |
| 17 | ---- | |
| 18 | ----@return string | |
| 19 | -local function question_formatter(q) | |
| 20 | - return string.format("%s. %s %s", q.frontend_id, q.title, q.translated_title) | |
| 21 | -end | |
| 22 | - | |
| 23 | -local function display_current(entry) | |
| 24 | - local tabp = vim.api.nvim_get_current_tabpage() | |
| 25 | - if tabp ~= entry.tabpage then | |
| 26 | - return unpack({ "", "" }) | |
| 27 | - end | |
| 28 | - | |
| 29 | - return { icons.caret.right, "leetcode_ref" } | |
| 30 | -end | |
| 31 | - | |
| 32 | -local function display_difficulty(q) | |
| 33 | - local lang = utils.get_lang(q.lang) | |
| 34 | - if not lang then | |
| 35 | - return {} | |
| 36 | - end | |
| 37 | - return { lang.icon, "leetcode_lang_" .. lang.slug } | |
| 38 | -end | |
| 39 | - | |
| 40 | ----@param question lc.QuestionResponse | |
| 41 | -local function display_question(question) | |
| 42 | - local hl = ui_utils.diff_to_hl(question.difficulty) | |
| 43 | - | |
| 44 | - local index = { question.frontend_id .. ".", hl } | |
| 45 | - local title = { utils.translate(question.title, question.translated_title) } | |
| 46 | - | |
| 47 | - return unpack({ index, title }) | |
| 48 | -end | |
| 49 | - | |
| 50 | -local displayer = entry_display.create({ | |
| 51 | - separator = " ", | |
| 52 | - items = { | |
| 53 | - { width = 1 }, | |
| 54 | - { width = 1 }, | |
| 55 | - { width = 5 }, | |
| 56 | - { remaining = true }, | |
| 57 | - }, | |
| 58 | -}) | |
| 59 | - | |
| 60 | -local function make_display(entry) | |
| 61 | - ---@type lc.cache.Question | |
| 62 | - local q = entry.value.question.q | |
| 63 | - | |
| 64 | - return displayer({ | |
| 65 | - display_current(entry.value), | |
| 66 | - display_difficulty(entry.value.question), | |
| 67 | - display_question(q), | |
| 68 | - }) | |
| 69 | -end | |
| 70 | - | |
| 71 | -local function entry_maker(entry) | |
| 72 | - return { | |
| 73 | - value = entry, | |
| 74 | - display = make_display, | |
| 75 | - ordinal = question_formatter(entry.question.q), | |
| 76 | - } | |
| 77 | -end | |
| 78 | - | |
| 79 | -local opts = require("telescope.themes").get_dropdown() | |
| 80 | - | |
| 81 | -return { | |
| 82 | - pick = function() | |
| 83 | - local tabs = utils.question_tabs() | |
| 84 | - | |
| 85 | - if vim.tbl_isempty(tabs) then | |
| 86 | - return log.warn("No questions opened") | |
| 87 | - end | |
| 88 | - | |
| 89 | - -- table.sort(tabs, function(q1, q2) | |
| 90 | - -- local fid1, fid2 = | |
| 91 | - -- tonumber(q1.question.q.frontend_id), tonumber(q2.question.q.frontend_id) | |
| 92 | - -- return (fid1 and fid2) and fid1 < fid2 or fid1 ~= nil | |
| 93 | - -- end) | |
| 94 | - | |
| 95 | - pickers | |
| 96 | - .new(opts, { | |
| 97 | - prompt_title = t("Select a Question"), | |
| 98 | - finder = finders.new_table({ | |
| 99 | - results = tabs, | |
| 100 | - entry_maker = entry_maker, | |
| 101 | - }), | |
| 102 | - sorter = conf.generic_sorter(opts), | |
| 103 | - attach_mappings = function(prompt_bufnr) | |
| 104 | - actions.select_default:replace(function() | |
| 105 | - actions.close(prompt_bufnr) | |
| 106 | - local selection = action_state.get_selected_entry() | |
| 107 | - | |
| 108 | - if not selection then | |
| 109 | - return | |
| 110 | - end | |
| 111 | - local ok, err = | |
| 112 | - pcall(vim.api.nvim_set_current_tabpage, selection.value.tabpage) | |
| 113 | - if not ok then | |
| 114 | - log.error(err) | |
| 115 | - end | |
| 116 | - end) | |
| 117 | - return true | |
| 118 | - end, | |
| 119 | - }) | |
| 120 | - :find() | |
| 121 | - end, | |
| 122 | -} |
+0-148lua/leetcode/pickers/question.lua
| @@ -1,148 +0,0 @@ | ||
| 1 | -local log = require("leetcode.logger") | |
| 2 | -local t = require("leetcode.translator") | |
| 3 | -local utils = require("leetcode.utils") | |
| 4 | -local ui_utils = require("leetcode-ui.utils") | |
| 5 | - | |
| 6 | -local Question = require("leetcode-ui.question") | |
| 7 | - | |
| 8 | -local pickers = require("telescope.pickers") | |
| 9 | -local finders = require("telescope.finders") | |
| 10 | -local conf = require("telescope.config").values | |
| 11 | -local config = require("leetcode.config") | |
| 12 | - | |
| 13 | -local entry_display = require("telescope.pickers.entry_display") | |
| 14 | -local actions = require("telescope.actions") | |
| 15 | -local action_state = require("telescope.actions.state") | |
| 16 | - | |
| 17 | ----@param question lc.cache.Question | |
| 18 | ---- | |
| 19 | ----@return string | |
| 20 | -local function question_formatter(question) | |
| 21 | - return ("%s. %s %s %s"):format( | |
| 22 | - tostring(question.frontend_id), | |
| 23 | - question.title, | |
| 24 | - question.title_cn, | |
| 25 | - question.title_slug | |
| 26 | - ) | |
| 27 | -end | |
| 28 | - | |
| 29 | ----@param question lc.cache.Question | |
| 30 | -local function display_difficulty(question) | |
| 31 | - local hl = ui_utils.diff_to_hl(question.difficulty) | |
| 32 | - return { config.icons.square, hl } | |
| 33 | -end | |
| 34 | - | |
| 35 | ----@param question lc.cache.Question | |
| 36 | -local function display_user_status(question) | |
| 37 | - if question.paid_only then | |
| 38 | - return config.auth.is_premium and config.icons.hl.unlock or config.icons.hl.lock | |
| 39 | - end | |
| 40 | - | |
| 41 | - return config.icons.hl.status[question.status] or { "" } | |
| 42 | -end | |
| 43 | - | |
| 44 | ----@param question lc.cache.Question | |
| 45 | -local function display_question(question) | |
| 46 | - local ac_rate = { ("%.1f%%"):format(question.ac_rate), "leetcode_ref" } | |
| 47 | - local index = { question.frontend_id .. ".", "leetcode_normal" } | |
| 48 | - | |
| 49 | - local title = { utils.translate(question.title, question.title_cn) } | |
| 50 | - | |
| 51 | - return unpack({ index, title, ac_rate }) | |
| 52 | -end | |
| 53 | - | |
| 54 | -local displayer = entry_display.create({ | |
| 55 | - separator = " ", | |
| 56 | - items = { | |
| 57 | - { width = 1 }, | |
| 58 | - { width = 1 }, | |
| 59 | - { width = 5 }, | |
| 60 | - { width = 78 }, | |
| 61 | - { width = 5 }, | |
| 62 | - }, | |
| 63 | -}) | |
| 64 | - | |
| 65 | -local function make_display(entry) | |
| 66 | - ---@type lc.cache.Question | |
| 67 | - local q = entry.value | |
| 68 | - | |
| 69 | - return displayer({ | |
| 70 | - display_user_status(q), | |
| 71 | - display_difficulty(q), | |
| 72 | - display_question(q), | |
| 73 | - }) | |
| 74 | -end | |
| 75 | - | |
| 76 | -local function entry_maker(entry) | |
| 77 | - return { | |
| 78 | - value = entry, | |
| 79 | - display = make_display, | |
| 80 | - ordinal = question_formatter(entry), | |
| 81 | - } | |
| 82 | -end | |
| 83 | - | |
| 84 | -local theme = require("telescope.themes").get_dropdown({ | |
| 85 | - layout_config = { | |
| 86 | - width = 100, | |
| 87 | - height = 20, | |
| 88 | - }, | |
| 89 | -}) | |
| 90 | - | |
| 91 | ----@param questions lc.cache.Question[] | |
| 92 | ----@param opts table<string, string[]> | |
| 93 | ---- | |
| 94 | ----@return lc.cache.Question[] | |
| 95 | -local function filter_questions(questions, opts) | |
| 96 | - if vim.tbl_isempty(opts or {}) then | |
| 97 | - return questions | |
| 98 | - end | |
| 99 | - | |
| 100 | - ---@param q lc.cache.Question | |
| 101 | - return vim.tbl_filter(function(q) | |
| 102 | - local diff_flag = true | |
| 103 | - if opts.difficulty and not vim.tbl_contains(opts.difficulty, q.difficulty:lower()) then | |
| 104 | - diff_flag = false | |
| 105 | - end | |
| 106 | - | |
| 107 | - local status_flag = true | |
| 108 | - if opts.status and not vim.tbl_contains(opts.status, q.status) then | |
| 109 | - status_flag = false | |
| 110 | - end | |
| 111 | - | |
| 112 | - return diff_flag and status_flag | |
| 113 | - end, questions) | |
| 114 | -end | |
| 115 | - | |
| 116 | -return { | |
| 117 | - ---@param questions lc.cache.Question[] | |
| 118 | - pick = function(questions, opts) | |
| 119 | - pickers | |
| 120 | - .new(theme, { | |
| 121 | - prompt_title = t("Select a Question"), | |
| 122 | - finder = finders.new_table({ | |
| 123 | - results = filter_questions(questions, opts), | |
| 124 | - entry_maker = entry_maker, | |
| 125 | - }), | |
| 126 | - sorter = conf.generic_sorter(theme), | |
| 127 | - attach_mappings = function(prompt_bufnr, map) | |
| 128 | - actions.select_default:replace(function() | |
| 129 | - local selection = action_state.get_selected_entry() | |
| 130 | - if not selection then | |
| 131 | - return | |
| 132 | - end | |
| 133 | - | |
| 134 | - local q = selection.value | |
| 135 | - if q.paid_only and not config.auth.is_premium then | |
| 136 | - return log.warn("Question is for premium users only") | |
| 137 | - end | |
| 138 | - | |
| 139 | - actions.close(prompt_bufnr) | |
| 140 | - Question(q):mount() | |
| 141 | - end) | |
| 142 | - | |
| 143 | - return true | |
| 144 | - end, | |
| 145 | - }) | |
| 146 | - :find() | |
| 147 | - end, | |
| 148 | -} |