irongit

A Neovim plugin enabling you to solve LeetCode problems.

236 lines5.2 KBLua
1local log = require("leetcode.logger")
2local cookie = require("leetcode.cache.cookie")
3local config = require("leetcode.config")
4local ui_utils = require("leetcode-ui.utils")
5local utils = require("leetcode.utils")
6local Renderer = require("leetcode-ui.renderer")
7local api = vim.api
8
9---@class lc.ui.Menu : lc.ui.Renderer
10---@field cursor lc-menu.cursor
11---@field maps table
12local Menu = Renderer:extend("LeetMenu")
13
14local function tbl_keys(t)
15 local keys = vim.tbl_keys(t)
16 if vim.tbl_isempty(keys) then
17 return
18 end
19 table.sort(keys)
20 return keys
21end
22
23function Menu:draw()
24 Menu.super.draw(self, self)
25 return self
26end
27
28---@private
29function Menu:autocmds()
30 local group_id = api.nvim_create_augroup("leetcode_menu", { clear = true })
31
32 api.nvim_create_autocmd("WinResized", {
33 group = group_id,
34 buffer = self.bufnr,
35 callback = function()
36 self:draw()
37 end,
38 })
39
40 api.nvim_create_autocmd("CursorMoved", {
41 group = group_id,
42 buffer = self.bufnr,
43 callback = function()
44 self:cursor_move()
45 end,
46 })
47
48 api.nvim_create_autocmd("QuitPre", {
49 group = group_id,
50 buffer = self.bufnr,
51 callback = function()
52 self.winid = nil
53 self.bufnr = nil
54 self:clear_keymaps()
55 end,
56 })
57end
58
59function Menu:cursor_move()
60 if not (self.winid and api.nvim_win_is_valid(self.winid)) then
61 return
62 end
63
64 local curr = api.nvim_win_get_cursor(self.winid)
65 local prev = self.cursor.prev
66
67 local keys = tbl_keys(self._.buttons)
68 if not keys then
69 return
70 end
71
72 local function find_nearest(l, r)
73 while l < r do
74 local m = math.floor((l + r) / 2)
75
76 if keys[m] < curr[1] then
77 l = m + 1
78 else
79 r = m
80 end
81 end
82
83 return math.max(r, 1)
84 end
85
86 if prev then
87 local next_idx = self.cursor.idx
88
89 if curr[1] < prev[1] then
90 next_idx = find_nearest(1, self.cursor.idx - 1)
91 elseif curr[1] > prev[1] then
92 next_idx = find_nearest(self.cursor.idx + 1, #keys)
93 end
94
95 self.cursor.idx = next_idx
96 end
97
98 local row = keys[self.cursor.idx]
99 local col = #vim.fn.getline(row):match("^%s*")
100
101 self.cursor.prev = { row, col }
102 api.nvim_win_set_cursor(self.winid, self.cursor.prev)
103end
104
105function Menu:cursor_reset()
106 self.cursor.idx = 1
107 self.cursor.prev = nil
108end
109
110---@param name lc-menu.page
111function Menu:set_page(name)
112 self:cursor_reset()
113
114 local ok, page = pcall(require, "leetcode-ui.group.page." .. name)
115 if ok then
116 self:replace({ page })
117 else
118 log.error(page)
119 end
120
121 return self:draw()
122end
123
124function Menu:apply_options()
125 api.nvim_buf_set_name(self.bufnr, "")
126
127 ui_utils.buf_set_opts(self.bufnr, {
128 modifiable = false,
129 buflisted = false,
130 matchpairs = "",
131 swapfile = false,
132 buftype = "nofile",
133 filetype = config.name,
134 synmaxcol = 0,
135 })
136 ui_utils.win_set_opts(self.winid, {
137 wrap = false,
138 colorcolumn = "",
139 foldlevel = 999,
140 foldcolumn = "0",
141 cursorcolumn = false,
142 cursorline = false,
143 number = false,
144 relativenumber = false,
145 list = false,
146 spell = false,
147 signcolumn = "no",
148 })
149 vim.schedule(function()
150 ui_utils.win_set_winfixbuf(self.winid)
151 end)
152end
153
154function Menu:unmount()
155 if vim.v.dying ~= 0 then
156 return
157 end
158
159 require("leetcode.command").q_close_all()
160
161 vim.schedule(function()
162 if self.winid and vim.api.nvim_win_is_valid(self.winid) then
163 vim.api.nvim_win_close(self.winid, true)
164 end
165
166 if self.bufnr and vim.api.nvim_buf_is_valid(self.bufnr) then
167 vim.api.nvim_buf_delete(self.bufnr, { force = true })
168 end
169 end)
170end
171
172function Menu:remount()
173 if self.winid and api.nvim_win_is_valid(self.winid) then
174 api.nvim_win_close(self.winid, true)
175 end
176 if self.bufnr and api.nvim_buf_is_valid(self.bufnr) then
177 api.nvim_buf_delete(self.bufnr, { force = true })
178 end
179
180 vim.cmd("0tabnew")
181 self.bufnr = api.nvim_get_current_buf()
182 self.winid = api.nvim_get_current_win()
183
184 self:_mount()
185end
186
187function Menu:mount()
188 if cookie.get() then
189 self:set_page("loading")
190
191 local auth_api = require("leetcode.api.auth")
192 auth_api.user(function(_, err)
193 if err then
194 self:set_page("signin")
195 log.err(err)
196 else
197 local cmd = require("leetcode.command")
198 cmd.start_user_session()
199 end
200 end)
201 else
202 self:set_page("signin")
203 end
204
205 self:_mount()
206
207 return self
208end
209
210function Menu:_mount()
211 self:apply_options()
212 self:autocmds()
213 self:draw()
214end
215
216function Menu:init()
217 Menu.super.init(self, {}, {
218 position = "center",
219 })
220
221 self.cursor = {
222 idx = 1,
223 prev = nil,
224 }
225 self.maps = {}
226
227 self.bufnr = api.nvim_get_current_buf()
228 self.winid = api.nvim_get_current_win()
229
230 _Lc_state.menu = self
231end
232
233---@type fun(): lc.ui.Menu
234local LeetMenu = Menu
235
236return LeetMenu