irongit

A Neovim plugin enabling you to solve LeetCode problems.

feat: `non_standalone` plugin (#51)

Will Hopkinsauthored committed by GitHub
parent fe51320commit 7bd3e3d54b462ce09431c6fea0eb63eb3c1f0f9aBrowse files

18 files changed, +248 -30

+1-0.stylua.toml
@@ -4,4 +4,5 @@ indent_type = "Spaces"
44 indent_width = 4
55 quote_style = "ForceDouble"
66 call_parentheses = "Always"
7+# collapse_simple_statement = "FunctionOnly"
78 collapse_simple_statement = "Always"
+39-0README.md
@@ -93,6 +93,11 @@ To see full configuration types see [template.lua](./lua/leetcode/config/templat
9393 cache = vim.fn.stdpath("cache") .. "/leetcode",
9494 },
9595
96+ ---@type table<string, boolean>
97+ plugins = {
98+ non_standalone = false,
99+ },
100+
96101 ---@type boolean
97102 logging = true,
98103
@@ -137,6 +142,9 @@ To see full configuration types see [template.lua](./lua/leetcode/config/templat
137142
138143 ---@type fun(question: lc.ui.Question)[]
139144 ["question_enter"] = {},
145+
146+ ---@type fun()[]
147+ ["leave"] = {},
140148 },
141149
142150 keys = {
@@ -226,6 +234,17 @@ storage = {
226234 },
227235 ```
228236
237+### plugins
238+
239+[plugins list](#-plugins)
240+
241+```lua
242+---@type table<string, boolean>
243+plugins = {
244+ non_standalone = false,
245+},
246+```
247+
229248 ### logging
230249
231250 Whether to log [leetcode.nvim] status notifications
@@ -272,6 +291,9 @@ hooks = {
272291
273292 ---@type fun(question: lc.ui.Question)[]
274293 ["question_enter"] = {},
294+
295+ ---@type fun()[]
296+ ["leave"] = {},
275297 },
276298 ```
277299
@@ -294,6 +316,8 @@ image_support = false,
294316
295317 - `menu` same as `Leet`
296318
319+- `exit` close [leetcode.nvim]
320+
297321 - `console` opens console pop-up for currently opened question
298322
299323 - `info` opens a pop-up containing information about the currently opened question
@@ -419,6 +443,21 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/b7be8b95-5e2c-4153-8845-4
419443 }
420444 ```
421445
446+## 🧩 Plugins
447+
448+### Non-Standalone mode
449+
450+To run [leetcode.nvim] in a non-standalone mode (i.e. not with argument or an empty Neovim session),
451+enable the `non_standalone` plugin in your config:
452+
453+```lua
454+plugins = {
455+ non_standalone = true,
456+}
457+```
458+
459+You can then exit [leetcode.nvim] using `:Leet exit` command
460+
422461 ## 🙌 Credits
423462
424463 - [Leetbuddy.nvim](https://github.com/Dhanus3133/Leetbuddy.nvim)
+39-0README.zh.md
@@ -95,6 +95,11 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/aee6584c-e099-4409-b114-1
9595 cache = vim.fn.stdpath("cache") .. "/leetcode",
9696 },
9797
98+ ---@type table<string, boolean>
99+ plugins = {
100+ non_standalone = false,
101+ },
102+
98103 ---@type boolean
99104 logging = true,
100105
@@ -139,6 +144,9 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/aee6584c-e099-4409-b114-1
139144
140145 ---@type fun(question: lc.ui.Question)[]
141146 ["question_enter"] = {},
147+
148+ ---@type fun()[]
149+ ["leave"] = {},
142150 },
143151
144152 keys = {
@@ -228,6 +236,17 @@ storage = {
228236 },
229237 ```
230238
239+### plugins
240+
241+[插件列表](#-plugins)
242+
243+```lua
244+---@type table<string, boolean>
245+plugins = {
246+ non_standalone = false,
247+},
248+```
249+
231250 ### logging
232251
233252 是否记录 [leetcode.nvim] 状态通知
@@ -271,6 +290,9 @@ hooks = {
271290
272291 ---@type fun(question: lc.ui.Question)[]
273292 ["question_enter"] = {},
293+
294+ ---@type fun()[]
295+ ["leave"] = {},
274296 },
275297 ```
276298
@@ -293,6 +315,8 @@ image_support = false, -- 将此设置为 `true` 将禁用问题描述的换行
293315
294316 - `menu` 与 `Leet` 相同
295317
318+- `exit` 关闭 [leetcode.nvim]
319+
296320 - `console` 打开当前打开问题的控制台弹出窗口
297321
298322 - `info` 打开包含当前打开问题信息的弹出窗口
@@ -417,6 +441,21 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/b7be8b95-5e2c-4153-8845-4
417441 }
418442 ```
419443
444+## 🧩 Plugins
445+
446+### Non-Standalone mode
447+
448+要在非独立模式下运行 [leetcode.nvim](即不带参数或在空的 Neovim 会话中运行),
449+请在您的配置中启用 `non_standalone` 插件:
450+
451+```lua
452+plugins = {
453+ non_standalone = true,
454+}
455+```
456+
457+你可以使用 `:Leet exit` 命令退出 [leetcode.nvim]
458+
420459 ## 🙌 鸣谢
421460
422461 - [Leetbuddy.nvim](https://github.com/Dhanus3133/Leetbuddy.nvim)
+12-0lua/leetcode-plugins/non_standalone/init.lua
@@ -0,0 +1,12 @@
1+---@class lc.plugins.non_standalone
2+local non_standalone = {}
3+
4+non_standalone.opts = {
5+ lazy = false,
6+}
7+
8+function non_standalone.load() --
9+ require("leetcode-plugins.non_standalone.leetcode")
10+end
11+
12+return non_standalone
+87-0lua/leetcode-plugins/non_standalone/leetcode.lua
@@ -0,0 +1,87 @@
1+local leetcode = require("leetcode")
2+local config = require("leetcode.config")
3+
4+local standalone = true
5+local prev_cwd = nil
6+
7+---@param on_vimenter boolean
8+leetcode.should_skip = function(on_vimenter)
9+ if on_vimenter then
10+ if vim.fn.argc() ~= 1 then return true end
11+
12+ local usr_arg, arg = vim.fn.argv()[1], config.user.arg
13+ if usr_arg ~= arg then return true end
14+
15+ local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true)
16+ if #lines > 1 or (#lines == 1 and lines[1]:len() > 0) then
17+ local log = require("leetcode.logger")
18+ log.warn(("Failed to initialize: `%s` is not an empty buffer"):format(usr_arg))
19+ return true
20+ end
21+ else
22+ for _, buf_id in pairs(vim.api.nvim_list_bufs()) do
23+ local bufinfo = vim.fn.getbufinfo(buf_id)[1]
24+ if bufinfo and (bufinfo.listed == 1 and #bufinfo.windows > 0) then --
25+ return false, true
26+ end
27+ end
28+ end
29+
30+ return false
31+end
32+
33+---@param on_vimenter boolean
34+leetcode.start = function(on_vimenter)
35+ local skip, buflisted = leetcode.should_skip(on_vimenter)
36+ if skip then --
37+ return false
38+ end
39+
40+ config.setup()
41+
42+ leetcode.setup_cmds()
43+
44+ local theme = require("leetcode.theme")
45+ theme.setup()
46+
47+ if not on_vimenter then --
48+ if buflisted then
49+ prev_cwd = vim.fn.getcwd()
50+ vim.cmd.tabe()
51+ else
52+ vim.cmd.enew()
53+ end
54+
55+ standalone = not buflisted
56+ end
57+
58+ vim.api.nvim_set_current_dir(config.storage.home:absolute())
59+
60+ local Menu = require("leetcode-ui.renderer.menu")
61+ Menu():mount()
62+
63+ local utils = require("leetcode.utils")
64+ utils.exec_hooks("enter")
65+
66+ return true
67+end
68+
69+leetcode.stop = vim.schedule_wrap(function()
70+ if standalone then return vim.cmd("qa!") end
71+
72+ _Lc_state.menu:unmount()
73+
74+ vim.api.nvim_create_user_command("Leet", require("leetcode.command").start_with_cmd, {
75+ bar = true,
76+ bang = true,
77+ desc = "Open leetcode.nvim",
78+ })
79+
80+ local utils = require("leetcode.utils")
81+ utils.exec_hooks("leave")
82+
83+ if prev_cwd then
84+ vim.api.nvim_set_current_dir(prev_cwd)
85+ prev_cwd = nil
86+ end
87+end)
+2-1lua/leetcode-ui/lines/button/menu/exit.lua
@@ -1,4 +1,5 @@
11 local MenuButton = require("leetcode-ui.lines.button.menu")
2+local leetcode = require("leetcode")
23
34 ---@class lc.ui.Button.Menu.Exit : lc.ui.Button.Menu
45 local MenuExitButton = MenuButton:extend("LeetMenuExitButton")
@@ -8,7 +9,7 @@ function MenuExitButton:init()
89 MenuExitButton.super.init(self, "Exit", {
910 icon = "󰩈",
1011 sc = "q",
11- on_press = function() vim.cmd("qa!") end,
12+ on_press = leetcode.stop,
1213 })
1314 end
1415
+1-1lua/leetcode-ui/lines/calendar.lua
@@ -83,7 +83,7 @@ function Calendar:handle_res(res)
8383 self:append(line):endl()
8484 end
8585
86- _Lc_menu:draw()
86+ _Lc_state.menu:draw()
8787 end
8888
8989 function Calendar:handle_submissions()
+1-1lua/leetcode-ui/lines/solved.lua
@@ -76,7 +76,7 @@ function Solved:handle_res(res)
7676 self:endl()
7777 end
7878
79- _Lc_menu:draw()
79+ _Lc_state.menu:draw()
8080 end
8181
8282 function Solved:update()
+8-7lua/leetcode-ui/question.lua
@@ -152,7 +152,7 @@ function Question:injector(code)
152152 return table.concat(parts, "\n")
153153 end
154154
155-function Question:unmount()
155+function Question:_unmount()
156156 if vim.v.dying ~= 0 then --
157157 return
158158 end
@@ -166,15 +166,16 @@ function Question:unmount()
166166 vim.api.nvim_buf_delete(self.bufnr, { force = true, unload = false })
167167 end
168168
169- _Lc_questions = vim.tbl_filter(function(q) --
170- return q.bufnr ~= self.bufnr
171- end, _Lc_questions)
169+ _Lc_state.questions = vim.tbl_filter(
170+ function(q) return q.bufnr ~= self.bufnr end,
171+ _Lc_state.questions
172+ )
172173
173174 self = nil
174175 end)
175176 end
176177
177-function Question:_unmount()
178+function Question:unmount()
178179 if vim.api.nvim_win_is_valid(self.winid) then vim.api.nvim_win_close(self.winid, true) end
179180 end
180181
@@ -183,14 +184,14 @@ function Question:autocmds()
183184 vim.api.nvim_create_autocmd("WinClosed", {
184185 group = group,
185186 pattern = tostring(self.winid),
186- callback = function() self:unmount() end,
187+ callback = function() self:_unmount() end,
187188 })
188189 end
189190
190191 function Question:handle_mount()
191192 self:create_buffer()
192193
193- table.insert(_Lc_questions, self)
194+ table.insert(_Lc_state.questions, self)
194195
195196 self.description = Description(self):mount()
196197 self.console = Console(self)
+2-0lua/leetcode-ui/renderer/init.lua
@@ -24,6 +24,8 @@ function Renderer:draw(component)
2424 self.bufnr = component.bufnr
2525 self.winid = component.winid
2626
27+ if not vim.api.nvim_buf_is_valid(self.bufnr) then return end
28+
2729 self:map("n", keys.confirm, function() self:handle_press() end)
2830
2931 self:clear_keymaps()
+19-1lua/leetcode-ui/renderer/menu.lua
@@ -137,6 +137,24 @@ function Menu:apply_options()
137137 })
138138 end
139139
140+function Menu:unmount()
141+ if vim.v.dying ~= 0 then --
142+ return
143+ end
144+
145+ require("leetcode.command").q_close_all()
146+
147+ if self.winid and vim.api.nvim_win_is_valid(self.winid) then
148+ vim.api.nvim_win_close(self.winid, true)
149+ end
150+
151+ vim.schedule(function()
152+ if self.bufnr and vim.api.nvim_buf_is_valid(self.bufnr) then
153+ vim.api.nvim_buf_delete(self.bufnr, { force = true })
154+ end
155+ end)
156+end
157+
140158 function Menu:remount()
141159 if self.winid and api.nvim_win_is_valid(self.winid) then --
142160 api.nvim_win_close(self.winid, true)
@@ -195,7 +213,7 @@ function Menu:init()
195213 self.bufnr = api.nvim_get_current_buf()
196214 self.winid = api.nvim_get_current_win()
197215
198- _Lc_menu = self
216+ _Lc_state.menu = self
199217 end
200218
201219 ---@type fun(): lc.ui.Menu
+3-3lua/leetcode.lua
@@ -37,9 +37,7 @@ function leetcode.setup_cmds() require("leetcode.command").setup() end
3737
3838 ---@param on_vimenter boolean
3939 function leetcode.start(on_vimenter)
40- if leetcode.should_skip(on_vimenter) then --
41- return false
42- end
40+ if leetcode.should_skip(on_vimenter) then return false end
4341
4442 config.setup()
4543
@@ -63,6 +61,8 @@ function leetcode.start(on_vimenter)
6361 return true
6462 end
6563
64+function leetcode.stop() vim.cmd("qa!") end
65+
6666 ---@param cfg? lc.UserConfig
6767 function leetcode.setup(cfg)
6868 config.apply(cfg or {})
+12-4lua/leetcode/command/init.lua
@@ -95,10 +95,15 @@ cmd.q_close_all = function()
9595 local qs = utils.question_tabs()
9696
9797 for _, tabp in ipairs(qs) do
98- tabp.question:_unmount()
98+ tabp.question:unmount()
9999 end
100100 end
101101
102+function cmd.exit()
103+ local leetcode = require("leetcode")
104+ leetcode.stop()
105+end
106+
102107 cmd.expire = vim.schedule_wrap(function()
103108 local tabp = api.nvim_get_current_tabpage()
104109 cmd.menu()
@@ -160,12 +165,14 @@ function cmd.start_with_cmd()
160165 end
161166
162167 function cmd.menu()
163- local ok, tabp = pcall(api.nvim_win_get_tabpage, _Lc_menu.winid)
168+ local winid, bufnr = _Lc_state.menu.winid, _Lc_state.menu.bufnr
169+ local ok, tabp = pcall(api.nvim_win_get_tabpage, winid)
164170
165171 if ok then
166172 api.nvim_set_current_tabpage(tabp)
173+ api.nvim_win_set_buf(winid, bufnr)
167174 else
168- _Lc_menu:remount()
175+ _Lc_state.menu:remount()
169176 end
170177 end
171178
@@ -184,7 +191,7 @@ function cmd.yank()
184191 end
185192
186193 ---@param page lc-menu.page
187-function cmd.set_menu_page(page) _Lc_menu:set_page(page) end
194+function cmd.set_menu_page(page) _Lc_state.menu:set_page(page) end
188195
189196 function cmd.start_user_session() --
190197 cmd.set_menu_page("menu")
@@ -525,6 +532,7 @@ cmd.commands = {
525532 cmd.menu,
526533
527534 menu = { cmd.menu },
535+ exit = { cmd.exit },
528536 console = { cmd.console },
529537 info = { cmd.info },
530538 hints = { cmd.hints },
+4-5lua/leetcode/config/init.lua
@@ -1,11 +1,10 @@
11 local template = require("leetcode.config.template")
22 local P = require("plenary.path")
33
4----@type lc.ui.Question[]
5-_Lc_questions = {}
6-
7----@type lc.ui.Menu
8-_Lc_menu = {} ---@diagnostic disable-line
4+_Lc_state = {
5+ menu = nil, ---@type lc.ui.Menu
6+ questions = {}, ---@type lc.ui.Question[]
7+}
98
109 local lazy_plugs = {}
1110
+5-4lua/leetcode/config/stats.lua
@@ -14,7 +14,7 @@ function Stats.update_streak()
1414 Stats.daily.streak = res.streakCount
1515 Stats.daily.today_completed = res.todayCompleted
1616
17- if _Lc_menu then _Lc_menu:draw() end
17+ _Lc_state.menu:draw()
1818 end)
1919 end
2020
@@ -26,7 +26,8 @@ function Stats.update_sessions()
2626
2727 stats_api.sessions(function(_, err)
2828 if err then return log.err(err) end
29- if _Lc_menu then _Lc_menu:draw() end
29+
30+ _Lc_state.menu:draw()
3031 end)
3132
3233 stats_api.session_progress(function(res, err)
@@ -39,7 +40,7 @@ function Stats.update_sessions()
3940 Stats.progress[p.difficulty:lower()] = p
4041 end
4142
42- if _Lc_menu then _Lc_menu:draw() end
43+ _Lc_state.menu:draw()
4344 end)
4445 end
4546
@@ -47,7 +48,7 @@ function Stats.update()
4748 Stats.update_streak()
4849 Stats.update_sessions()
4950
50- if _Lc_menu then _Lc_menu:draw() end
51+ _Lc_state.menu:draw()
5152 end
5253
5354 return Stats
+8-1lua/leetcode/config/template.lua
@@ -23,6 +23,7 @@
2323 ---@alias lc.hook
2424 ---| "enter"
2525 ---| "question_enter"
26+---| "leave"
2627
2728 ---@alias lc.size
2829 ---| string
@@ -57,7 +58,10 @@ local M = {
5758 cache = vim.fn.stdpath("cache") .. "/leetcode",
5859 },
5960
60- plugins = {},
61+ ---@type table<string, boolean>
62+ plugins = {
63+ non_standalone = false,
64+ },
6165
6266 ---@type boolean
6367 logging = true,
@@ -103,6 +107,9 @@ local M = {
103107
104108 ---@type fun(question: lc.ui.Question)[]
105109 ["question_enter"] = {},
110+
111+ ---@type fun()[]
112+ ["leave"] = {},
106113 },
107114
108115 keys = {
+4-1lua/leetcode/logger/init.lua
@@ -36,7 +36,10 @@ logger.info = function(msg) logger.log(msg) end
3636 logger.warn = function(msg) logger.log(msg, lvls.WARN) end
3737
3838 ---@param msg any
39-logger.error = function(msg) logger.log(msg, lvls.ERROR) end
39+logger.error = function(msg)
40+ logger.log(msg, lvls.ERROR)
41+ logger.debug(msg)
42+end
4043
4144 ---@param err lc.err
4245 logger.err = function(err)
+1-1lua/leetcode/utils.lua
@@ -31,7 +31,7 @@ end
3131 function utils.question_tabs()
3232 local questions = {}
3333
34- for _, q in ipairs(_Lc_questions) do
34+ for _, q in ipairs(_Lc_state.questions) do
3535 local tabp = utils.question_tabp(q)
3636 if tabp then table.insert(questions, { tabpage = tabp, question = q }) end
3737 end