A Neovim plugin enabling you to solve LeetCode problems.
feat(console): testcase virtual text
6 files changed, +93 -10
+2-0lua/leetcode/api/question.lua
| @@ -56,6 +56,8 @@ function question.by_title_slug(title_slug) | ||
| 56 | 56 | q.meta_data = select(2, pcall(utils.decode, q.meta_data)) |
| 57 | 57 | q.stats = select(2, pcall(utils.decode, q.stats)) |
| 58 | 58 | |
| 59 | + log.debug(q.meta_data) | |
| 60 | + | |
| 59 | 61 | return q |
| 60 | 62 | end |
| 61 | 63 |
+1-0lua/leetcode/api/types.lua
| @@ -236,6 +236,7 @@ | ||
| 236 | 236 | ---@class lc.QuestionResponse.metadata |
| 237 | 237 | ---@field manual boolean |
| 238 | 238 | ---@field name string |
| 239 | +---@field database boolean | |
| 239 | 240 | ---@field params metadata_param[] |
| 240 | 241 | ---@field return metadata_return |
| 241 | 242 |
+2-0lua/leetcode/config/template.lua
| @@ -70,6 +70,8 @@ local M = { | ||
| 70 | 70 | }, |
| 71 | 71 | |
| 72 | 72 | testcase = { |
| 73 | + virt_text = true, ---@type boolean | |
| 74 | + | |
| 73 | 75 | size = "40%", ---@type string | integer |
| 74 | 76 | }, |
| 75 | 77 | }, |
+1-0lua/leetcode/ui/console/init.lua
| @@ -65,6 +65,7 @@ function console:init(parent) | ||
| 65 | 65 | local keymap = { |
| 66 | 66 | ["R"] = function() self:run() end, |
| 67 | 67 | ["S"] = function() self:submit() end, |
| 68 | + ["r"] = function() self.testcase:reset() end, | |
| 68 | 69 | [{ "q", "<Esc>" }] = function() self:hide() end, |
| 69 | 70 | } |
| 70 | 71 |
+82-6lua/leetcode/ui/console/testcase.lua
| @@ -1,12 +1,12 @@ | ||
| 1 | -local Layout = require("leetcode-ui.layout") | |
| 2 | -local Text = require("leetcode-ui.component.text") | |
| 3 | -local NuiLine = require("nui.line") | |
| 4 | 1 | local log = require("leetcode.logger") |
| 2 | +local config = require("leetcode.config") | |
| 5 | 3 | local NuiPopup = require("nui.popup") |
| 6 | 4 | local console_popup = require("leetcode.ui.console.popup") |
| 7 | 5 | |
| 8 | 6 | ---@class lc.Testcase: lc.Console.Popup |
| 9 | 7 | ---@field testcases string[] |
| 8 | +---@field init_testcases string[] | |
| 9 | +---@field extmarks integer[] | |
| 10 | 10 | local testcase = {} |
| 11 | 11 | testcase.__index = testcase |
| 12 | 12 | setmetatable(testcase, console_popup) |
| @@ -41,9 +41,82 @@ function testcase:draw() | ||
| 41 | 41 | |
| 42 | 42 | vim.api.nvim_buf_set_lines(self.popup.bufnr, 0, -1, false, t) |
| 43 | 43 | |
| 44 | + self:draw_extmarks() | |
| 44 | 45 | return self |
| 45 | 46 | end |
| 46 | 47 | |
| 48 | +function testcase:clear_extmarks() | |
| 49 | + if not config.user.console.testcase.virt_text then return end | |
| 50 | + | |
| 51 | + local ns = vim.api.nvim_create_namespace("leetcode_extmarks") | |
| 52 | + for _, id in ipairs(self.extmarks) do | |
| 53 | + vim.api.nvim_buf_del_extmark(self.popup.bufnr, ns, id) | |
| 54 | + end | |
| 55 | + | |
| 56 | + self.extmarks = {} | |
| 57 | +end | |
| 58 | + | |
| 59 | +function testcase:add_extmark(line, col, opts) | |
| 60 | + local ns = vim.api.nvim_create_namespace("leetcode_extmarks") | |
| 61 | + | |
| 62 | + local id = vim.api.nvim_buf_set_extmark(self.popup.bufnr, ns, line, col, opts or {}) | |
| 63 | + table.insert(self.extmarks, id) | |
| 64 | +end | |
| 65 | + | |
| 66 | +function testcase:draw_extmarks() | |
| 67 | + if not config.user.console.testcase.virt_text then return end | |
| 68 | + | |
| 69 | + self:clear_extmarks() | |
| 70 | + local bufnr = self.popup.bufnr | |
| 71 | + | |
| 72 | + local md = self.parent.parent.q.meta_data | |
| 73 | + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) | |
| 74 | + | |
| 75 | + if not md.params then return end | |
| 76 | + | |
| 77 | + local j = 1 | |
| 78 | + local invalid = false | |
| 79 | + | |
| 80 | + for i, line in ipairs(lines) do | |
| 81 | + pcall(function() | |
| 82 | + if lines[i - 1] == "" and lines[i] == "" then invalid = true end | |
| 83 | + end) | |
| 84 | + | |
| 85 | + if line == "" then | |
| 86 | + j = 1 | |
| 87 | + else | |
| 88 | + local ok, text = pcall( | |
| 89 | + function() | |
| 90 | + return { | |
| 91 | + { " " }, | |
| 92 | + { "", "Operator" }, | |
| 93 | + { " " }, | |
| 94 | + { md.params[j].name, "Comment" }, | |
| 95 | + { " " }, | |
| 96 | + { md.params[j].type, "Type" }, | |
| 97 | + } | |
| 98 | + end | |
| 99 | + ) | |
| 100 | + if not ok or invalid then text = { { " invalid", "leetcode_error" } } end | |
| 101 | + | |
| 102 | + self:add_extmark(i - 1, -1, { virt_text = text }) | |
| 103 | + j = j + 1 | |
| 104 | + end | |
| 105 | + end | |
| 106 | +end | |
| 107 | + | |
| 108 | +function testcase:reset() | |
| 109 | + self:draw() | |
| 110 | + log.info("Test cases have been reset") | |
| 111 | +end | |
| 112 | + | |
| 113 | +function testcase:autocmds() | |
| 114 | + self.popup:on( | |
| 115 | + { "TextChanged", "TextChangedI", "TextChangedP", "TextChangedT" }, | |
| 116 | + function() self:draw_extmarks() end | |
| 117 | + ) | |
| 118 | +end | |
| 119 | + | |
| 47 | 120 | ---@param parent lc.Console |
| 48 | 121 | function testcase:init(parent) |
| 49 | 122 | local popup = NuiPopup({ |
| @@ -60,7 +133,7 @@ function testcase:init(parent) | ||
| 60 | 133 | text = { |
| 61 | 134 | top = " Console | (q) Hide ", |
| 62 | 135 | top_align = "center", |
| 63 | - bottom = " Testcase ", | |
| 136 | + bottom = " Testcase | (r) Reset ", | |
| 64 | 137 | bottom_align = "center", |
| 65 | 138 | }, |
| 66 | 139 | }, |
| @@ -73,13 +146,16 @@ function testcase:init(parent) | ||
| 73 | 146 | }, |
| 74 | 147 | }) |
| 75 | 148 | |
| 76 | - local obj = setmetatable({ | |
| 149 | + self = setmetatable({ | |
| 77 | 150 | popup = popup, |
| 78 | 151 | testcases = {}, |
| 152 | + init_testcases = {}, | |
| 153 | + extmarks = {}, | |
| 79 | 154 | parent = parent, |
| 80 | 155 | }, self) |
| 81 | 156 | |
| 82 | - return obj:draw() | |
| 157 | + self:autocmds() | |
| 158 | + return self:draw() | |
| 83 | 159 | end |
| 84 | 160 | |
| 85 | 161 | return testcase |
+5-4lua/leetcode/ui/question.lua
| @@ -23,11 +23,12 @@ Question.__index = Question | ||
| 23 | 23 | --- |
| 24 | 24 | ---@return boolean |
| 25 | 25 | local function is_sql(q) |
| 26 | - for _, value in ipairs(q.topic_tags or {}) do | |
| 27 | - if value.slug == "database" then return true end | |
| 28 | - end | |
| 26 | + -- for _, value in ipairs(q.topic_tags or {}) do | |
| 27 | + -- if value.slug == "database" then return true end | |
| 28 | + -- end | |
| 29 | 29 | |
| 30 | - return false | |
| 30 | + -- return false | |
| 31 | + return q.meta_data.database | |
| 31 | 32 | end |
| 32 | 33 | |
| 33 | 34 | function Question:get_snippet() |