irongit

A Neovim plugin enabling you to solve LeetCode problems.

feat: open lang picker when session lang is not available

kawreauthored
parent ebab870commit 556ed7aeb1cd07b26c6e93c538bfa9d93dc42a6eBrowse files

10 files changed, +140 -89

+0-7LICENSE
@@ -1,7 +0,0 @@
1-Copyright 2023 kawre
2-
3-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4-
5-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6-
7-THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+4-0lua/leetcode.lua
@@ -59,6 +59,10 @@ local function start()
5959 setup_highlights()
6060 setup_cmds()
6161
62+ local utils = require("leetcode.utils")
63+ assert(utils.get_lang(config.lang), "Unknown language: " .. config.lang)
64+ assert(utils.get_lang(config.sql), "Unknown sql dialect: " .. config.sql)
65+
6266 require("leetcode-menu"):init()
6367 end
6468
+13-2lua/leetcode/command.lua
@@ -100,12 +100,18 @@ function cmd.question_tabs() require("leetcode.pickers.question-tabs").pick() en
100100 function cmd.change_lang()
101101 local utils = require("leetcode.utils")
102102 local q = utils.curr_question()
103-
104103 if not q then return log.warn("No current question found") end
104+
105105 require("leetcode.pickers.language").pick(q)
106106 end
107107
108-function cmd.desc_toggle() _Lc_questions[_Lc_curr_question].description:toggle() end
108+function cmd.desc_toggle()
109+ local utils = require("leetcode.utils")
110+ local q = utils.curr_question()
111+ if not q then return log.error("No current question found") end
112+
113+ q.description:toggle()
114+end
109115
110116 function cmd.console()
111117 local utils = require("leetcode.utils")
@@ -128,4 +134,9 @@ function cmd.q_submit()
128134 q.console:submit()
129135 end
130136
137+function cmd.fix()
138+ local config = require("leetcode.config")
139+ config.home:rmdir()
140+end
141+
131142 return cmd
+4-0lua/leetcode/config/icons.lua
@@ -0,0 +1,4 @@
1+return {
2+ current = "",
3+ dot = "",
4+}
+3-0lua/leetcode/config/init.lua
@@ -9,8 +9,11 @@ local config = {
99 domain = "https://leetcode.com",
1010 debug = false,
1111 lang = "cpp",
12+ sql = "mysql",
1213 home = {}, ---@type Path
14+
1315 langs = require("leetcode.config.langs"),
16+ icons = require("leetcode.config.icons"),
1417 }
1518
1619 ---@class lc.UserAuth
+2-2lua/leetcode/config/langs.lua
@@ -51,7 +51,7 @@ return {
5151 slug = "swift",
5252 icon = "",
5353 color = "#FFAC45",
54- short = "swft",
54+ short = "swift",
5555 ft = "swift",
5656 },
5757 {
@@ -70,7 +70,7 @@ return {
7070 slug = "scala",
7171 icon = "",
7272 color = "#DC322F",
73- short = "scla",
73+ short = "scala",
7474 ft = "scala",
7575 },
7676 { lang = "Rust", slug = "rust", icon = "", color = "#DEA584", short = "rust", ft = "rs" },
+9-4lua/leetcode/config/template.lua
@@ -18,8 +18,13 @@
1818 ---| "racket"
1919 ---| "erlang"
2020 ---| "elixir"
21+---| "bash"
22+---| "html"
23+---| "pythonml"
24+---| "react"
25+---| "vanillajs"
2126
22----@alias lc.sql_lang
27+---@alias lc.sql
2328 ---| "pythondata"
2429 ---| "mysql"
2530 ---| "mssql"
@@ -41,7 +46,7 @@ local M = {
4146 ---@type lc.lang
4247 lang = "cpp",
4348
44- ---@type lc.sql_lang
49+ ---@type lc.sql
4550 sql = "mysql",
4651
4752 ---@type string
@@ -51,13 +56,13 @@ local M = {
5156 logging = true,
5257
5358 console = {
54- ---@type boolean
55- open_on_runcode = false,
59+ open_on_runcode = false, ---@type boolean
5660
5761 size = {
5862 width = "75%", ---@type string | integer
5963 height = "75%", ---@type string | integer
6064 },
65+
6166 dir = "row", ---@type "col" | "row"
6267 },
6368
+65-37lua/leetcode/pickers/language.lua
@@ -12,6 +12,8 @@ local entry_display = require("telescope.pickers.entry_display")
1212 local actions = require("telescope.actions")
1313 local action_state = require("telescope.actions.state")
1414
15+local M = {}
16+
1517 ---@param snippet lc.QuestionCodeSnippet
1618 ---
1719 ---@return string
@@ -64,40 +66,66 @@ end
6466
6567 local opts = require("telescope.themes").get_dropdown()
6668
67-return {
68- ---@param question lc.Question
69- pick = function(question)
70- pickers
71- .new(opts, {
72- prompt_title = "Available Languages",
73- finder = finders.new_table({
74- results = question.q.code_snippets,
75- entry_maker = entry_maker,
76- }),
77- sorter = conf.generic_sorter(opts),
78- attach_mappings = function(prompt_bufnr, map)
79- actions.select_default:replace(function()
80- local selection = action_state.get_selected_entry()
81- if not selection then return end
82- local snippet = selection.value
83-
84- if question.lang == snippet.t.slug then
85- log.warn("Language already set to: " .. snippet.t.lang)
86- return
87- end
88-
89- if snippet.t.sql then
90- config.sql = snippet.t.slug
91- else
92- config.lang = snippet.t.slug
93- end
94-
95- actions.close(prompt_bufnr)
96- Question:init(question.cache)
97- end)
98- return true
99- end,
100- })
101- :find()
102- end,
103-}
69+---@param question lc.Question
70+M.pick_lang = function(question, callback)
71+ pickers
72+ .new(opts, {
73+ prompt_title = "Available Languages",
74+ finder = finders.new_table({
75+ results = question.q.code_snippets,
76+ entry_maker = entry_maker,
77+ }),
78+ sorter = conf.generic_sorter(opts),
79+ attach_mappings = function(prompt_bufnr, map)
80+ actions.select_default:replace(function()
81+ local selection = action_state.get_selected_entry()
82+ if not selection then return end
83+
84+ local snippet = selection.value
85+ if question.lang == snippet.t.slug then
86+ return log.warn("Language already set to: " .. snippet.t.lang)
87+ end
88+
89+ config[snippet.t.sql and "sql" or "lang"] = snippet.t.slug
90+ actions.close(prompt_bufnr)
91+ callback(snippet)
92+ end)
93+
94+ return true
95+ end,
96+ })
97+ :find()
98+end
99+
100+---@param question lc.Question
101+M.pick = function(question)
102+ pickers
103+ .new(opts, {
104+ prompt_title = "Available Languages",
105+ finder = finders.new_table({
106+ results = question.q.code_snippets,
107+ entry_maker = entry_maker,
108+ }),
109+ sorter = conf.generic_sorter(opts),
110+ attach_mappings = function(prompt_bufnr, map)
111+ actions.select_default:replace(function()
112+ local selection = action_state.get_selected_entry()
113+ if not selection then return end
114+
115+ local snippet = selection.value
116+ if question.lang == snippet.t.slug then
117+ return log.warn("Language already set to: " .. snippet.t.lang)
118+ end
119+
120+ config[snippet.t.sql and "sql" or "lang"] = snippet.t.slug
121+ actions.close(prompt_bufnr)
122+ Question:init(question.cache)
123+ end)
124+
125+ return true
126+ end,
127+ })
128+ :find()
129+end
130+
131+return M
+0-3lua/leetcode/pickers/question-tabs.lua
@@ -1,12 +1,9 @@
11 local log = require("leetcode.logger")
22 local utils = require("leetcode.utils")
33
4-local Question = require("leetcode.ui.question")
5-
64 local pickers = require("telescope.pickers")
75 local finders = require("telescope.finders")
86 local conf = require("telescope.config").values
9-local config = require("leetcode.config")
107
118 local entry_display = require("telescope.pickers.entry_display")
129 local actions = require("telescope.actions")
+40-34lua/leetcode/ui/question.lua
@@ -4,6 +4,7 @@ local Description = require("leetcode.ui.description")
44 local api_question = require("leetcode.api.question")
55 local Console = require("leetcode.ui.console")
66 local utils = require("leetcode.utils")
7+local async = require("plenary.async")
78
89 ---@class lc.Question
910 ---@field file Path
@@ -22,29 +23,37 @@ _Lc_questions = {}
2223 ---@type integer
2324 _Lc_curr_question = 0
2425
26+---@param q lc.QuestionResponse
27+---
28+---@return boolean
29+local function is_sql(q)
30+ for _, value in ipairs(q.topic_tags or {}) do
31+ if value.slug == "database" then return true end
32+ end
33+
34+ return false
35+end
36+
37+function Question:get_snippet()
38+ local snippets = self.q.code_snippets ~= vim.NIL and self.q.code_snippets or {}
39+ return vim.tbl_filter(function(snip) return snip.lang_slug == self.lang end, snippets)[1]
40+end
41+
2542 ---@private
2643 function Question:create_file()
27- local snippets = self.q.code_snippets
28- local snippet = {}
29-
30- for _, snip in pairs(snippets ~= vim.NIL and snippets or {}) do
31- if snip.lang_slug == self.lang then
32- snippet.code = snip.code
33- snippet.lang = snip.lang_slug
34- break
35- end
36- end
44+ local lang = utils.get_lang(is_sql(self.q) and config.sql or config.lang)
45+ local suffix = lang.sql and "-" .. lang.short or ""
46+ local fn = string.format("%s.%s%s.%s", self.q.frontend_id, self.q.title_slug, suffix, lang.ft)
3747
38- if not snippet then return log.error("failed to fetch code snippet") end
39- self.file:write(snippet.code, "w")
48+ self.file = config.home:joinpath(fn)
4049 end
4150
4251 function Question:mount()
43- if not self.file:exists() then self:create_file() end
52+ self:create_file()
53+ if not self.file:exists() then self.file:write(self:get_snippet().code, "w") end
4454
45- vim.api.nvim_set_current_dir(self.file:parent().filename)
55+ vim.api.nvim_set_current_dir(config.home:absolute())
4656 vim.cmd("$tabe " .. self.file:absolute())
47- _Lc_tabpage = vim.api.nvim_get_current_tabpage()
4857
4958 self.bufnr = vim.api.nvim_get_current_buf()
5059 _Lc_curr_question = self.bufnr
@@ -52,6 +61,7 @@ function Question:mount()
5261
5362 self.description = Description:init(self)
5463 self.console = Console:init(self)
64+
5565 self:autocmds()
5666 return self
5767 end
@@ -84,44 +94,40 @@ function Question:autocmds()
8494 })
8595 end
8696
87----@param q lc.QuestionResponse
88----
89----@return boolean
90-local function is_sql(q)
91- for _, value in ipairs(q.topic_tags or {}) do
92- if value.slug == "database" then return true end
97+function Question:handle_mount()
98+ if self:get_snippet() then
99+ self:mount()
100+ else
101+ local msg =
102+ string.format("Snippet for `%s` not found. Select a different language", self.lang)
103+ log.warn(msg)
104+
105+ require("leetcode.pickers.language").pick_lang(self, function(snippet)
106+ self.lang = snippet.t.slug
107+ self:mount()
108+ end)
93109 end
94-
95- return false
96110 end
97111
98112 ---@param problem lc.Cache.Question
99113 function Question:init(problem)
100114 local tabp = utils.detect_duplicate_question(problem.title_slug)
101- if tabp then
102- pcall(vim.cmd.tabnext, tabp)
103- return
104- end
115+ if tabp then return pcall(vim.cmd.tabnext, tabp) end
105116
106117 local q = api_question.by_title_slug(problem.title_slug)
107118 if q.is_paid_only and not config.auth.is_premium then
108- log.warn("Question is for premium users only")
109- return
119+ return log.warn("Question is for premium users only")
110120 end
111121
112122 local lang = utils.get_lang(is_sql(q) and config.sql or config.lang)
113- local suffix = lang.sql and "-" .. lang.short or ""
114- local fn = string.format("%s.%s%s.%s", q.frontend_id, q.title_slug, suffix, lang.ft)
115- local file = config.home:joinpath(fn)
116123
117124 local obj = setmetatable({
118- file = file,
119125 q = q,
120126 lang = lang.slug,
121127 cache = problem,
122128 }, self)
123129
124- return obj:mount()
130+ return obj:handle_mount()
125131 end
126132
127133 return Question