A Neovim plugin enabling you to solve LeetCode problems.
chore: merge with master
8 files changed, +76 -16
+2-1.neoconf.json
| @@ -7,7 +7,8 @@ | ||
| 7 | 7 | "telescope.nvim", |
| 8 | 8 | "plenary.nvim", |
| 9 | 9 | "nui.nvim", |
| 10 | - "nvim-web-devicons" | |
| 10 | + "nvim-web-devicons", | |
| 11 | + "image.nvim" | |
| 11 | 12 | ] |
| 12 | 13 | } |
| 13 | 14 | } |
+16-1README.md
| @@ -49,12 +49,12 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/309088c9-4ff7-4dff-ab61-a | ||
| 49 | 49 | "kawre/leetcode.nvim", |
| 50 | 50 | build = ":TSUpdate html", |
| 51 | 51 | dependencies = { |
| 52 | - "nvim-treesitter/nvim-treesitter", | |
| 53 | 52 | "nvim-telescope/telescope.nvim", |
| 54 | 53 | "nvim-lua/plenary.nvim", -- required by telescope |
| 55 | 54 | "MunifTanjim/nui.nvim", |
| 56 | 55 | |
| 57 | 56 | -- optional |
| 57 | + "nvim-treesitter/nvim-treesitter", | |
| 58 | 58 | "rcarriga/nvim-notify", |
| 59 | 59 | "nvim-tree/nvim-web-devicons", |
| 60 | 60 | }, |
| @@ -127,6 +127,9 @@ To see full configuration types see [template.lua](./lua/leetcode/config/templat | ||
| 127 | 127 | ---@type fun(question: { lang: string })[] |
| 128 | 128 | LeetQuestionNew = {}, |
| 129 | 129 | }, |
| 130 | + | |
| 131 | + ---@type boolean | |
| 132 | + image_support = false, -- setting this to `true` will disable question description wrap | |
| 130 | 133 | } |
| 131 | 134 | ``` |
| 132 | 135 | |
| @@ -193,6 +196,15 @@ hooks = { | ||
| 193 | 196 | }, |
| 194 | 197 | ``` |
| 195 | 198 | |
| 199 | +### image support | |
| 200 | + | |
| 201 | +Whether to render question description images using [image.nvim] | |
| 202 | + | |
| 203 | +```lua | |
| 204 | +---@type boolean | |
| 205 | +image_support = false, -- setting this to `true` will disable question description wrap | |
| 206 | +``` | |
| 207 | + | |
| 196 | 208 | ## 📋 Commands |
| 197 | 209 | |
| 198 | 210 | ### `Leet` opens menu dashboard |
| @@ -268,10 +280,12 @@ local leet_arg = "leetcode.nvim" | ||
| 268 | 280 | |
| 269 | 281 | return { |
| 270 | 282 | "kawre/leetcode.nvim", |
| 283 | + ... | |
| 271 | 284 | lazy = leet_arg ~= vim.fn.argv()[1], |
| 272 | 285 | opts = { |
| 273 | 286 | arg = leet_arg, |
| 274 | 287 | }, |
| 288 | + ... | |
| 275 | 289 | } |
| 276 | 290 | ``` |
| 277 | 291 | |
| @@ -288,6 +302,7 @@ return { | ||
| 288 | 302 | |
| 289 | 303 | - [alpha-nvim](https://github.com/goolord/alpha-nvim) |
| 290 | 304 | |
| 305 | +[image.nvim]: https://github.com/3rd/image.nvim | |
| 291 | 306 | [lazy.nvim]: https://github.com/folke/lazy.nvim |
| 292 | 307 | [leetcode]: https://leetcode.com |
| 293 | 308 | [leetcode.nvim]: https://github.com/kawre/leetcode.nvim |
+3-0lua/leetcode/config/template.lua
| @@ -84,6 +84,9 @@ local M = { | ||
| 84 | 84 | LeetQuestionNew = {}, |
| 85 | 85 | }, |
| 86 | 86 | |
| 87 | + ---@type boolean | |
| 88 | + image_support = false, -- setting this to `true` will disable question description wrap | |
| 89 | + | |
| 87 | 90 | cn = { |
| 88 | 91 | translate = true, ---@type boolean |
| 89 | 92 | }, |
+9-0lua/leetcode/parser/html.lua
| @@ -165,6 +165,15 @@ function Html:handle_link(text, tag_data) | ||
| 165 | 165 | if attr.name == "src" then return attr end |
| 166 | 166 | end, tag_data.attrs)[1] or {} |
| 167 | 167 | link = src.value or "" |
| 168 | + | |
| 169 | + -- local bufnr = vim.api.nvim_get_current_buf() | |
| 170 | + -- local image = image_api.from_url(link, { | |
| 171 | + -- -- buffer = bufnr, | |
| 172 | + -- -- with_virtual_padding = true, | |
| 173 | + -- }, function(image) | |
| 174 | + -- -- | |
| 175 | + -- image:render() | |
| 176 | + -- end) | |
| 168 | 177 | end |
| 169 | 178 | |
| 170 | 179 | local line = NuiLine() |
+1-0lua/leetcode/pickers/question-tabs.lua
| @@ -25,6 +25,7 @@ end | ||
| 25 | 25 | |
| 26 | 26 | local function display_difficulty(q) |
| 27 | 27 | local lang = utils.get_lang(q.lang) |
| 28 | + if not lang then return {} end | |
| 28 | 29 | return { lang.icon, "leetcode_lang_" .. lang.slug } |
| 29 | 30 | end |
| 30 | 31 |
+41-10lua/leetcode/ui/description.lua
| @@ -1,4 +1,9 @@ | ||
| 1 | 1 | local config = require("leetcode.config") |
| 2 | +local log = require("leetcode.logger") | |
| 3 | + | |
| 4 | +local img_ok, image_api = pcall(require, "image") | |
| 5 | +local img_sup = img_ok and config.user.image_support | |
| 6 | + | |
| 2 | 7 | local parser = require("leetcode.parser") |
| 3 | 8 | local t = require("leetcode.translator") |
| 4 | 9 | |
| @@ -16,6 +21,7 @@ local Split = require("nui.split") | ||
| 16 | 21 | ---@field title any |
| 17 | 22 | ---@field layout lc-ui.Layout |
| 18 | 23 | ---@field visible boolean |
| 24 | +---@field images table<string, Image> | |
| 19 | 25 | local description = {} |
| 20 | 26 | description.__index = description |
| 21 | 27 | |
| @@ -46,7 +52,7 @@ function description:mount() | ||
| 46 | 52 | }) |
| 47 | 53 | utils.set_win_opts(self.split.winid, { |
| 48 | 54 | winhighlight = "Normal:NormalFloat,FloatBorder:FloatBorder", |
| 49 | - wrap = true, | |
| 55 | + wrap = not img_sup, | |
| 50 | 56 | colorcolumn = "", |
| 51 | 57 | foldlevel = 999, |
| 52 | 58 | foldcolumn = "1", |
| @@ -58,6 +64,9 @@ function description:mount() | ||
| 58 | 64 | spell = false, |
| 59 | 65 | signcolumn = "no", |
| 60 | 66 | }) |
| 67 | + if not img_ok and config.user.image_support then | |
| 68 | + log.error("image.nvim not found but `image_support` is enabled") | |
| 69 | + end | |
| 61 | 70 | |
| 62 | 71 | self:draw() |
| 63 | 72 | self:autocmds() |
| @@ -75,16 +84,37 @@ function description:toggle() | ||
| 75 | 84 | end |
| 76 | 85 | |
| 77 | 86 | function description:draw() |
| 78 | - -- local c = vim.api.nvim_win_get_cursor(self.split.winid) | |
| 79 | 87 | self.layout:draw(self.split) |
| 80 | - -- vim.api.nvim_win_set_cursor(self.split.winid, c) | |
| 88 | + self:draw_imgs() | |
| 81 | 89 | end |
| 82 | 90 | |
| 83 | --- function description:redraw() | |
| 84 | --- local c = vim.api.nvim_win_get_cursor(self.split.winid) | |
| 85 | --- self.layout:draw() | |
| 86 | --- vim.api.nvim_win_set_cursor(self.split.winid, c) | |
| 87 | --- end | |
| 91 | +function description:draw_imgs() | |
| 92 | + if not img_sup then return end | |
| 93 | + | |
| 94 | + local lines = vim.api.nvim_buf_get_lines(self.split.bufnr, 1, -1, false) | |
| 95 | + for i, line in ipairs(lines) do | |
| 96 | + for link in line:gmatch("->%((http[s]?://%S+)%)") do | |
| 97 | + local img = self.images[link] | |
| 98 | + | |
| 99 | + if not img then | |
| 100 | + self.images[link] = {} | |
| 101 | + | |
| 102 | + image_api.from_url(link, { | |
| 103 | + buffer = self.split.bufnr, | |
| 104 | + window = self.split.winid, | |
| 105 | + with_virtual_padding = true, | |
| 106 | + }, function(image) | |
| 107 | + if not image then return end | |
| 108 | + | |
| 109 | + self.images[link] = image | |
| 110 | + image:render({ y = i + 1 }) | |
| 111 | + end) | |
| 112 | + elseif not vim.tbl_isempty(img) then | |
| 113 | + img:clear(true) | |
| 114 | + end | |
| 115 | + end | |
| 116 | + end | |
| 117 | +end | |
| 88 | 118 | |
| 89 | 119 | ---@private |
| 90 | 120 | function description:populate() |
| @@ -147,14 +177,15 @@ function description:init(parent) | ||
| 147 | 177 | focusable = true, |
| 148 | 178 | }) |
| 149 | 179 | |
| 150 | - local obj = setmetatable({ | |
| 180 | + self = setmetatable({ | |
| 151 | 181 | split = split, |
| 152 | 182 | parent = parent, |
| 153 | 183 | layout = {}, |
| 154 | 184 | visible = false, |
| 185 | + images = {}, | |
| 155 | 186 | }, self) |
| 156 | 187 | |
| 157 | - return obj:mount() | |
| 188 | + return self:mount() | |
| 158 | 189 | end |
| 159 | 190 | |
| 160 | 191 | return description |
+2-2lua/leetcode/utils.lua
| @@ -112,14 +112,14 @@ end | ||
| 112 | 112 | |
| 113 | 113 | ---@return lc.language |
| 114 | 114 | function utils.get_lang(slug) |
| 115 | - return vim.tbl_filter(function(l) return l.slug == slug end, config.langs)[1] or {} | |
| 115 | + return vim.tbl_filter(function(l) return l.slug == slug end, config.langs)[1] | |
| 116 | 116 | end |
| 117 | 117 | |
| 118 | 118 | ---@param event lc.hook |
| 119 | 119 | function utils.exec_hooks(event, ...) |
| 120 | 120 | local log = require("leetcode.logger") |
| 121 | 121 | local fns = config.user.hooks[event] |
| 122 | - if not fns then log.error("Unknown hook event: " .. event) end | |
| 122 | + if not fns then log.error("unknown hook event: " .. event) end | |
| 123 | 123 | |
| 124 | 124 | for i, fn in ipairs(fns) do |
| 125 | 125 | local ok, msg = pcall(fn, ...) |