irongit

A Neovim plugin enabling you to solve LeetCode problems.

217 lines5.9 KBLua
1local config = require("leetcode.config")
2local Spinner = require("leetcode.logger.spinner")
3local Lines = require("leetcode-ui.lines")
4local statistics = require("leetcode.api.statistics")
5local t = require("leetcode.translator")
6
7local Line = require("leetcode-ui.line")
8local NuiText = require("nui.text")
9
10local log = require("leetcode.logger")
11
12---@class lc.ui.Calendar : lc.ui.Lines
13---@field curr_time integer
14---@field calendar lc.Stats.CalendarData
15---@field calendar_lines NuiLine[]
16---@field last_year_sub_count integer
17local Calendar = Lines:extend("LeetCalendar")
18
19local function get_days_in_month(month, year)
20 local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
21 if (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0) then
22 days_in_month[2] = 29
23 end
24 return days_in_month[month]
25end
26
27local function is_same_day(date1, date2)
28 return date1.year == date2.year and date1.month == date2.month and date1.day == date2.day
29end
30
31---@param osdate osdate
32function Calendar:get_submission(osdate)
33 for i, sub in ipairs(self.submissions) do
34 if is_same_day(sub.osdate, osdate) then
35 table.remove(self.submissions, i)
36 return sub.count
37 end
38 end
39end
40
41---@param res { calendar: lc.Stats.CalendarData }
42function Calendar:handle_res(res)
43 self:clear()
44 self.calendar = res.calendar
45
46 local time = os.date("*t") --[[@as table]]
47 self.curr_time = os.time(vim.tbl_extend("force", time, {
48 year = time.year - 1,
49 month = time.month,
50 day = time.day,
51 hour = 1,
52 isdst = false,
53 }))
54
55 self.threshold = os.time({
56 year = time.year,
57 month = time.month,
58 day = time.day,
59 hour = 1,
60 isdst = false,
61 })
62 if self.threshold <= os.time(time) then
63 self.threshold = self.threshold + 24 * 60 * 60
64 end
65
66 self.last_year_sub_count = 0
67 self.month_lens = {}
68 self.max_sub_count = 0
69
70 self.submissions = {}
71 for ts, count in pairs(self.calendar.submission_calendar) do
72 local osdate = os.date("*t", tonumber(ts))
73 self.max_sub_count = math.max(self.max_sub_count, count)
74 table.insert(self.submissions, { osdate = osdate, count = count })
75 self.last_year_sub_count = self.last_year_sub_count + count
76 end
77
78 self.calendar_lines = {}
79 for _ = 1, 7 do
80 table.insert(self.calendar_lines, Line())
81 end
82
83 self:handle_months()
84 self:handle_submissions()
85
86 for _, line in ipairs(self.calendar_lines) do
87 self:append(line):endl()
88 end
89
90 _Lc_state.menu:draw()
91end
92
93function Calendar:handle_submissions()
94 local max_len = 0
95 for _, line in ipairs(self.calendar_lines) do
96 max_len = math.max(vim.api.nvim_strwidth(line:content()), max_len)
97 end
98
99 local subs_line = Line()
100 if not config.is_cn then
101 subs_line:append("" .. self.last_year_sub_count)
102 subs_line:append((" %s"):format(t("submissions")), "leetcode_alt")
103 else
104 subs_line:append(t("submissions"), "leetcode_alt")
105 subs_line:append(" " .. self.last_year_sub_count)
106 subs_line:append(" 次", "leetcode_alt")
107 end
108
109 local ad_line = Line()
110 ad_line:append(("%s:"):format(t("active days")), "leetcode_alt")
111 ad_line:append("", "leetcode_list")
112 ad_line:append("" .. self.calendar.total_active_days)
113
114 local streak_line = Line()
115 streak_line:append(("%s:"):format(t("max streak")), "leetcode_alt")
116 streak_line:append(" 󰈸 ", "leetcode_list")
117 streak_line:append("" .. self.calendar.streak)
118
119 local padding = (
120 max_len
121 - vim.api.nvim_strwidth(subs_line:content() .. ad_line:content() .. streak_line:content())
122 ) / 2
123
124 local sub_line = Line()
125 sub_line:append(subs_line)
126 sub_line:append((" "):rep(padding + (padding % 2 == 0 and 1 or 0)))
127 sub_line:append(ad_line)
128 sub_line:append((" "):rep(padding))
129 sub_line:append(streak_line)
130 table.insert(self.calendar_lines, sub_line)
131end
132
133function Calendar:handle_months()
134 for m = 1, 13 do
135 if m ~= 1 then
136 for i = 1, 7 do
137 self.calendar_lines[i]:append(" ")
138 end
139 end
140 self:handle_weeks(m)
141 end
142end
143
144---@param m integer
145function Calendar:handle_weeks(m)
146 local curr = os.date("*t", self.curr_time)
147 for i = 1, curr.wday - 1 do
148 self.calendar_lines[i]:append(" ")
149 end
150
151 for _ = curr.day, get_days_in_month(curr.month, curr.year) do
152 self:handle_weekdays(m)
153 end
154
155 curr = os.date("*t", self.curr_time)
156 for i = curr.wday, 7 do
157 self.calendar_lines[i]:append(" ")
158 end
159end
160
161local function square_hl(count, max_count)
162 local perc = (count / math.max(max_count, 1)) * 100
163 local num = math.ceil(perc / 10) * 10
164 return ("leetcode_calendar_%d"):format(num)
165end
166
167function Calendar:handle_weekdays()
168 if self.curr_time > self.threshold then
169 return
170 end
171
172 local curr = os.date("*t", self.curr_time)
173 local count = self:get_submission(curr) or 0
174
175 local text = NuiText("󰝤", square_hl(count, self.max_sub_count))
176 self.calendar_lines[curr.wday]:append(text)
177
178 self.curr_time = self.curr_time + (60 * 60 * 24)
179end
180
181function Calendar:fetch()
182 statistics.calendar(function(res, err)
183 if err then
184 return log.err(err)
185 end
186 self:handle_res(res)
187 end)
188end
189
190function Calendar:update()
191 local spinner = Spinner:start("updating calendar")
192 statistics.calendar(function(res, err)
193 if err then
194 spinner:error(err.msg)
195 else
196 spinner:success("calendar updated")
197 self:handle_res(res)
198 end
199 end)
200end
201
202function Calendar:init()
203 local opts = {
204 position = "center",
205 hl = "Keyword",
206 }
207
208 Calendar.super.init(self, {}, opts)
209
210 self:append(t("Loading..."))
211 self:fetch()
212end
213
214---@type fun(): lc.ui.Calendar
215local LeetCalendar = Calendar
216
217return LeetCalendar