irongit

A Neovim plugin enabling you to solve LeetCode problems.

feat: `folke/snacks.nvim` picker support (#177)

feat: `folke/snacks.nvim` picker support
Marcinauthored committed by GitHub
parent db7e1cdparent 4598846commit 46c627cc51c36163141b1951e703bfdb437a9e18Browse files

7 files changed, +256 -22

+8-7README.md
@@ -279,9 +279,14 @@ injector = { ---@type table<lc.lang, lc.inject>
279279
280280 ### picker
281281
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`.
282+Supported picker providers are:
283+
284+- `snacks-picker`
285+- `fzf-lua`
286+- `telescope`
287+
288+If `provider` is `nil`, [leetcode.nvim] will try to resolve the first
289+available one in the order above.
285290
286291 ```lua
287292 ---@type lc.picker
@@ -377,26 +382,22 @@ image_support = false,
377382 - `inject` re-inject code for the current question
378383
379384 - `session`
380-
381385 - `create` create a new session
382386 - `change` change the current session
383387
384388 - `update` update the current session in case it went out of sync
385389
386390 - `desc` toggle question description
387-
388391 - `toggle` same as `Leet desc`
389392
390393 - `stats` toggle description stats visibility
391394
392395 - `cookie`
393-
394396 - `update` opens a prompt to enter a new cookie
395397
396398 - `delete` sign-out
397399
398400 - `cache`
399-
400401 - `update` updates cache
401402
402403 #### Some commands can take optional arguments. To stack argument values separate them by a `,`
+1-1lua/leetcode/config/template.lua
@@ -38,7 +38,7 @@
3838
3939 ---@alias lc.storage table<"cache"|"home", string>
4040
41----@alias lc.picker { provider?: "fzf-lua" | "telescope" }
41+---@alias lc.picker { provider?: "fzf-lua" | "telescope" | "snacks-picker" }
4242
4343 ---@class lc.UserConfig
4444 local M = {
+52-13lua/leetcode/picker/init.lua
@@ -1,26 +1,65 @@
11 local log = require("leetcode.logger")
22 local config = require("leetcode.config")
33
4----@return "fzf" | "telescope"
4+local provider_order = { "snacks-picker", "fzf-lua", "telescope" }
5+local providers = {
6+ ["fzf-lua"] = {
7+ name = "fzf",
8+ is_available = function()
9+ return pcall(require, "fzf-lua")
10+ end,
11+ },
12+ ["snacks-picker"] = {
13+ name = "snacks",
14+ is_available = function()
15+ return pcall(function() ---@diagnostic disable-next-line: undefined-field
16+ assert(Snacks.config["picker"].enabled)
17+ end)
18+ end,
19+ },
20+ ["telescope"] = {
21+ name = "telescope",
22+ is_available = function()
23+ return pcall(require, "telescope")
24+ end,
25+ },
26+}
27+
28+local available_pickers = table.concat(
29+ vim.tbl_map(function(p)
30+ return providers[p].name
31+ end, provider_order),
32+ ", "
33+)
34+
35+---@return "fzf" | "telescope" | "snacks"
536 local function resolve_provider()
637 ---@type string
738 local provider = config.user.picker.provider
839
940 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"
41+ for _, key in ipairs(provider_order) do
42+ local picker = providers[key]
43+
44+ if picker.is_available() then
45+ return picker.name
46+ end
1747 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
48+
49+ error(("No picker is available. Please install one of the following: `%s`") --
50+ :format(available_pickers))
2351 end
52+
53+ local picker = providers[provider]
54+ assert(
55+ picker,
56+ ("Picker `%s` is not supported. Available pickers: `%s`") --
57+ :format(provider, available_pickers)
58+ )
59+
60+ local ok = picker.is_available()
61+ assert(ok, ("Picker `%s` is not available. Make sure it is installed"):format(provider))
62+ return picker.name
2463 end
2564
2665 ---@class leet.Picker
+64-0lua/leetcode/picker/language/snacks.lua
@@ -0,0 +1,64 @@
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+ items = finder_items,
23+ format = function(item)
24+ local ret = {}
25+ vim.tbl_map(function(col)
26+ if type(col) == "table" then
27+ ret[#ret + 1] = col
28+ else
29+ ret[#ret + 1] = { col }
30+ end
31+ ret[#ret + 1] = { " " }
32+ end, item.item.entry)
33+ return ret
34+ end,
35+ title = t("Available Languages"),
36+ layout = {
37+ preview = false,
38+ preset = "select",
39+ layout = {
40+ height = language_picker.height,
41+ width = language_picker.width,
42+ },
43+ },
44+ actions = {
45+ confirm = function(p, item)
46+ if completed then
47+ return
48+ end
49+ completed = true
50+ p:close()
51+ vim.schedule(function()
52+ language_picker.select(item.item.value.t, question, cb)
53+ end)
54+ end,
55+ },
56+ on_close = function()
57+ if completed then
58+ return
59+ end
60+ completed = true
61+ log.warn("No selection")
62+ end,
63+ })
64+end
+1-1lua/leetcode/picker/question/init.lua
@@ -9,7 +9,7 @@ local Picker = require("leetcode.picker")
99 local P = {}
1010
1111 P.width = 100
12-P.height = 20
12+P.height = 0.6
1313
1414 ---@param items lc.cache.Question[]
1515 ---@param opts table<string, string[]>
+64-0lua/leetcode/picker/question/snacks.lua
@@ -0,0 +1,64 @@
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+ items = finder_items,
23+ format = function(item)
24+ local ret = {}
25+ vim.tbl_map(function(col)
26+ if type(col) == "table" then
27+ ret[#ret + 1] = col
28+ else
29+ ret[#ret + 1] = { col }
30+ end
31+ ret[#ret + 1] = { " " }
32+ end, item.item.entry)
33+ return ret
34+ end,
35+ title = t("Select a Question"),
36+ layout = {
37+ preset = "select",
38+ preview = false,
39+ layout = {
40+ height = question_picker.height,
41+ width = question_picker.width,
42+ },
43+ },
44+ actions = {
45+ confirm = function(p, item)
46+ if completed then
47+ return
48+ end
49+ completed = true
50+ p:close()
51+ vim.schedule(function()
52+ question_picker.select(item.item.value)
53+ end)
54+ end,
55+ },
56+ on_close = function()
57+ if completed then
58+ return
59+ end
60+ completed = true
61+ log.warn("No selection")
62+ end,
63+ })
64+end
+66-0lua/leetcode/picker/tabs/snacks.lua
@@ -0,0 +1,66 @@
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+ items = finder_items,
25+ format = function(item)
26+ local ret = {}
27+ vim.tbl_map(function(col)
28+ if type(col) == "table" then
29+ ret[#ret + 1] = col
30+ else
31+ ret[#ret + 1] = { col }
32+ end
33+ ret[#ret + 1] = { " " }
34+ end, item.entry)
35+ return ret
36+ end,
37+ title = t("Select a Question"),
38+ layout = {
39+ preview = false,
40+ preset = "select",
41+ layout = {
42+ height = tabs_picker.height,
43+ width = tabs_picker.width,
44+ },
45+ },
46+ actions = {
47+ confirm = function(p, item)
48+ if completed then
49+ return
50+ end
51+ completed = true
52+ p:close()
53+ vim.schedule(function()
54+ tabs_picker.select(items_reflect[item.item])
55+ end)
56+ end,
57+ },
58+ on_close = function()
59+ if completed then
60+ return
61+ end
62+ completed = true
63+ log.warn("No selection")
64+ end,
65+ })
66+end