diff --git a/README.md b/README.md index 57ed6f9f..cff3dd79 100644 --- a/README.md +++ b/README.md @@ -279,9 +279,14 @@ injector = { ---@type table ### picker -Supported picker providers are `telescope` and `fzf-lua`. -When provider is `nil`, [leetcode.nvim] will first try to use `fzf-lua`, -if not found it will fallback to `telescope`. +Supported picker providers are: + +- `snacks-picker` +- `fzf-lua` +- `telescope` + +If `provider` is `nil`, [leetcode.nvim] will try to resolve the first +available one in the order above. ```lua ---@type lc.picker @@ -377,26 +382,22 @@ image_support = false, - `inject` re-inject code for the current question - `session` - - `create` create a new session - `change` change the current session - `update` update the current session in case it went out of sync - `desc` toggle question description - - `toggle` same as `Leet desc` - `stats` toggle description stats visibility - `cookie` - - `update` opens a prompt to enter a new cookie - `delete` sign-out - `cache` - - `update` updates cache #### Some commands can take optional arguments. To stack argument values separate them by a `,` diff --git a/lua/leetcode/config/template.lua b/lua/leetcode/config/template.lua index 40c5779c..82be3847 100644 --- a/lua/leetcode/config/template.lua +++ b/lua/leetcode/config/template.lua @@ -38,7 +38,7 @@ ---@alias lc.storage table<"cache"|"home", string> ----@alias lc.picker { provider?: "fzf-lua" | "telescope" } +---@alias lc.picker { provider?: "fzf-lua" | "telescope" | "snacks-picker" } ---@class lc.UserConfig local M = { diff --git a/lua/leetcode/picker/init.lua b/lua/leetcode/picker/init.lua index 8fabd228..65547ba9 100644 --- a/lua/leetcode/picker/init.lua +++ b/lua/leetcode/picker/init.lua @@ -1,26 +1,65 @@ local log = require("leetcode.logger") local config = require("leetcode.config") ----@return "fzf" | "telescope" +local provider_order = { "snacks-picker", "fzf-lua", "telescope" } +local providers = { + ["fzf-lua"] = { + name = "fzf", + is_available = function() + return pcall(require, "fzf-lua") + end, + }, + ["snacks-picker"] = { + name = "snacks", + is_available = function() + return pcall(function() ---@diagnostic disable-next-line: undefined-field + assert(Snacks.config["picker"].enabled) + end) + end, + }, + ["telescope"] = { + name = "telescope", + is_available = function() + return pcall(require, "telescope") + end, + }, +} + +local available_pickers = table.concat( + vim.tbl_map(function(p) + return providers[p].name + end, provider_order), + ", " +) + +---@return "fzf" | "telescope" | "snacks" local function resolve_provider() ---@type string local provider = config.user.picker.provider if provider == nil then - local fzf_ok = pcall(require, "fzf-lua") - if fzf_ok then - return "fzf" - end - local telescope_ok = pcall(require, "telescope") - if telescope_ok then - return "telescope" + for _, key in ipairs(provider_order) do + local picker = providers[key] + + if picker.is_available() then + return picker.name + end end - error("no supported picker provider found") - else - local provider_ok = pcall(require, provider) - assert(provider_ok, ("specified picker provider not found: `%s`"):format(provider)) - return provider == "fzf-lua" and "fzf" or provider + + error(("No picker is available. Please install one of the following: `%s`") -- + :format(available_pickers)) end + + local picker = providers[provider] + assert( + picker, + ("Picker `%s` is not supported. Available pickers: `%s`") -- + :format(provider, available_pickers) + ) + + local ok = picker.is_available() + assert(ok, ("Picker `%s` is not available. Make sure it is installed"):format(provider)) + return picker.name end ---@class leet.Picker diff --git a/lua/leetcode/picker/language/snacks.lua b/lua/leetcode/picker/language/snacks.lua new file mode 100644 index 00000000..7729ecb6 --- /dev/null +++ b/lua/leetcode/picker/language/snacks.lua @@ -0,0 +1,64 @@ +local log = require("leetcode.logger") +local t = require("leetcode.translator") +local picker = require("snacks.picker") + +local language_picker = require("leetcode.picker.language") + +---@param question lc.ui.Question +return function(question, cb) + local items = language_picker.items(question.q.code_snippets) + local finder_items = {} + local completed = false + + for _, item in ipairs(items) do + local text = language_picker.ordinal(item.value) + table.insert(finder_items, { + text = text, + item = item, + }) + end + + picker.pick({ + items = finder_items, + format = function(item) + local ret = {} + vim.tbl_map(function(col) + if type(col) == "table" then + ret[#ret + 1] = col + else + ret[#ret + 1] = { col } + end + ret[#ret + 1] = { " " } + end, item.item.entry) + return ret + end, + title = t("Available Languages"), + layout = { + preview = false, + preset = "select", + layout = { + height = language_picker.height, + width = language_picker.width, + }, + }, + actions = { + confirm = function(p, item) + if completed then + return + end + completed = true + p:close() + vim.schedule(function() + language_picker.select(item.item.value.t, question, cb) + end) + end, + }, + on_close = function() + if completed then + return + end + completed = true + log.warn("No selection") + end, + }) +end diff --git a/lua/leetcode/picker/question/init.lua b/lua/leetcode/picker/question/init.lua index 77866f28..6967b583 100644 --- a/lua/leetcode/picker/question/init.lua +++ b/lua/leetcode/picker/question/init.lua @@ -9,7 +9,7 @@ local Picker = require("leetcode.picker") local P = {} P.width = 100 -P.height = 20 +P.height = 0.6 ---@param items lc.cache.Question[] ---@param opts table diff --git a/lua/leetcode/picker/question/snacks.lua b/lua/leetcode/picker/question/snacks.lua new file mode 100644 index 00000000..a4e527b7 --- /dev/null +++ b/lua/leetcode/picker/question/snacks.lua @@ -0,0 +1,64 @@ +local log = require("leetcode.logger") +local t = require("leetcode.translator") +local question_picker = require("leetcode.picker.question") + +local picker = require("snacks.picker") + +---@param questions lc.cache.Question[] +return function(questions, opts) + local items = question_picker.items(questions, opts) + local finder_items = {} + local completed = false + + for _, item in ipairs(items) do + local text = question_picker.ordinal(item.value) + table.insert(finder_items, { + text = text, + item = item, + }) + end + + picker.pick({ + items = finder_items, + format = function(item) + local ret = {} + vim.tbl_map(function(col) + if type(col) == "table" then + ret[#ret + 1] = col + else + ret[#ret + 1] = { col } + end + ret[#ret + 1] = { " " } + end, item.item.entry) + return ret + end, + title = t("Select a Question"), + layout = { + preset = "select", + preview = false, + layout = { + height = question_picker.height, + width = question_picker.width, + }, + }, + actions = { + confirm = function(p, item) + if completed then + return + end + completed = true + p:close() + vim.schedule(function() + question_picker.select(item.item.value) + end) + end, + }, + on_close = function() + if completed then + return + end + completed = true + log.warn("No selection") + end, + }) +end diff --git a/lua/leetcode/picker/tabs/snacks.lua b/lua/leetcode/picker/tabs/snacks.lua new file mode 100644 index 00000000..6d987f8f --- /dev/null +++ b/lua/leetcode/picker/tabs/snacks.lua @@ -0,0 +1,66 @@ +local log = require("leetcode.logger") +local t = require("leetcode.translator") +local tabs_picker = require("leetcode.picker.tabs") + +local picker = require("snacks.picker") + +return function(tabs) + local items = tabs_picker.items(tabs) + local finder_items = {} + local completed = false + local items_reflect = {} + + for _, item in ipairs(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 + + picker.pick({ + items = finder_items, + format = function(item) + local ret = {} + vim.tbl_map(function(col) + if type(col) == "table" then + ret[#ret + 1] = col + else + ret[#ret + 1] = { col } + end + ret[#ret + 1] = { " " } + end, item.entry) + return ret + end, + title = t("Select a Question"), + layout = { + preview = false, + preset = "select", + layout = { + height = tabs_picker.height, + width = tabs_picker.width, + }, + }, + actions = { + confirm = function(p, item) + if completed then + return + end + completed = true + p:close() + vim.schedule(function() + tabs_picker.select(items_reflect[item.item]) + end) + end, + }, + on_close = function() + if completed then + return + end + completed = true + log.warn("No selection") + end, + }) +end