A Neovim plugin enabling you to solve LeetCode problems.
| 1 | local config = require("leetcode.config") |
| 2 | local img_ok, image_api = pcall(require, "image") |
| 3 | local img_sup = img_ok and config.user.image_support |
| 4 | |
| 5 | local Group = require("leetcode-ui.group") |
| 6 | local Padding = require("leetcode-ui.lines.padding") |
| 7 | local Split = require("leetcode-ui.split") |
| 8 | local Parser = require("leetcode.parser") |
| 9 | |
| 10 | local log = require("leetcode.logger") |
| 11 | local utils = require("leetcode.utils") |
| 12 | |
| 13 | local t = require("leetcode.translator") |
| 14 | |
| 15 | ---@class lc.ui.Description : lc-ui.Split |
| 16 | ---@field question lc.ui.Question |
| 17 | ---@field images table<string, Image> |
| 18 | local Description = Split:extend("LeetDescription") |
| 19 | |
| 20 | local group_id = vim.api.nvim_create_augroup("leetcode_description", { clear = true }) |
| 21 | |
| 22 | |
| 23 | vim.api.nvim_create_autocmd("WinResized", { |
| 24 | group = group_id, |
| 25 | buffer = self.bufnr, |
| 26 | callback = function() |
| 27 | self:draw() |
| 28 | end, |
| 29 | }) |
| 30 | end |
| 31 | |
| 32 | |
| 33 | Description.super.mount(self) |
| 34 | self:populate() |
| 35 | |
| 36 | local ui_utils = require("leetcode-ui.utils") |
| 37 | ui_utils.buf_set_opts(self.bufnr, { |
| 38 | modifiable = false, |
| 39 | buflisted = false, |
| 40 | matchpairs = "", |
| 41 | swapfile = false, |
| 42 | buftype = "nofile", |
| 43 | filetype = config.name, |
| 44 | synmaxcol = 0, |
| 45 | }) |
| 46 | ui_utils.win_set_opts(self.winid, { |
| 47 | winhighlight = "Normal:NormalFloat,FloatBorder:FloatBorder", |
| 48 | wrap = not img_sup, |
| 49 | colorcolumn = "", |
| 50 | foldlevel = 999, |
| 51 | foldcolumn = "1", |
| 52 | cursorcolumn = false, |
| 53 | cursorline = false, |
| 54 | number = false, |
| 55 | relativenumber = false, |
| 56 | list = false, |
| 57 | spell = false, |
| 58 | signcolumn = "no", |
| 59 | linebreak = true, |
| 60 | }) |
| 61 | ui_utils.win_set_winfixbuf(self.winid) |
| 62 | |
| 63 | if not img_ok and config.user.image_support then |
| 64 | log.error("image.nvim not found but `image_support` is enabled") |
| 65 | end |
| 66 | |
| 67 | self:draw() |
| 68 | self:autocmds() |
| 69 | return self |
| 70 | end |
| 71 | |
| 72 | |
| 73 | Description.super.draw(self) |
| 74 | self:draw_imgs() |
| 75 | end |
| 76 | |
| 77 | |
| 78 | if not img_sup then |
| 79 | return |
| 80 | end |
| 81 | |
| 82 | local lines = vim.api.nvim_buf_get_lines(self.bufnr, 1, -1, false) |
| 83 | for i, line in ipairs(lines) do |
| 84 | for link in line:gmatch("%((http[s]?://%S+)%)") do |
| 85 | local img = self.images[link] |
| 86 | |
| 87 | if not img then |
| 88 | self.images[link] = {} |
| 89 | |
| 90 | image_api.from_url(link, { |
| 91 | buffer = self.bufnr, |
| 92 | window = self.winid, |
| 93 | with_virtual_padding = true, |
| 94 | }, function(image) |
| 95 | if not image then |
| 96 | return |
| 97 | end |
| 98 | |
| 99 | self.images[link] = image |
| 100 | image:render({ y = i + 1 }) |
| 101 | end) |
| 102 | elseif not vim.tbl_isempty(img) then |
| 103 | img:clear(true) |
| 104 | end |
| 105 | end |
| 106 | end |
| 107 | end |
| 108 | |
| 109 | |
| 110 | self.show_stats = not self.show_stats |
| 111 | self:populate() |
| 112 | self:draw() |
| 113 | end |
| 114 | |
| 115 | ---@private |
| 116 | |
| 117 | local q = self.question.q |
| 118 | |
| 119 | local header = Group({}, { |
| 120 | position = "center", |
| 121 | }) |
| 122 | |
| 123 | header:append(self.question.cache.link or "", "leetcode_alt") |
| 124 | header:endgrp() |
| 125 | |
| 126 | header:insert(Padding(1)) |
| 127 | |
| 128 | header:append(q.frontend_id .. ". ", "leetcode_normal") |
| 129 | header:append(utils.translate(q.title, q.translated_title)) |
| 130 | if q.is_paid_only then |
| 131 | header:append(" " .. t("Premium"), "leetcode_medium") |
| 132 | end |
| 133 | header:endgrp() |
| 134 | |
| 135 | local show_stats = self.show_stats |
| 136 | |
| 137 | if show_stats then |
| 138 | header:append(t(q.difficulty), "leetcode_" .. q.difficulty:lower()) |
| 139 | else |
| 140 | header:append("????", "leetcode_list") |
| 141 | end |
| 142 | |
| 143 | if config.icons.hl.status[self.question.cache.status] then |
| 144 | local s = config.icons.hl.status[self.question.cache.status] |
| 145 | header:append(" "):append(s[1], s[2]) |
| 146 | end |
| 147 | |
| 148 | header:append((" %s "):format(config.icons.bar)) |
| 149 | |
| 150 | local likes = show_stats and q.likes or "___" |
| 151 | header:append(likes .. " ", "leetcode_alt") |
| 152 | |
| 153 | local dislikes = show_stats and q.dislikes or "___" |
| 154 | if not config.is_cn then |
| 155 | header:append((" %s "):format(dislikes), "leetcode_alt") |
| 156 | end |
| 157 | |
| 158 | header:append((" %s "):format(config.icons.bar)) |
| 159 | |
| 160 | local ac_rate = show_stats and q.stats.acRate or "__%" |
| 161 | local total_sub = show_stats and q.stats.totalSubmission or "__" |
| 162 | header:append(("%s %s %s"):format(ac_rate, t("of"), total_sub), "leetcode_alt") |
| 163 | if not vim.tbl_isempty(q.hints) then |
| 164 | header:append((" %s "):format(config.icons.bar)) |
| 165 | header:append(" " .. t("Hints"), "leetcode_hint") |
| 166 | end |
| 167 | header:endgrp() |
| 168 | |
| 169 | local contents = Parser:parse(utils.translate(q.content, q.translated_content)) |
| 170 | |
| 171 | self.renderer:replace({ |
| 172 | header, |
| 173 | Padding(3), |
| 174 | contents, |
| 175 | }) |
| 176 | end |
| 177 | |
| 178 | ---@param parent lc.ui.Question |
| 179 | |
| 180 | Description.super.init(self, { |
| 181 | relative = "editor", |
| 182 | position = config.user.description.position, |
| 183 | size = config.user.description.width, |
| 184 | enter = false, |
| 185 | focusable = true, |
| 186 | }) |
| 187 | |
| 188 | self.question = parent |
| 189 | self.show_stats = config.user.description.show_stats |
| 190 | self.images = {} |
| 191 | end |
| 192 | |
| 193 | ---@type fun(parent: lc.ui.Question): lc.ui.Description |
| 194 | local LeetDescription = Description |
| 195 | |
| 196 | return LeetDescription |