diff --git a/.stylua.toml b/.stylua.toml index e9668dde..1af79332 100644 --- a/.stylua.toml +++ b/.stylua.toml @@ -4,4 +4,5 @@ indent_type = "Spaces" indent_width = 4 quote_style = "ForceDouble" call_parentheses = "Always" +# collapse_simple_statement = "FunctionOnly" collapse_simple_statement = "Always" diff --git a/README.md b/README.md index fe62ef0e..afb7ecb0 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,11 @@ To see full configuration types see [template.lua](./lua/leetcode/config/templat cache = vim.fn.stdpath("cache") .. "/leetcode", }, + ---@type table + plugins = { + non_standalone = false, + }, + ---@type boolean logging = true, @@ -137,6 +142,9 @@ To see full configuration types see [template.lua](./lua/leetcode/config/templat ---@type fun(question: lc.ui.Question)[] ["question_enter"] = {}, + + ---@type fun()[] + ["leave"] = {}, }, keys = { @@ -226,6 +234,17 @@ storage = { }, ``` +### plugins + +[plugins list](#-plugins) + +```lua +---@type table +plugins = { + non_standalone = false, +}, +``` + ### logging Whether to log [leetcode.nvim] status notifications @@ -272,6 +291,9 @@ hooks = { ---@type fun(question: lc.ui.Question)[] ["question_enter"] = {}, + + ---@type fun()[] + ["leave"] = {}, }, ``` @@ -294,6 +316,8 @@ image_support = false, - `menu` same as `Leet` +- `exit` close [leetcode.nvim] + - `console` opens console pop-up for currently opened question - `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 } ``` +## 🧩 Plugins + +### Non-Standalone mode + +To run [leetcode.nvim] in a non-standalone mode (i.e. not with argument or an empty Neovim session), +enable the `non_standalone` plugin in your config: + +```lua +plugins = { + non_standalone = true, +} +``` + +You can then exit [leetcode.nvim] using `:Leet exit` command + ## 🙌 Credits - [Leetbuddy.nvim](https://github.com/Dhanus3133/Leetbuddy.nvim) diff --git a/README.zh.md b/README.zh.md index 12926722..5ce0a7b9 100644 --- a/README.zh.md +++ b/README.zh.md @@ -95,6 +95,11 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/aee6584c-e099-4409-b114-1 cache = vim.fn.stdpath("cache") .. "/leetcode", }, + ---@type table + plugins = { + non_standalone = false, + }, + ---@type boolean logging = true, @@ -139,6 +144,9 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/aee6584c-e099-4409-b114-1 ---@type fun(question: lc.ui.Question)[] ["question_enter"] = {}, + + ---@type fun()[] + ["leave"] = {}, }, keys = { @@ -228,6 +236,17 @@ storage = { }, ``` +### plugins + +[插件列表](#-plugins) + +```lua +---@type table +plugins = { + non_standalone = false, +}, +``` + ### logging 是否记录 [leetcode.nvim] 状态通知 @@ -271,6 +290,9 @@ hooks = { ---@type fun(question: lc.ui.Question)[] ["question_enter"] = {}, + + ---@type fun()[] + ["leave"] = {}, }, ``` @@ -293,6 +315,8 @@ image_support = false, -- 将此设置为 `true` 将禁用问题描述的换行 - `menu` 与 `Leet` 相同 +- `exit` 关闭 [leetcode.nvim] + - `console` 打开当前打开问题的控制台弹出窗口 - `info` 打开包含当前打开问题信息的弹出窗口 @@ -417,6 +441,21 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/b7be8b95-5e2c-4153-8845-4 } ``` +## 🧩 Plugins + +### Non-Standalone mode + +要在非独立模式下运行 [leetcode.nvim](即不带参数或在空的 Neovim 会话中运行), +请在您的配置中启用 `non_standalone` 插件: + +```lua +plugins = { + non_standalone = true, +} +``` + +你可以使用 `:Leet exit` 命令退出 [leetcode.nvim] + ## 🙌 鸣谢 - [Leetbuddy.nvim](https://github.com/Dhanus3133/Leetbuddy.nvim) diff --git a/lua/leetcode-plugins/non_standalone/init.lua b/lua/leetcode-plugins/non_standalone/init.lua new file mode 100644 index 00000000..709b7c80 --- /dev/null +++ b/lua/leetcode-plugins/non_standalone/init.lua @@ -0,0 +1,12 @@ +---@class lc.plugins.non_standalone +local non_standalone = {} + +non_standalone.opts = { + lazy = false, +} + +function non_standalone.load() -- + require("leetcode-plugins.non_standalone.leetcode") +end + +return non_standalone diff --git a/lua/leetcode-plugins/non_standalone/leetcode.lua b/lua/leetcode-plugins/non_standalone/leetcode.lua new file mode 100644 index 00000000..2c9df97c --- /dev/null +++ b/lua/leetcode-plugins/non_standalone/leetcode.lua @@ -0,0 +1,87 @@ +local leetcode = require("leetcode") +local config = require("leetcode.config") + +local standalone = true +local prev_cwd = nil + +---@param on_vimenter boolean +leetcode.should_skip = function(on_vimenter) + if on_vimenter then + if vim.fn.argc() ~= 1 then return true end + + local usr_arg, arg = vim.fn.argv()[1], config.user.arg + if usr_arg ~= arg then return true end + + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true) + if #lines > 1 or (#lines == 1 and lines[1]:len() > 0) then + local log = require("leetcode.logger") + log.warn(("Failed to initialize: `%s` is not an empty buffer"):format(usr_arg)) + return true + end + else + for _, buf_id in pairs(vim.api.nvim_list_bufs()) do + local bufinfo = vim.fn.getbufinfo(buf_id)[1] + if bufinfo and (bufinfo.listed == 1 and #bufinfo.windows > 0) then -- + return false, true + end + end + end + + return false +end + +---@param on_vimenter boolean +leetcode.start = function(on_vimenter) + local skip, buflisted = leetcode.should_skip(on_vimenter) + if skip then -- + return false + end + + config.setup() + + leetcode.setup_cmds() + + local theme = require("leetcode.theme") + theme.setup() + + if not on_vimenter then -- + if buflisted then + prev_cwd = vim.fn.getcwd() + vim.cmd.tabe() + else + vim.cmd.enew() + end + + standalone = not buflisted + end + + vim.api.nvim_set_current_dir(config.storage.home:absolute()) + + local Menu = require("leetcode-ui.renderer.menu") + Menu():mount() + + local utils = require("leetcode.utils") + utils.exec_hooks("enter") + + return true +end + +leetcode.stop = vim.schedule_wrap(function() + if standalone then return vim.cmd("qa!") end + + _Lc_state.menu:unmount() + + vim.api.nvim_create_user_command("Leet", require("leetcode.command").start_with_cmd, { + bar = true, + bang = true, + desc = "Open leetcode.nvim", + }) + + local utils = require("leetcode.utils") + utils.exec_hooks("leave") + + if prev_cwd then + vim.api.nvim_set_current_dir(prev_cwd) + prev_cwd = nil + end +end) diff --git a/lua/leetcode-ui/lines/button/menu/exit.lua b/lua/leetcode-ui/lines/button/menu/exit.lua index c9e16dc7..566dfb04 100644 --- a/lua/leetcode-ui/lines/button/menu/exit.lua +++ b/lua/leetcode-ui/lines/button/menu/exit.lua @@ -1,4 +1,5 @@ local MenuButton = require("leetcode-ui.lines.button.menu") +local leetcode = require("leetcode") ---@class lc.ui.Button.Menu.Exit : lc.ui.Button.Menu local MenuExitButton = MenuButton:extend("LeetMenuExitButton") @@ -8,7 +9,7 @@ function MenuExitButton:init() MenuExitButton.super.init(self, "Exit", { icon = "󰩈", sc = "q", - on_press = function() vim.cmd("qa!") end, + on_press = leetcode.stop, }) end diff --git a/lua/leetcode-ui/lines/calendar.lua b/lua/leetcode-ui/lines/calendar.lua index 86ee7345..cb482114 100644 --- a/lua/leetcode-ui/lines/calendar.lua +++ b/lua/leetcode-ui/lines/calendar.lua @@ -83,7 +83,7 @@ function Calendar:handle_res(res) self:append(line):endl() end - _Lc_menu:draw() + _Lc_state.menu:draw() end function Calendar:handle_submissions() diff --git a/lua/leetcode-ui/lines/solved.lua b/lua/leetcode-ui/lines/solved.lua index 17c6571c..70f9eb53 100644 --- a/lua/leetcode-ui/lines/solved.lua +++ b/lua/leetcode-ui/lines/solved.lua @@ -76,7 +76,7 @@ function Solved:handle_res(res) self:endl() end - _Lc_menu:draw() + _Lc_state.menu:draw() end function Solved:update() diff --git a/lua/leetcode-ui/question.lua b/lua/leetcode-ui/question.lua index 53ae5c3e..44ab426d 100644 --- a/lua/leetcode-ui/question.lua +++ b/lua/leetcode-ui/question.lua @@ -152,7 +152,7 @@ function Question:injector(code) return table.concat(parts, "\n") end -function Question:unmount() +function Question:_unmount() if vim.v.dying ~= 0 then -- return end @@ -166,15 +166,16 @@ function Question:unmount() vim.api.nvim_buf_delete(self.bufnr, { force = true, unload = false }) end - _Lc_questions = vim.tbl_filter(function(q) -- - return q.bufnr ~= self.bufnr - end, _Lc_questions) + _Lc_state.questions = vim.tbl_filter( + function(q) return q.bufnr ~= self.bufnr end, + _Lc_state.questions + ) self = nil end) end -function Question:_unmount() +function Question:unmount() if vim.api.nvim_win_is_valid(self.winid) then vim.api.nvim_win_close(self.winid, true) end end @@ -183,14 +184,14 @@ function Question:autocmds() vim.api.nvim_create_autocmd("WinClosed", { group = group, pattern = tostring(self.winid), - callback = function() self:unmount() end, + callback = function() self:_unmount() end, }) end function Question:handle_mount() self:create_buffer() - table.insert(_Lc_questions, self) + table.insert(_Lc_state.questions, self) self.description = Description(self):mount() self.console = Console(self) diff --git a/lua/leetcode-ui/renderer/init.lua b/lua/leetcode-ui/renderer/init.lua index ac2cf489..e5ce85aa 100644 --- a/lua/leetcode-ui/renderer/init.lua +++ b/lua/leetcode-ui/renderer/init.lua @@ -24,6 +24,8 @@ function Renderer:draw(component) self.bufnr = component.bufnr self.winid = component.winid + if not vim.api.nvim_buf_is_valid(self.bufnr) then return end + self:map("n", keys.confirm, function() self:handle_press() end) self:clear_keymaps() diff --git a/lua/leetcode-ui/renderer/menu.lua b/lua/leetcode-ui/renderer/menu.lua index b143992f..784fbf08 100644 --- a/lua/leetcode-ui/renderer/menu.lua +++ b/lua/leetcode-ui/renderer/menu.lua @@ -137,6 +137,24 @@ function Menu:apply_options() }) end +function Menu:unmount() + if vim.v.dying ~= 0 then -- + return + end + + require("leetcode.command").q_close_all() + + if self.winid and vim.api.nvim_win_is_valid(self.winid) then + vim.api.nvim_win_close(self.winid, true) + end + + vim.schedule(function() + if self.bufnr and vim.api.nvim_buf_is_valid(self.bufnr) then + vim.api.nvim_buf_delete(self.bufnr, { force = true }) + end + end) +end + function Menu:remount() if self.winid and api.nvim_win_is_valid(self.winid) then -- api.nvim_win_close(self.winid, true) @@ -195,7 +213,7 @@ function Menu:init() self.bufnr = api.nvim_get_current_buf() self.winid = api.nvim_get_current_win() - _Lc_menu = self + _Lc_state.menu = self end ---@type fun(): lc.ui.Menu diff --git a/lua/leetcode.lua b/lua/leetcode.lua index 23c4fec7..b0db267e 100644 --- a/lua/leetcode.lua +++ b/lua/leetcode.lua @@ -37,9 +37,7 @@ function leetcode.setup_cmds() require("leetcode.command").setup() end ---@param on_vimenter boolean function leetcode.start(on_vimenter) - if leetcode.should_skip(on_vimenter) then -- - return false - end + if leetcode.should_skip(on_vimenter) then return false end config.setup() @@ -63,6 +61,8 @@ function leetcode.start(on_vimenter) return true end +function leetcode.stop() vim.cmd("qa!") end + ---@param cfg? lc.UserConfig function leetcode.setup(cfg) config.apply(cfg or {}) diff --git a/lua/leetcode/command/init.lua b/lua/leetcode/command/init.lua index fcd96dc5..f8628b2e 100644 --- a/lua/leetcode/command/init.lua +++ b/lua/leetcode/command/init.lua @@ -95,10 +95,15 @@ cmd.q_close_all = function() local qs = utils.question_tabs() for _, tabp in ipairs(qs) do - tabp.question:_unmount() + tabp.question:unmount() end end +function cmd.exit() + local leetcode = require("leetcode") + leetcode.stop() +end + cmd.expire = vim.schedule_wrap(function() local tabp = api.nvim_get_current_tabpage() cmd.menu() @@ -160,12 +165,14 @@ function cmd.start_with_cmd() end function cmd.menu() - local ok, tabp = pcall(api.nvim_win_get_tabpage, _Lc_menu.winid) + local winid, bufnr = _Lc_state.menu.winid, _Lc_state.menu.bufnr + local ok, tabp = pcall(api.nvim_win_get_tabpage, winid) if ok then api.nvim_set_current_tabpage(tabp) + api.nvim_win_set_buf(winid, bufnr) else - _Lc_menu:remount() + _Lc_state.menu:remount() end end @@ -184,7 +191,7 @@ function cmd.yank() end ---@param page lc-menu.page -function cmd.set_menu_page(page) _Lc_menu:set_page(page) end +function cmd.set_menu_page(page) _Lc_state.menu:set_page(page) end function cmd.start_user_session() -- cmd.set_menu_page("menu") @@ -525,6 +532,7 @@ cmd.commands = { cmd.menu, menu = { cmd.menu }, + exit = { cmd.exit }, console = { cmd.console }, info = { cmd.info }, hints = { cmd.hints }, diff --git a/lua/leetcode/config/init.lua b/lua/leetcode/config/init.lua index ffe61201..7d7af699 100644 --- a/lua/leetcode/config/init.lua +++ b/lua/leetcode/config/init.lua @@ -1,11 +1,10 @@ local template = require("leetcode.config.template") local P = require("plenary.path") ----@type lc.ui.Question[] -_Lc_questions = {} - ----@type lc.ui.Menu -_Lc_menu = {} ---@diagnostic disable-line +_Lc_state = { + menu = nil, ---@type lc.ui.Menu + questions = {}, ---@type lc.ui.Question[] +} local lazy_plugs = {} diff --git a/lua/leetcode/config/stats.lua b/lua/leetcode/config/stats.lua index 4f141c66..0a6aca1e 100644 --- a/lua/leetcode/config/stats.lua +++ b/lua/leetcode/config/stats.lua @@ -14,7 +14,7 @@ function Stats.update_streak() Stats.daily.streak = res.streakCount Stats.daily.today_completed = res.todayCompleted - if _Lc_menu then _Lc_menu:draw() end + _Lc_state.menu:draw() end) end @@ -26,7 +26,8 @@ function Stats.update_sessions() stats_api.sessions(function(_, err) if err then return log.err(err) end - if _Lc_menu then _Lc_menu:draw() end + + _Lc_state.menu:draw() end) stats_api.session_progress(function(res, err) @@ -39,7 +40,7 @@ function Stats.update_sessions() Stats.progress[p.difficulty:lower()] = p end - if _Lc_menu then _Lc_menu:draw() end + _Lc_state.menu:draw() end) end @@ -47,7 +48,7 @@ function Stats.update() Stats.update_streak() Stats.update_sessions() - if _Lc_menu then _Lc_menu:draw() end + _Lc_state.menu:draw() end return Stats diff --git a/lua/leetcode/config/template.lua b/lua/leetcode/config/template.lua index ca7765b0..f6a1d155 100644 --- a/lua/leetcode/config/template.lua +++ b/lua/leetcode/config/template.lua @@ -23,6 +23,7 @@ ---@alias lc.hook ---| "enter" ---| "question_enter" +---| "leave" ---@alias lc.size ---| string @@ -57,7 +58,10 @@ local M = { cache = vim.fn.stdpath("cache") .. "/leetcode", }, - plugins = {}, + ---@type table + plugins = { + non_standalone = false, + }, ---@type boolean logging = true, @@ -103,6 +107,9 @@ local M = { ---@type fun(question: lc.ui.Question)[] ["question_enter"] = {}, + + ---@type fun()[] + ["leave"] = {}, }, keys = { diff --git a/lua/leetcode/logger/init.lua b/lua/leetcode/logger/init.lua index ff517af2..2aeef496 100644 --- a/lua/leetcode/logger/init.lua +++ b/lua/leetcode/logger/init.lua @@ -36,7 +36,10 @@ logger.info = function(msg) logger.log(msg) end logger.warn = function(msg) logger.log(msg, lvls.WARN) end ---@param msg any -logger.error = function(msg) logger.log(msg, lvls.ERROR) end +logger.error = function(msg) + logger.log(msg, lvls.ERROR) + logger.debug(msg) +end ---@param err lc.err logger.err = function(err) diff --git a/lua/leetcode/utils.lua b/lua/leetcode/utils.lua index a4297918..daf84160 100644 --- a/lua/leetcode/utils.lua +++ b/lua/leetcode/utils.lua @@ -31,7 +31,7 @@ end function utils.question_tabs() local questions = {} - for _, q in ipairs(_Lc_questions) do + for _, q in ipairs(_Lc_state.questions) do local tabp = utils.question_tabp(q) if tabp then table.insert(questions, { tabpage = tabp, question = q }) end end