From 3f83267886ad470a6530f03911f3829d2af0f36f Mon Sep 17 00:00:00 2001 From: xusupeng <xusupeng@beisen.com> Date: Wed, 14 May 2025 00:53:45 +0800 Subject: [PATCH 1/2] =?UTF-8?q?refactor(explorer):=20=E9=87=8D=E6=9E=84=20?= =?UTF-8?q?Hot100=20=E5=92=8C=20Interview=20Classics=20150=20=E7=9A=84?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -从 explorerNodeManager.ts 中移除直接调用接口获取 Hot100 和 Interview Classics 150 数据的逻辑 - 在 LeetCodeNode.ts 中添加 isHot100 和 isClassic150属性,用于判断节点是否属于 Hot100 或 Classic150 - 更新 list.ts,将 Hot100 和 Interview Classics 150 的数据作为问题列表的一部分进行处理 - 在 shared.ts 中为 IProblem 接口添加 isHot100 和 isClassic150 字段 暂存 --- src/commands/list.ts | 259 ++++++++++++++++++++++- src/explorer/LeetCodeNode.ts | 6 + src/explorer/LeetCodeTreeDataProvider.ts | 4 + src/explorer/explorerNodeManager.ts | 28 +++ src/leetCodeExecutor.ts | 8 + src/shared.ts | 6 + 6 files changed, 310 insertions(+), 1 deletion(-) diff --git a/src/commands/list.ts b/src/commands/list.ts index 3ebe236a..1e7ef605 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -7,6 +7,7 @@ import { IProblem, ProblemState, UserStatus } from "../shared"; import * as settingUtils from "../utils/settingUtils"; import { DialogType, promptForOpenOutputChannel } from "../utils/uiUtils"; + export async function listProblems(): Promise<IProblem[]> { try { if (leetCodeManager.getStatus() === UserStatus.SignedOut) { @@ -14,7 +15,261 @@ export async function listProblems(): Promise<IProblem[]> { } const useEndpointTranslation: boolean = settingUtils.shouldUseEndpointTranslation(); - const result: string = await leetCodeExecutor.listProblems(true, useEndpointTranslation); + let result = await leetCodeExecutor.listProblems(true, useEndpointTranslation); + let classic150Problems = [ + 88, + 27, + 26, + 80, + 169, + 189, + 121, + 122, + 55, + 45, + 274, + 380, + 238, + 134, + 135, + 42, + 13, + 12, + 58, + 14, + 151, + 6, + 28, + 68, + 125, + 392, + 167, + 11, + 15, + 209, + 3, + 30, + 76, + 36, + 54, + 48, + 73, + 289, + 383, + 205, + 290, + 242, + 49, + 1, + 202, + 219, + 128, + 228, + 56, + 57, + 452, + 20, + 71, + 155, + 150, + 224, + 141, + 2, + 21, + 138, + 92, + 25, + 19, + 82, + 61, + 86, + 146, + 104, + 100, + 226, + 101, + 105, + 106, + 117, + 114, + 112, + 129, + 124, + 173, + 222, + 236, + 199, + 637, + 102, + 103, + 530, + 230, + 98, + 200, + 130, + 133, + 399, + 207, + 210, + 945, + 433, + 127, + 208, + 211, + 212, + 17, + 77, + 46, + 39, + 52, + 22, + 79, + 108, + 148, + 772, + 23, + 53, + 954, + 35, + 74, + 162, + 33, + 34, + 153, + 4, + 215, + 502, + 373, + 295, + 67, + 190, + 191, + 136, + 137, + 201, + 9, + 66, + 172, + 69, + 50, + 149, + 70, + 198, + 139, + 322, + 300, + 120, + 64, + 63, + 5, + 97, + 72, + 123, + 188, + 221 + ]; + let hot100Problems = [ + 1, + 49, + 128, + 283, + 11, + 15, + 42, + 3, + 438, + 560, + 239, + 76, + 53, + 56, + 189, + 238, + 41, + 73, + 54, + 48, + 240, + 160, + 206, + 234, + 141, + 142, + 21, + 2, + 19, + 24, + 25, + 138, + 148, + 23, + 146, + 94, + 104, + 226, + 101, + 543, + 102, + 108, + 98, + 230, + 199, + 114, + 105, + 437, + 236, + 124, + 200, + 1036, + 207, + 208, + 46, + 78, + 17, + 39, + 22, + 79, + 131, + 51, + 35, + 74, + 34, + 33, + 153, + 4, + 20, + 155, + 394, + 739, + 84, + 215, + 347, + 295, + 121, + 55, + 45, + 768, + 70, + 118, + 198, + 279, + 322, + 139, + 300, + 152, + 416, + 32, + 62, + 64, + 5, + 1250, + 72, + 136, + 169, + 75, + 31, + 287 + ] const problems: IProblem[] = []; const lines: string[] = result.split("\n"); const reg: RegExp = /^(.)\s(.{1,2})\s(.)\s\[\s*(\d*)\s*\]\s*(.*)\s*(Easy|Medium|Hard)\s*\((\s*\d+\.\d+ %)\)/; @@ -28,6 +283,8 @@ export async function listProblems(): Promise<IProblem[]> { isFavorite: match[1].trim().length > 0, locked: match[2].trim().length > 0, state: parseProblemState(match[3]), + isHot100: hot100Problems.includes(Number(id)), + isClassic150: classic150Problems.includes(Number(id)), name: match[5].trim(), difficulty: match[6].trim(), passRate: match[7].trim(), diff --git a/src/explorer/LeetCodeNode.ts b/src/explorer/LeetCodeNode.ts index 3d2cc74f..c5091796 100644 --- a/src/explorer/LeetCodeNode.ts +++ b/src/explorer/LeetCodeNode.ts @@ -42,6 +42,12 @@ export class LeetCodeNode { public get isFavorite(): boolean { return this.data.isFavorite; } + public get isHot100(): boolean { + return this.data.isHot100; + } + public get isClassic150(): boolean { + return this.data.isClassic150; + } public get isProblem(): boolean { return this.isProblemNode; diff --git a/src/explorer/LeetCodeTreeDataProvider.ts b/src/explorer/LeetCodeTreeDataProvider.ts index 9c298944..6ad39e33 100644 --- a/src/explorer/LeetCodeTreeDataProvider.ts +++ b/src/explorer/LeetCodeTreeDataProvider.ts @@ -85,6 +85,10 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod return explorerNodeManager.getAllTagNodes(); case Category.Company: return explorerNodeManager.getAllCompanyNodes(); + case Category.Hot100: + return explorerNodeManager.getHot100Nodes(); + case Category.InterviewClassics150Problems: + return explorerNodeManager.getInterviewClassics150Nodes(); default: if (element.isProblem) { return []; diff --git a/src/explorer/explorerNodeManager.ts b/src/explorer/explorerNodeManager.ts index a04ad55c..91c8f1ce 100644 --- a/src/explorer/explorerNodeManager.ts +++ b/src/explorer/explorerNodeManager.ts @@ -53,6 +53,14 @@ class ExplorerNodeManager implements Disposable { id: Category.Favorite, name: Category.Favorite, }), false), + new LeetCodeNode(Object.assign({}, defaultProblem, { + id: Category.Hot100, + name: Category.Hot100, + }), false), + new LeetCodeNode(Object.assign({}, defaultProblem, { + id: Category.InterviewClassics150Problems, + name: Category.InterviewClassics150Problems, + }), false), ]; } @@ -106,6 +114,26 @@ class ExplorerNodeManager implements Disposable { return res; } + public getHot100Nodes(): LeetCodeNode[] { + const res: LeetCodeNode[] = []; + for (const node of this.explorerNodeMap.values()) { + if (node.isHot100) { + res.push(node); + } + } + return this.applySortingStrategy(res); + } + + public getInterviewClassics150Nodes(): LeetCodeNode[] { + const res: LeetCodeNode[] = []; + for (const node of this.explorerNodeMap.values()) { + if (node.isClassic150) { + res.push(node); + } + } + return this.applySortingStrategy(res); + } + public getNodeById(id: string): LeetCodeNode | undefined { return this.explorerNodeMap.get(id); } diff --git a/src/leetCodeExecutor.ts b/src/leetCodeExecutor.ts index d2332c7a..3ca872f8 100644 --- a/src/leetCodeExecutor.ts +++ b/src/leetCodeExecutor.ts @@ -198,6 +198,14 @@ class LeetCodeExecutor implements Disposable { await this.executeCommandWithProgressEx("Updating the favorite list...", "node", commandParams); } + public async fetchHot100Problems(): Promise<string> { + return await this.executeCommandEx(this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "custom", "--url", "https://leetcode.cn/_next/data/LdkbymEZAqp5oj7_FRWLn/studyplan/top-100-liked.json?slug=top-100-liked"]); + } + + public async fetchInterviewClassics150Problems(): Promise<string> { + return await this.executeCommandEx(this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "custom", "--url", "https://leetcode.cn/_next/data/LdkbymEZAqp5oj7_FRWLn/studyplan/top-interview-150.json?slug=top-interview-150"]); + } + public async getCompaniesAndTags(): Promise<{ companies: { [key: string]: string[] }, tags: { [key: string]: string[] } }> { // preprocess the plugin source const companiesTagsPath: string = path.join(this.leetCodeRootPath, "lib", "plugins", "company.js"); diff --git a/src/shared.ts b/src/shared.ts index e8b59d89..3286a849 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -73,6 +73,8 @@ export enum Endpoint { export interface IProblem { isFavorite: boolean; + isHot100: boolean; + isClassic150: boolean; locked: boolean; state: ProblemState; id: string; @@ -87,6 +89,8 @@ export const defaultProblem: IProblem = { isFavorite: false, locked: false, state: ProblemState.Unknown, + isHot100: false, + isClassic150: false, id: "", name: "", difficulty: "", @@ -101,6 +105,8 @@ export enum Category { Tag = "Tag", Company = "Company", Favorite = "Favorite", + Hot100 = "Hot100", + InterviewClassics150Problems = "InterviewClassics150Problems", } export const supportedPlugins: string[] = ["company", "solution.discuss", "leetcode.cn"]; From 5ab223bb2ae7a81750f34d6ec563200a4721c120 Mon Sep 17 00:00:00 2001 From: xusupeng <xusupeng@beisen.com> Date: Mon, 2 Jun 2025 14:13:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(explorer):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E9=A2=98=E7=9B=AE=E5=90=8D=E7=A7=B0=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在问题列表和展示功能中增加英文题目名称字段,用于生成不同格式的路径名称 修改相关接口和逻辑以支持中英文题目名称切换 --- .idea/.gitignore | 13 +++++++++++++ .idea/indexLayout.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ src/commands/list.ts | 14 +++++++++++++- src/commands/show.ts | 6 +++--- src/explorer/LeetCodeNode.ts | 3 +++ src/shared.ts | 2 ++ 7 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/indexLayout.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..f1ff11e6 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,13 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# Rider 忽略的文件 +/contentModel.xml +/.idea.vscode-leetcode.iml +/projectSettingsUpdater.xml +/modules.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/indexLayout.xml b/.idea/indexLayout.xml new file mode 100644 index 00000000..7b08163c --- /dev/null +++ b/.idea/indexLayout.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="UserContentModel"> + <attachedFolders /> + <explicitIncludes /> + <explicitExcludes /> + </component> +</project> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/src/commands/list.ts b/src/commands/list.ts index 1e7ef605..4bcf84bc 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -16,6 +16,8 @@ export async function listProblems(): Promise<IProblem[]> { const useEndpointTranslation: boolean = settingUtils.shouldUseEndpointTranslation(); let result = await leetCodeExecutor.listProblems(true, useEndpointTranslation); + let resultEn = await leetCodeExecutor.listProblems(true,false); + const reg: RegExp = /^(.)\s(.{1,2})\s(.)\s\[\s*(\d*)\s*\]\s*(.*)\s*(Easy|Medium|Hard)\s*\((\s*\d+\.\d+ %)\)/; let classic150Problems = [ 88, 27, @@ -271,8 +273,17 @@ export async function listProblems(): Promise<IProblem[]> { 287 ] const problems: IProblem[] = []; + const linesEn: string[] = resultEn.split("\n"); + const enNameMap: { [key: string]: string } = {}; + for (const lineEn of linesEn) { + const matchEn: RegExpMatchArray | null = lineEn.match(reg); + if (matchEn && matchEn.length === 8) { + const idEn: string = matchEn[4].trim(); + enNameMap[idEn] = matchEn[5].trim(); + } + } const lines: string[] = result.split("\n"); - const reg: RegExp = /^(.)\s(.{1,2})\s(.)\s\[\s*(\d*)\s*\]\s*(.*)\s*(Easy|Medium|Hard)\s*\((\s*\d+\.\d+ %)\)/; + const { companies, tags } = await leetCodeExecutor.getCompaniesAndTags(); for (const line of lines) { const match: RegExpMatchArray | null = line.match(reg); @@ -286,6 +297,7 @@ export async function listProblems(): Promise<IProblem[]> { isHot100: hot100Problems.includes(Number(id)), isClassic150: classic150Problems.includes(Number(id)), name: match[5].trim(), + nameEn: enNameMap[id] , difficulty: match[6].trim(), passRate: match[7].trim(), companies: companies[id] || ["Unknown"], diff --git a/src/commands/show.ts b/src/commands/show.ts index eccf5571..2b57eaf5 100644 --- a/src/commands/show.ts +++ b/src/commands/show.ts @@ -263,12 +263,12 @@ async function resolveRelativePath(relativePath: string, node: IProblem, selecte case "name": return node.name; case "camelcasename": - return _.camelCase(node.name); + return _.camelCase(node.nameEn); case "pascalcasename": - return _.upperFirst(_.camelCase(node.name)); + return _.upperFirst(_.camelCase(node.nameEn)); case "kebabcasename": case "kebab-case-name": - return _.kebabCase(node.name); + return _.kebabCase(node.nameEn); case "snakecasename": case "snake_case_name": return _.snakeCase(node.name); diff --git a/src/explorer/LeetCodeNode.ts b/src/explorer/LeetCodeNode.ts index c5091796..dfee15bf 100644 --- a/src/explorer/LeetCodeNode.ts +++ b/src/explorer/LeetCodeNode.ts @@ -34,6 +34,9 @@ export class LeetCodeNode { public get tags(): string[] { return this.data.tags; } + public get nameEn(): string { + return this.data.nameEn; + } public get companies(): string[] { return this.data.companies; diff --git a/src/shared.ts b/src/shared.ts index 3286a849..61b7b99f 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -79,6 +79,7 @@ export interface IProblem { state: ProblemState; id: string; name: string; + nameEn:string; difficulty: string; passRate: string; companies: string[]; @@ -93,6 +94,7 @@ export const defaultProblem: IProblem = { isClassic150: false, id: "", name: "", + nameEn: "", difficulty: "", passRate: "", companies: [] as string[],