A Neovim plugin enabling you to solve LeetCode problems.
feat: `folke/snacks.nvim` picker support
5 files changed, +203 -3
+1-1lua/leetcode/config/template.lua
| @@ -38,7 +38,7 @@ | ||
| 38 | 38 | |
| 39 | 39 | ---@alias lc.storage table<"cache"|"home", string> |
| 40 | 40 | |
| 41 | ----@alias lc.picker { provider?: "fzf-lua" | "telescope" } | |
| 41 | +---@alias lc.picker { provider?: "fzf-lua" | "telescope" | "snacks-picker" } | |
| 42 | 42 | |
| 43 | 43 | ---@class lc.UserConfig |
| 44 | 44 | local M = { |
+11-2lua/leetcode/picker/init.lua
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | local log = require("leetcode.logger") |
| 2 | 2 | local config = require("leetcode.config") |
| 3 | 3 | |
| 4 | ----@return "fzf" | "telescope" | |
| 4 | +---@return "fzf" | "telescope" | "snacks" | |
| 5 | 5 | local function resolve_provider() |
| 6 | 6 | ---@type string |
| 7 | 7 | local provider = config.user.picker.provider |
| @@ -15,9 +15,18 @@ local function resolve_provider() | ||
| 15 | 15 | if telescope_ok then |
| 16 | 16 | return "telescope" |
| 17 | 17 | end |
| 18 | + local snacks_ok = pcall(require, "snacks.picker") | |
| 19 | + if snacks_ok then | |
| 20 | + return "snacks" | |
| 21 | + end | |
| 18 | 22 | error("no supported picker provider found") |
| 19 | 23 | else |
| 20 | - local provider_ok = pcall(require, provider) | |
| 24 | + local mod = provider | |
| 25 | + if provider == "snacks-picker" then | |
| 26 | + provider = "snacks" | |
| 27 | + mod = "snacks.picker" | |
| 28 | + end | |
| 29 | + local provider_ok = pcall(require, mod) | |
| 21 | 30 | assert(provider_ok, ("specified picker provider not found: `%s`"):format(provider)) |
| 22 | 31 | return provider == "fzf-lua" and "fzf" or provider |
| 23 | 32 | end |
+63-0lua/leetcode/picker/language/snacks.lua
| @@ -0,0 +1,63 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | +local picker = require("snacks.picker") | |
| 4 | + | |
| 5 | +local language_picker = require("leetcode.picker.language") | |
| 6 | + | |
| 7 | +---@param question lc.ui.Question | |
| 8 | +return function(question, cb) | |
| 9 | + local items = language_picker.items(question.q.code_snippets) | |
| 10 | + local finder_items = {} | |
| 11 | + local completed = false | |
| 12 | + | |
| 13 | + for _, item in ipairs(items) do | |
| 14 | + local text = language_picker.ordinal(item.value) | |
| 15 | + table.insert(finder_items, { | |
| 16 | + text = text, | |
| 17 | + item = item, | |
| 18 | + }) | |
| 19 | + end | |
| 20 | + | |
| 21 | + picker.pick({ | |
| 22 | + source = "select", | |
| 23 | + items = finder_items, | |
| 24 | + format = function(item) | |
| 25 | + local ret = {} | |
| 26 | + vim.tbl_map(function(col) | |
| 27 | + if type(col) == "table" then | |
| 28 | + ret[#ret + 1] = col | |
| 29 | + else | |
| 30 | + ret[#ret + 1] = { col } | |
| 31 | + end | |
| 32 | + ret[#ret + 1] = { " " } | |
| 33 | + end, item.item.entry) | |
| 34 | + return ret | |
| 35 | + end, | |
| 36 | + title = t("Available Languages"), | |
| 37 | + layout = { | |
| 38 | + preview = false, | |
| 39 | + layout = { | |
| 40 | + height = math.floor(math.min(vim.o.lines * 0.8 - 10, #items + 2) + 0.5), | |
| 41 | + }, | |
| 42 | + }, | |
| 43 | + actions = { | |
| 44 | + confirm = function(p, item) | |
| 45 | + if completed then | |
| 46 | + return | |
| 47 | + end | |
| 48 | + completed = true | |
| 49 | + p:close() | |
| 50 | + vim.schedule(function() | |
| 51 | + language_picker.select(item.item.value.t, question, cb) | |
| 52 | + end) | |
| 53 | + end, | |
| 54 | + }, | |
| 55 | + on_close = function() | |
| 56 | + if completed then | |
| 57 | + return | |
| 58 | + end | |
| 59 | + completed = true | |
| 60 | + log.warn("No selection") | |
| 61 | + end, | |
| 62 | + }) | |
| 63 | +end |
+63-0lua/leetcode/picker/question/snacks.lua
| @@ -0,0 +1,63 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | +local question_picker = require("leetcode.picker.question") | |
| 4 | + | |
| 5 | +local picker = require("snacks.picker") | |
| 6 | + | |
| 7 | +---@param questions lc.cache.Question[] | |
| 8 | +return function(questions, opts) | |
| 9 | + local items = question_picker.items(questions, opts) | |
| 10 | + local finder_items = {} | |
| 11 | + local completed = false | |
| 12 | + | |
| 13 | + for _, item in ipairs(items) do | |
| 14 | + local text = question_picker.ordinal(item.value) | |
| 15 | + table.insert(finder_items, { | |
| 16 | + text = text, | |
| 17 | + item = item, | |
| 18 | + }) | |
| 19 | + end | |
| 20 | + | |
| 21 | + picker.pick({ | |
| 22 | + source = "select", | |
| 23 | + items = finder_items, | |
| 24 | + format = function(item) | |
| 25 | + local ret = {} | |
| 26 | + vim.tbl_map(function(col) | |
| 27 | + if type(col) == "table" then | |
| 28 | + ret[#ret + 1] = col | |
| 29 | + else | |
| 30 | + ret[#ret + 1] = { col } | |
| 31 | + end | |
| 32 | + ret[#ret + 1] = { " " } | |
| 33 | + end, item.item.entry) | |
| 34 | + return ret | |
| 35 | + end, | |
| 36 | + title = t("Select a Question"), | |
| 37 | + layout = { | |
| 38 | + preview = false, | |
| 39 | + layout = { | |
| 40 | + height = math.floor(math.min(vim.o.lines * 0.8 - 10, #items + 2) + 0.5), | |
| 41 | + }, | |
| 42 | + }, | |
| 43 | + actions = { | |
| 44 | + confirm = function(p, item) | |
| 45 | + if completed then | |
| 46 | + return | |
| 47 | + end | |
| 48 | + completed = true | |
| 49 | + p:close() | |
| 50 | + vim.schedule(function() | |
| 51 | + question_picker.select(item.value) | |
| 52 | + end) | |
| 53 | + end, | |
| 54 | + }, | |
| 55 | + on_close = function() | |
| 56 | + if completed then | |
| 57 | + return | |
| 58 | + end | |
| 59 | + completed = true | |
| 60 | + log.warn("No selection") | |
| 61 | + end, | |
| 62 | + }) | |
| 63 | +end |
+65-0lua/leetcode/picker/tabs/snacks.lua
| @@ -0,0 +1,65 @@ | ||
| 1 | +local log = require("leetcode.logger") | |
| 2 | +local t = require("leetcode.translator") | |
| 3 | +local tabs_picker = require("leetcode.picker.tabs") | |
| 4 | + | |
| 5 | +local picker = require("snacks.picker") | |
| 6 | + | |
| 7 | +return function(tabs) | |
| 8 | + local items = tabs_picker.items(tabs) | |
| 9 | + local finder_items = {} | |
| 10 | + local completed = false | |
| 11 | + local items_reflect = {} | |
| 12 | + | |
| 13 | + for _, item in ipairs(items) do | |
| 14 | + items_reflect[item.value.question.q.frontend_id] = item.value | |
| 15 | + local text = tabs_picker.ordinal(item.value.question.q) | |
| 16 | + table.insert(finder_items, { | |
| 17 | + entry = item.entry, | |
| 18 | + item = item.value.question.q.frontend_id, | |
| 19 | + text = text, | |
| 20 | + }) | |
| 21 | + end | |
| 22 | + | |
| 23 | + picker.pick({ | |
| 24 | + source = "select", | |
| 25 | + items = finder_items, | |
| 26 | + format = function(item) | |
| 27 | + local ret = {} | |
| 28 | + vim.tbl_map(function(col) | |
| 29 | + if type(col) == "table" then | |
| 30 | + ret[#ret + 1] = col | |
| 31 | + else | |
| 32 | + ret[#ret + 1] = { col } | |
| 33 | + end | |
| 34 | + ret[#ret + 1] = { " " } | |
| 35 | + end, item.entry) | |
| 36 | + return ret | |
| 37 | + end, | |
| 38 | + title = t("Select a Question"), | |
| 39 | + layout = { | |
| 40 | + preview = false, | |
| 41 | + layout = { | |
| 42 | + height = math.floor(math.min(vim.o.lines * 0.8 - 10, #items + 2) + 0.5), | |
| 43 | + }, | |
| 44 | + }, | |
| 45 | + actions = { | |
| 46 | + confirm = function(p, item) | |
| 47 | + if completed then | |
| 48 | + return | |
| 49 | + end | |
| 50 | + completed = true | |
| 51 | + p:close() | |
| 52 | + vim.schedule(function() | |
| 53 | + tabs_picker.select(items_reflect[item.item]) | |
| 54 | + end) | |
| 55 | + end, | |
| 56 | + }, | |
| 57 | + on_close = function() | |
| 58 | + if completed then | |
| 59 | + return | |
| 60 | + end | |
| 61 | + completed = true | |
| 62 | + log.warn("No selection") | |
| 63 | + end, | |
| 64 | + }) | |
| 65 | +end |