irongit

A Neovim plugin enabling you to solve LeetCode problems.

feat(stats): progress bars

kawreauthored
parent 9bbd4e4commit 192ef6a02b68b554e277dab5087715c030132d76Browse files

7 files changed, +189 -35

+20-17lua/leetcode-menu/components/calendar.lua
@@ -36,18 +36,21 @@ function Calendar:get_submission(osdate)
3636 end
3737 end
3838
39+---@param res lc.Stats.Res
3940 function Calendar:handle_res(res)
40- self.calendar = res
41+ self.calendar = res.calendar
4142
4243 local time = os.date("*t")
4344 self.curr_time = os.time({ year = time.year - 1, month = time.month, day = 1 })
4445 self.time = time
4546 self.last_year_sub_count = 0
4647 self.month_lens = {}
48+ self.max_sub_count = 0
4749
4850 self.submissions = {}
4951 for ts, count in pairs(self.calendar.submission_calendar) do
5052 local osdate = os.date("*t", ts)
53+ self.max_sub_count = math.max(self.max_sub_count, count)
5154 table.insert(self.submissions, { osdate = osdate, count = count })
5255 self.last_year_sub_count = self.last_year_sub_count + count
5356 end
@@ -60,8 +63,6 @@ function Calendar:handle_res(res)
6063 self:handle_months()
6164 self:handle_submissions()
6265 self.lines = self.calendar_lines
63-
64- _Lc_Menu:draw()
6566 end
6667
6768 function Calendar:handle_submissions()
@@ -113,35 +114,37 @@ function Calendar:handle_weeks(m)
113114 end
114115 end
115116
117+local function square_hl(count, max_count)
118+ local perc = (count / max_count) * 100
119+ local num = math.ceil(perc / 10) * 10
120+ return ("leetcode_calendar_%d"):format(num)
121+end
122+
116123 ---@param m integer
117124 function Calendar:handle_weekdays(m)
118125 local curr = os.date("*t", self.curr_time)
119126
120- local count = self:get_submission(curr)
127+ local count = self:get_submission(curr) or 0
121128
122- local text
123- if count then
124- text = NuiText("󰝤", "leetcode_easy")
125- else
126- text = NuiText("󰝤", "Whitespace")
127- end
129+ local text = NuiText("󰝤", square_hl(count, self.max_sub_count))
128130 self.calendar_lines[curr.wday]:append(text)
129131
130132 self.curr_time = self.curr_time + (60 * 60 * 24)
131133 end
132134
133-function Calendar:init(opts)
135+---@param res lc.Stats.Res
136+function Calendar:init(res, opts)
134137 opts = vim.tbl_deep_extend("force", {
135138 position = "center",
136139 hl = "Keyword",
137- padding = {
138- top = 4,
139- bot = 2,
140- },
140+ -- padding = {
141+ -- top = 4,
142+ -- bot = 2,
143+ -- },
141144 }, opts or {})
142145
143- self = setmetatable(Text:init(Header.ascii, opts), self)
144- stats_api.calendar(function(res) self:handle_res(res) end)
146+ self = setmetatable(Text:init({}, opts), self)
147+ self:handle_res(res)
145148 return self
146149 end
147150
+90-4lua/leetcode-menu/components/solved.lua
@@ -1,14 +1,100 @@
1+local Text = require("leetcode-ui.component.text")
2+local NuiLine = require("nui.line")
3+
14 local log = require("leetcode.logger")
5+local hl = {
6+ ["All"] = "leetcode_all",
7+ ["Easy"] = "leetcode_easy",
8+ ["Medium"] = "leetcode_medium",
9+ ["Hard"] = "leetcode_hard",
10+}
211
3----@class lc-menu.Solved
12+---@class lc-menu.Solved : lc-ui.Text
413 local Solved = {}
514 Solved.__index = Solved
15+setmetatable(Solved, Text)
16+
17+---@return NuiLine
18+function Solved:progress_bar(width, solved, total_count, difficulty)
19+ local line = NuiLine()
20+ local solved_len = math.ceil(width * (solved / total_count))
21+
22+ for _ = 1, solved_len do
23+ line:append("󰝤", hl[difficulty])
24+ end
25+
26+ for _ = solved_len, width do
27+ line:append("󰝤", hl[difficulty] .. "_alt")
28+ end
29+
30+ return line
31+end
32+
33+---@param res lc.Stats.Res
34+function Solved:handle_res(res)
35+ self.stats = res
36+ self.solved_lines = {}
37+
38+ local subs = {}
39+
40+ local max_count_len = 0
41+ for _, stat in ipairs(res.submit_stats.acSubmissionNum) do
42+ local total_count = vim.tbl_filter(
43+ function(c) return c.difficulty == stat.difficulty end,
44+ self.stats.questions_count
45+ )[1].count
646
7-function Solved:init()
8- self = setmetatable({}, self)
47+ local solved_line = NuiLine()
48+ solved_line:append(tostring(stat.count))
49+ solved_line:append(("/%d"):format(total_count), "leetcode_alt")
50+ max_count_len = math.max(solved_line:content():len(), max_count_len)
51+
52+ subs[stat.difficulty] = {
53+ total_count = total_count,
54+ solved = stat.count,
55+ line = solved_line,
56+ idx = ({
57+ ["All"] = 1,
58+ ["Easy"] = 2,
59+ ["Medium"] = 3,
60+ ["Hard"] = 4,
61+ })[stat.difficulty],
62+ }
63+ end
64+
65+ for diff, stat in pairs(subs) do
66+ local line = NuiLine()
67+ line:append(diff, hl[diff])
68+
69+ local pad1 = (" "):rep(8 - vim.api.nvim_strwidth(diff))
70+ line:append(pad1)
71+
72+ line:append(stat.line)
73+
74+ local pad2 = (" "):rep(max_count_len + 2 - stat.line:content():len())
75+ line:append(pad2)
76+
77+ line:append(self:progress_bar(50, stat.solved, stat.total_count, diff))
78+
79+ table.insert(self.solved_lines, stat.idx, line)
80+ end
81+
82+ self.lines = self.solved_lines
83+end
984
10- log.info("siema")
85+---@param res lc.Stats.Res
86+function Solved:init(res, opts)
87+ opts = vim.tbl_deep_extend("force", {
88+ position = "center",
89+ hl = "Keyword",
90+ -- padding = {
91+ -- top = 4,
92+ -- bot = 2,
93+ -- },
94+ }, opts or {})
1195
96+ self = setmetatable(Text:init({}, opts), self)
97+ self:handle_res(res)
1298 return self
1399 end
14100
+17-5lua/leetcode-menu/layout/stats.lua
@@ -1,6 +1,10 @@
11 local cmd = require("leetcode.command")
2-local Calendar = require("leetcode-menu.components.calendar")
32 local statistics = require("leetcode.api.statistics")
3+local Solved = require("leetcode-menu.components.solved")
4+local Calendar = require("leetcode-menu.components.calendar")
5+local Group = require("leetcode-ui.component.group")
6+local Footer = require("leetcode-menu.components.footer")
7+local Header = require("leetcode-menu.components.header")
48 local log = require("leetcode.logger")
59
610 local Layout = require("leetcode-ui.layout")
@@ -29,15 +33,23 @@ local back = Button:init(
2933 function() cmd.menu_layout("menu") end
3034 )
3135
32-local Footer = require("leetcode-menu.components.footer")
36+local group = Group:init({ Header:init() })
3337
3438 statistics.solved(function(res)
35- --
36- log.info(res)
39+ group:set_opts({
40+ spacing = 3,
41+ padding = {
42+ top = 3,
43+ bot = 3,
44+ },
45+ })
46+
47+ group.components = { Solved:init(res), Calendar:init(res) }
48+ _Lc_Menu:draw()
3749 end)
3850
3951 return Layout:init({
40- Calendar:init(),
52+ group,
4153
4254 title,
4355
+3-0lua/leetcode-ui/component/group.lua
@@ -21,6 +21,9 @@ function Group:append(cmp)
2121 return self
2222 end
2323
24+---@param opts lc-ui.Group.opts
25+function Group:set_opts(opts) self.opts = vim.tbl_deep_extend("force", self.opts, opts) end
26+
2427 ---@param layout lc-ui.Layout
2528 function Group:draw(layout)
2629 local components = vim.deepcopy(self.components)
+12-9lua/leetcode/api/statistics.lua
@@ -1,5 +1,6 @@
11 local utils = require("leetcode.api.utils")
22 local config = require("leetcode.config")
3+local log = require("leetcode.logger")
34
45 local statistics = {}
56
@@ -38,13 +39,14 @@ function statistics.calendar(cb)
3839 end)
3940 end
4041
42+---@param cb fun(res: lc.Stats.Res)
4143 function statistics.solved(cb)
4244 local variables = {
4345 username = config.auth.name,
4446 }
4547
4648 local query = [[
47- query userProfileStatistics($username: String!, $year: Int) {
49+ query userStatistics($username: String!, $year: Int) {
4850 allQuestionsCount {
4951 difficulty
5052 count
@@ -63,16 +65,14 @@ function statistics.solved(cb)
6365 }
6466 submission_calendar: submissionCalendar
6567 }
66- submit_stats: submitStats {
68+ solved_beats: problemsSolvedBeatsStats {
69+ difficulty
70+ percentage
71+ }
72+ submit_stats: submitStatsGlobal {
6773 acSubmissionNum {
6874 difficulty
6975 count
70- submissions
71- }
72- totalSubmissionNum {
73- difficulty
74- count
75- submissions
7676 }
7777 }
7878 }
@@ -81,9 +81,11 @@ function statistics.solved(cb)
8181
8282 utils._query(query, variables, function(res)
8383 local data = res.body.data
84- local calendar = data["matchedUser"]["calendar"]
84+
8585 local questions_count = data["allQuestionsCount"]
86+ local calendar = data["matchedUser"]["calendar"]
8687 local submit_stats = data["matchedUser"]["submit_stats"]
88+ local solved_beats = data["matchedUser"]["solved_beats"]
8789
8890 calendar.submission_calendar = select(2, pcall(utils.decode, calendar.submission_calendar))
8991
@@ -91,6 +93,7 @@ function statistics.solved(cb)
9193 calendar = calendar,
9294 questions_count = questions_count,
9395 submit_stats = submit_stats,
96+ solved_beats = solved_beats,
9497 })
9598 end)
9699 end
+27-0lua/leetcode/api/types.lua
@@ -329,3 +329,30 @@
329329 ---@field is_premium boolean
330330 ---@field is_verified boolean
331331 ---@field id integer
332+
333+--------------------------------------------
334+--- Statistics
335+--------------------------------------------
336+---@class lc.Stats.CalendarData
337+---@field active_years table
338+---@field dcc_badges table
339+---@field streak number
340+---@field submission_calendar table
341+---@field total_active_days number
342+
343+---@class lc.Stats.QuestionCount
344+---@field count number
345+---@field difficulty string
346+
347+---@class lc.Stats.SolvedBeat
348+---@field difficulty string
349+---@field percentage number
350+
351+---@class lc.Stats.SubmissionStat
352+---@field acSubmissionNum table
353+
354+---@class lc.Stats.Res
355+---@field calendar lc.Stats.CalendarData
356+---@field questions_count table<lc.Stats.QuestionCount>
357+---@field solved_beats table<lc.Stats.SolvedBeat>
358+---@field submit_stats lc.Stats.SubmissionStat
+20-0lua/leetcode/theme/default.lua
@@ -28,6 +28,10 @@ M.get = function()
2828 medium = { fg = "#fac31d" },
2929 hard = { fg = "#f8615c" },
3030
31+ easy_alt = { fg = "#294d35" },
32+ medium_alt = { fg = "#5e4e25" },
33+ hard_alt = { fg = "#5a302f" },
34+
3135 ok = { fg = hl("DiagnosticOk").fg },
3236 info = { fg = hl("DiagnosticInfo").fg },
3337 hint = { fg = hl("DiagnosticHint").fg },
@@ -50,6 +54,22 @@ M.get = function()
5054 link = { fg = hl("Function").fg, underline = true },
5155 list = { fg = hl("SpecialChar").fg },
5256 ref = { fg = hl("Tag").fg },
57+
58+ -- calendar
59+ calendar_0 = { fg = "#45475b" },
60+ calendar_10 = { fg = "#004d1b" },
61+ calendar_20 = { fg = "#016620" },
62+ calendar_30 = { fg = "#078029" },
63+ calendar_40 = { fg = "#109932" },
64+ calendar_50 = { fg = "#1cb33d" },
65+ calendar_60 = { fg = "#28c244" },
66+ calendar_70 = { fg = "#51d164" },
67+ calendar_80 = { fg = "#7fe18b" },
68+ calendar_90 = { fg = "#b3f0b8" },
69+ calendar_100 = { fg = "#d8ffda" },
70+
71+ all = { fg = hl("Normal").fg },
72+ all_alt = { fg = "#45475b" },
5373 }
5474 end
5575