irongit

A Neovim plugin enabling you to solve LeetCode problems.

feat: chinese problemlist

kawreauthored
parent 83a09bfcommit 6235129863ef6bb4e5434cc2ba01ed54da4b62aeBrowse files

12 files changed, +115 -69

+3-3lua/leetcode/api/interpreter.lua
@@ -51,14 +51,14 @@ end
5151 ---@param body lc.Interpret.body
5252 ---@param callback function
5353 function interpreter.interpret_solution(title_slug, body, callback)
54- local url = (config.domain .. "/problems/%s/interpret_solution/"):format(title_slug)
54+ local url = ("%s/problems/%s/interpret_solution/"):format(config.domain, title_slug)
5555 local res = interpreter.fetch(url, body)
5656
5757 if res then interpreter.listener(res.interpret_id, callback) end
5858 end
5959
6060 function interpreter.submit(title_slug, body, callback)
61- local url = (config.domain .. "/problems/%s/submit/"):format(title_slug)
61+ local url = ("%s/problems/%s/submit/"):format(config.domain, title_slug)
6262 local res = interpreter.fetch(url, body)
6363
6464 if res then interpreter.listener(res.submission_id, callback) end
@@ -69,7 +69,7 @@ end
6969 ---
7070 ---@return lc.Interpreter.Response
7171 function interpreter.check(id, cb)
72- local url = (config.domain .. "/submissions/detail/%s/check/"):format(id)
72+ local url = ("%s/submissions/detail/%s/check/"):format(config.domain, id)
7373 utils.get(url, cb)
7474 end
7575
+36-12lua/leetcode/api/problems.lua
@@ -1,5 +1,7 @@
11 local utils = require("leetcode.api.utils")
22 local queries = require("leetcode.api.queries")
3+local config = require("leetcode.config")
4+
35 local log = require("leetcode.logger")
46
57 ---@class lc.ProblemsApi
@@ -7,22 +9,33 @@ local M = {}
79
810 ---@return lc.Cache.Question[]
911 function M.all(cb)
10- local variables = {
11- limit = 9999,
12- }
13-
14- local query = queries.problemset()
12+ local url = ("%s/api/problems/algorithms/"):format(config.domain)
1513
1614 if cb then
17- utils.query(query, variables, function(res, err)
18- local data = res.data
19- local questions = data["problemsetQuestionList"]["questions"]
20- cb(utils.normalize_cn_problemlist(questions))
15+ utils.get(url, function(res, err)
16+ if err then return end
17+
18+ local problems = utils.normalize_problems(res.stat_status_pairs)
19+
20+ if config.is_cn and config.user.cn.translate then
21+ M.translated_titles(function(titles)
22+ problems = utils.translate_titles(problems, titles)
23+ cb(problems)
24+ end)
25+ else
26+ cb(problems)
27+ end
2128 end)
2229 else
23- local res, err = utils.query(query, variables)
24- local questions = res.data["problemsetQuestionList"]["questions"]
25- return utils.normalize_cn_problemlist(questions)
30+ local res, err = utils.get(url)
31+ local problems = utils.normalize_problems(res.stat_status_pairs)
32+
33+ if config.is_cn and config.user.cn.translate then
34+ local titles = M.translated_titles()
35+ return utils.translate_titles(problems, titles)
36+ else
37+ return problems
38+ end
2639 end
2740 end
2841
@@ -37,4 +50,15 @@ function M.question_of_today(cb)
3750 utils.query(query, {}, callback)
3851 end
3952
53+function M.translated_titles(cb)
54+ local query = queries.translations()
55+
56+ if cb then
57+ utils.query(query, {}, function(res, err) cb(res.data.translations) end)
58+ else
59+ local res, err = utils.query(query, {})
60+ return res.data.translations
61+ end
62+end
63+
4064 return M
+14-1lua/leetcode/api/queries.lua
@@ -3,7 +3,7 @@ local config = require("leetcode.config")
33 ---@class lc.Api.Queries
44 local queries = {}
55
6-local function com_or(query, query_cn) return config.user.domain == "com" and query or query_cn end
6+local function com_or(query, query_cn) return not config.is_cn and query or query_cn end
77
88 function queries.auth()
99 local query = [[
@@ -238,4 +238,17 @@ function queries.random_question()
238238 return com_or(query, query_cn)
239239 end
240240
241+function queries.translations()
242+ local query_cn = [[
243+ query getQuestionTranslation($lang: String) {
244+ translations: allAppliedQuestionTranslations(lang: $lang) {
245+ title
246+ questionId
247+ }
248+ }
249+ ]]
250+
251+ return com_or(nil, query_cn)
252+end
253+
241254 return queries
+1-1lua/leetcode/api/question.lua
@@ -40,7 +40,7 @@ function question.random()
4040 local res, err = utils.query(query, variables)
4141
4242 local q = res.data.randomQuestion
43- if config.user.domain == "cn" then
43+ if config.is_cn then
4444 q = {
4545 title_slug = q,
4646 paid_only = problemlist.get_by_title_slug(q).paid_only,
+46-39lua/leetcode/api/utils.lua
@@ -115,45 +115,6 @@ function utils.auth_guard()
115115 assert(auth and auth.is_signed_in, "User not signed in")
116116 end
117117
118-local rename_cn_keys = {
119- acRate = "ac_rate",
120- frontendQuestionId = "frontend_id",
121- titleCn = "title_cn",
122- titleSlug = "title_slug",
123- topicTags = "topic_tags",
124- paidOnly = "paid_only",
125-}
126-
127-local status_cn = {
128- ["NOT_STARTED"] = vim.NIL,
129-}
130-
131-local difficulty_cn = {
132- ["EASY"] = "Easy",
133- ["MEDIUM"] = "Medium",
134- ["HARD"] = "Hard",
135-}
136-
137-function utils.normalize_cn_problemlist(problems)
138- if config.user.domain == "com" then return problems end
139-
140- for _, question in ipairs(problems) do
141- for key, value in pairs(question) do
142- if vim.tbl_contains(vim.tbl_keys(rename_cn_keys), key) then
143- question[rename_cn_keys[key]] = value
144- question[key] = nil
145- end
146- end
147-
148- question.difficulty = difficulty_cn[question.difficulty]
149- question.status = status_cn[question.status]
150- question.ac_rate = question.ac_rate * 100
151- question.extra = nil
152- end
153-
154- return problems
155-end
156-
157118 function utils.normalize_similar_cn(s)
158119 s = select(2, pcall(utils.decode, s))
159120
@@ -165,4 +126,50 @@ function utils.normalize_similar_cn(s)
165126 end, s)
166127 end
167128
129+function utils.lvl_to_name(lvl) return ({ "Easy", "Medium", "Hard" })[lvl] end
130+
131+---@return lc.Cache.Question[]
132+function utils.normalize_problems(problems)
133+ problems = vim.tbl_filter(function(p) return not p.stat.question__hide end, problems)
134+
135+ table.sort(
136+ problems,
137+ function(a, b) return a.stat.frontend_question_id < b.stat.frontend_question_id end
138+ )
139+
140+ return vim.tbl_map(
141+ function(p)
142+ return {
143+ status = p.status,
144+ id = p.stat.question_id,
145+ frontend_id = p.stat.frontend_question_id,
146+ title = p.stat.question__title,
147+ title_slug = p.stat.question__title_slug,
148+ link = ("%s/problems/%s/"):format(config.domain, p.stat.question__title_slug),
149+ paid_only = p.paid_only,
150+ ac_rate = p.stat.total_acs * 100 / p.stat.total_submitted,
151+ difficulty = utils.lvl_to_name(p.difficulty.level),
152+ starred = p.is_favor,
153+ topic_tags = {},
154+ }
155+ end,
156+ problems
157+ )
158+end
159+
160+---@param problems lc.Cache.Question[]
161+---@param titles { questionId: integer, title: string }[]
162+function utils.translate_titles(problems, titles)
163+ local map = {}
164+ for _, t in ipairs(titles) do
165+ map[t.questionId] = t.title
166+ end
167+
168+ return vim.tbl_map(function(p)
169+ local title = map[tostring(p.id)]
170+ if title then p.title = title end
171+ return p
172+ end, problems)
173+end
174+
168175 return utils
+1-1lua/leetcode/cache/cookie.lua
@@ -2,7 +2,7 @@ local path = require("plenary.path")
22 local log = require("leetcode.logger")
33
44 local config = require("leetcode.config")
5-local file = config.home:joinpath((".cookie%s"):format(config.user.domain == "cn" and "_cn" or ""))
5+local file = config.home:joinpath((".cookie%s"):format(config.is_cn and "_cn" or ""))
66
77 ---@class lc.Cookie
88 ---@field csrftoken string
+3-2lua/leetcode/cache/problemlist.lua
@@ -2,8 +2,7 @@ local path = require("plenary.path")
22 local log = require("leetcode.logger")
33
44 local config = require("leetcode.config")
5-local file =
6- config.home:joinpath((".problemlist%s"):format(config.user.domain == "cn" and "_cn" or ""))
5+local file = config.home:joinpath((".problemlist%s"):format(config.is_cn and "_cn" or ""))
76
87 local problems_api = require("leetcode.api.problems")
98
@@ -12,7 +11,9 @@ local problems_api = require("leetcode.api.problems")
1211 ---@field slug string
1312
1413 ---@class lc.Cache.Question
14+---@field id integer
1515 ---@field frontend_id string
16+---@field link string
1617 ---@field title string
1718 ---@field title_cn string
1819 ---@field title_slug string
+2-0lua/leetcode/config/init.lua
@@ -34,6 +34,8 @@ function config.apply(cfg)
3434 config.debug = config.user.debug or false ---@diagnostic disable-line
3535 config.domain = "https://leetcode." .. config.user.domain
3636
37+ config.is_cn = config.user.domain == "cn"
38+
3739 config.lang = config.user.lang
3840 config.sql = config.user.sql
3941 end
+4-0lua/leetcode/config/template.lua
@@ -93,6 +93,10 @@ local M = {
9393 ---@type fun(question: { lang: string })[]
9494 LeetQuestionNew = {},
9595 },
96+
97+ cn = {
98+ translate = true, ---@type boolean
99+ },
96100 }
97101
98102 return M
+2-4lua/leetcode/pickers/question.lua
@@ -15,8 +15,7 @@ local action_state = require("telescope.actions.state")
1515 ---
1616 ---@return string
1717 local function question_formatter(question)
18- local title = config.user.domain == "com" and question.title or question.title_cn
19- return ("%d. %s %s"):format(question.frontend_id, title, question.title_slug)
18+ return ("%s. %s %s"):format(tostring(question.frontend_id), question.title, question.title_slug)
2019 end
2120
2221 ---@param question lc.Cache.Question
@@ -50,8 +49,7 @@ end
5049 local function display_question(question)
5150 local ac_rate = { ("%.1f%%"):format(question.ac_rate), "leetcode_ref" }
5251 local index = { question.frontend_id .. ".", "leetcode_normal" }
53- local title_txt = config.user.domain == "com" and question.title or question.title_cn
54- local title = { title_txt }
52+ local title = { question.title }
5553
5654 return unpack({ index, title, ac_rate })
5755 end
+1-1lua/leetcode/translator/init.lua
@@ -42,7 +42,7 @@ local function t(text)
4242 return "?"
4343 end
4444
45- return config.user.domain == "com" and text or translate[text]
45+ return not config.is_cn and text or translate[text]
4646 end
4747
4848 ---@param text string|string[]
+2-5lua/leetcode/ui/description.lua
@@ -91,8 +91,7 @@ function description:populate()
9191 local q = self.parent.q
9292
9393 local linkline = NuiLine()
94- local question_link = string.format("%s/problems/%s/", config.domain, q.title_slug)
95- linkline:append(question_link, "leetcode_alt")
94+ linkline:append(self.parent.cache.link, "leetcode_alt")
9695
9796 local titleline = NuiLine()
9897 titleline:append(q.frontend_id .. ". ", "leetcode_normal")
@@ -110,9 +109,7 @@ function description:populate()
110109 statsline:append(" | ")
111110
112111 statsline:append(q.likes .. " ", "leetcode_alt")
113- if config.user.domain == "com" then
114- statsline:append(" " .. q.dislikes .. " ", "leetcode_alt")
115- end
112+ if not config.is_cn then statsline:append(" " .. q.dislikes .. " ", "leetcode_alt") end
116113 statsline:append(" | ")
117114
118115 statsline:append(