diff --git a/CHANGELOG.md b/CHANGELOG.md index da596f5..93d6bf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## version 2.19.13 + +- 增加重试上次测试用例按钮 retest + ## version 2.19.12 - 配置 filePath 文件名 ${name} ${camelcasename} ${pascalcasename} ${kebabcasename} ${kebab-case-name} ${snakecasename} ${snake_case_name} 原本参数使用英文名 diff --git a/package.json b/package.json index 2d0dfb4..114e06c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-leetcode-problem-rating", "displayName": "LeetCode", "description": "%main.description%", - "version": "2.19.12", + "version": "2.19.13", "author": "ccagml", "publisher": "ccagml", "license": "MIT", @@ -1025,6 +1025,7 @@ "case", "allcase", "test", + "retest", "solution", "debug" ], @@ -1039,7 +1040,8 @@ "description", "case", "allcase", - "debug" + "debug", + "retest" ], "enumDescriptions": [ "%main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.0%", @@ -1049,7 +1051,8 @@ "%main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.4%", "%main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.5%", "%main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.6%", - "%main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.7%" + "%main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.7%", + "%main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.8%" ] }, "description": "%main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.description%" diff --git a/package.nls.json b/package.nls.json index 6b45439..4d204ee 100644 --- a/package.nls.json +++ b/package.nls.json @@ -115,6 +115,8 @@ "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.4": "Show the problem description page.", "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.5": "Test default case", "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.6": "Test all default case", + "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.7": "Debug solution", + "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.8": "Test all default case", "main.contributes.configuration.properties.leetcode-problem-rating.hideScore.enumDescriptions.0": "Don't hide", "main.contributes.configuration.properties.leetcode-problem-rating.hideScore.enumDescriptions.1": "Hide questions with scores", "main.contributes.configuration.properties.leetcode-problem-rating.hideScore.enumDescriptions.2": "Hide questions with no scores", diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json index e18d5ba..47f1caf 100644 --- a/package.nls.zh-cn.json +++ b/package.nls.zh-cn.json @@ -115,6 +115,8 @@ "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.4": "展示题目介绍", "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.5": "测试默认用例", "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.6": "测试所有用例", + "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.7": "调试代码", + "main.contributes.configuration.properties.leetcode-problem-rating.editor.shortcuts.items.enumDescriptions.8": "重新执行用例", "main.contributes.configuration.properties.leetcode-problem-rating.hideScore.enumDescriptions.0": "不处理", "main.contributes.configuration.properties.leetcode-problem-rating.hideScore.enumDescriptions.1": "隐藏有分数的题目", "main.contributes.configuration.properties.leetcode-problem-rating.hideScore.enumDescriptions.2": "隐藏没有分数的题目", diff --git a/src/controller/TreeViewController.ts b/src/controller/TreeViewController.ts index 3c4a4d3..a2f0596 100644 --- a/src/controller/TreeViewController.ts +++ b/src/controller/TreeViewController.ts @@ -11,6 +11,7 @@ import * as lodash from "lodash"; import * as path from "path"; import * as vscode from "vscode"; import { toNumber } from "lodash"; +import * as fs from "fs"; import { Disposable, Uri, window, workspace, ConfigurationChangeEvent } from "vscode"; import { SearchNode, @@ -30,6 +31,9 @@ import { SORT_ORDER, Endpoint, OutPutType, + TestSolutionType, + ITestSolutionData, + defaultTestSolutionData, } from "../model/Model"; import { isHideSolvedProblem, @@ -75,6 +79,7 @@ import * as fse from "fs-extra"; import { submissionService } from "../service/SubmissionService"; import { bricksDataService } from "../service/BricksDataService"; import { groupDao } from "../dao/groupDao"; +import { fileMeta, ProblemMeta } from "../utils/problemUtils"; // 视图控制器 class TreeViewController implements Disposable { @@ -118,8 +123,10 @@ class TreeViewController implements Disposable { try { const result: string = await executeService.submitSolution(filePath); - submissionService.show(result); - eventService.emit("submit", submissionService.getSubmitEvent()); + + eventService.emit("submitSolutionResult", result); + // submissionService.show(result); + // eventService.emit("submit", submissionService.getSubmitEvent()); } catch (error) { await promptForOpenOutputChannel("提交出错了. 请查看控制台信息~", OutPutType.error); return; @@ -147,12 +154,6 @@ class TreeViewController implements Disposable { } const picks: Array> = []; picks.push( - // { - // label: "$(three-bars) Default test cases", - // description: "", - // detail: "默认用例", - // value: ":default", - // }, { label: "$(pencil) Write directly...", description: "", @@ -165,12 +166,6 @@ class TreeViewController implements Disposable { detail: "文件中的测试用例", value: ":file", } - // { - // label: "All Default test cases...", - // description: "", - // detail: "所有的测试用例", - // value: ":alldefault", - // }, ); const choice: IQuickItemEx | undefined = await vscode.window.showQuickPick(picks); if (!choice) { @@ -180,10 +175,10 @@ class TreeViewController implements Disposable { let result: string | undefined; let testString: string | undefined; let testFile: vscode.Uri[] | undefined; + + let tsd: ITestSolutionData = Object.assign({}, defaultTestSolutionData, {}); + switch (choice.value) { - case ":default": - result = await executeService.testSolution(filePath); - break; case ":direct": testString = await vscode.window.showInputBox({ prompt: "Enter the test cases.", @@ -193,7 +188,12 @@ class TreeViewController implements Disposable { ignoreFocusOut: true, }); if (testString) { - result = await executeService.testSolution(filePath, this.parseTestString(testString)); + tsd.filePath = filePath; + tsd.testString = this.parseTestString(testString); + tsd.allCase = false; + tsd.type = TestSolutionType.Type_1; + result = await executeService.testSolution(tsd.filePath, tsd.testString, tsd.allCase); + tsd.result = result; } break; case ":file": @@ -201,26 +201,26 @@ class TreeViewController implements Disposable { if (testFile && testFile.length) { const input: string = (await fse.readFile(testFile[0].fsPath, "utf-8")).trim(); if (input) { - result = await executeService.testSolution( - filePath, - this.parseTestString(input.replace(/\r?\n/g, "\\n")) - ); + tsd.filePath = filePath; + tsd.testString = this.parseTestString(input.replace(/\r?\n/g, "\\n")); + tsd.allCase = false; + result = await executeService.testSolution(tsd.filePath, tsd.testString, tsd.allCase); + tsd.result = result; + tsd.type = TestSolutionType.Type_2; } else { vscode.window.showErrorMessage("The selected test file must not be empty."); } } break; - case ":alldefault": - result = await executeService.testSolution(filePath, undefined, true); - break; default: break; } if (!result) { return; } - submissionService.show(result); - eventService.emit("submit", submissionService.getSubmitEvent()); + // submissionService.show(result); + // eventService.emit("submit", submissionService.getSubmitEvent()); + eventService.emit("testSolutionResult", result, tsd); } catch (error) { await promptForOpenOutputChannel("提交测试出错了. 请查看控制台信息~", OutPutType.error); } @@ -263,12 +263,64 @@ class TreeViewController implements Disposable { return; } - let result: string | undefined = await executeService.testSolution(filePath, undefined, allCase || false); + let tsd: ITestSolutionData = Object.assign({}, defaultTestSolutionData, {}); + tsd.filePath = filePath; + tsd.testString = undefined; + tsd.allCase = allCase || false; + tsd.type = TestSolutionType.Type_3; + let result: string | undefined = await executeService.testSolution(tsd.filePath, tsd.testString, tsd.allCase); + tsd.result = result; + if (!result) { + return; + } + // submissionService.show(result); + // eventService.emit("submit", submissionService.getSubmitEvent()); + eventService.emit("testSolutionResult", result, tsd); + } catch (error) { + await promptForOpenOutputChannel("提交测试出错了. 请查看控制台信息~", OutPutType.error); + } + } + + // 提交测试用例 + /** + * It takes the current file, and sends it to the server to be tested + * @param [uri] - The file path of the file to be submitted. If it is not passed, the currently active + * file is submitted. + */ + public async reTestSolution(uri?: vscode.Uri): Promise { + try { + if (statusBarService.getStatus() === UserStatus.SignedOut) { + return; + } + + const filePath: string | undefined = await getTextEditorFilePathByUri(uri); + if (!filePath) { + return; + } + const fileContent: Buffer = fs.readFileSync(filePath); + const meta: ProblemMeta | null = fileMeta(fileContent.toString()); + + let qid: string | undefined = undefined; + if (meta?.id != undefined) { + qid = this.getQidByFid(meta?.id); + } + + if (qid == undefined) { + return; + } + + let tsd: ITestSolutionData | undefined = submissionService.getTSDByQid(qid); + if (tsd == undefined) { + return; + } + + let result: string | undefined = await executeService.testSolution(tsd.filePath, tsd.testString, tsd.allCase); if (!result) { return; } - submissionService.show(result); - eventService.emit("submit", submissionService.getSubmitEvent()); + // submissionService.show(result); + // eventService.emit("submit", submissionService.getSubmitEvent()); + eventService.emit("testSolutionResult", result, tsd); } catch (error) { await promptForOpenOutputChannel("提交测试出错了. 请查看控制台信息~", OutPutType.error); } @@ -294,12 +346,19 @@ class TreeViewController implements Disposable { return; } - let result: string | undefined = await executeService.testSolution(filePath, testcase, false); + let tsd: ITestSolutionData = Object.assign({}, defaultTestSolutionData, {}); + tsd.filePath = filePath; + tsd.testString = testcase; + tsd.allCase = false; + tsd.type = TestSolutionType.Type_4; + let result: string | undefined = await executeService.testSolution(tsd.filePath, tsd.testString, tsd.allCase); + tsd.result = result; if (!result) { return; } - submissionService.show(result); - eventService.emit("submit", submissionService.getSubmitEvent()); + // submissionService.show(result); + // eventService.emit("submit", submissionService.getSubmitEvent()); + eventService.emit("testSolutionResult", result, tsd); } catch (error) { await promptForOpenOutputChannel("提交测试出错了. 请查看控制台信息~", OutPutType.error); } diff --git a/src/debugex/debugCpp.ts b/src/debugex/debugCpp.ts index ef99f9b..aacb1da 100644 --- a/src/debugex/debugCpp.ts +++ b/src/debugex/debugCpp.ts @@ -192,7 +192,7 @@ class DebugCpp extends DebugBase { insertCode += `${indent}(new Solution())->${problemType.funName}(arg1, arg0);\n`; } else if (templateId === "146") { insertCode += `LRUCache *lc; for (int i = 0; i < arg0.size(); i++) { if (arg0[i].compare("LRUCache") == 0) { lc = new LRUCache(arg1[i][0]); } else if (arg0[i].compare("put") == 0) { lc->put(arg1[i][0], arg1[i][1]); } else if (arg0[i].compare("get") == 0) { lc->get(arg1[i][0]); } } \n`; - }else { + } else { insertCode += `${indent}(new Solution())->${problemType.funName}(${callArgs.join(", ")});\n`; } diff --git a/src/extension.ts b/src/extension.ts index b5d0d2d..7ffa4df 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -76,6 +76,7 @@ export async function activate(context: ExtensionContext): Promise { commands.registerCommand("lcpr.getHelp", (input: NodeModel | Uri) => treeViewController.getHelp(input)), commands.registerCommand("lcpr.refreshExplorer", () => treeDataService.refresh()), commands.registerCommand("lcpr.testSolution", (uri?: Uri) => treeViewController.testSolution(uri)), + commands.registerCommand("lcpr.reTestSolution", (uri?: Uri) => treeViewController.reTestSolution(uri)), commands.registerCommand("lcpr.testCaseDef", (uri?, allCase?) => treeViewController.testCaseDef(uri, allCase)), commands.registerCommand("lcpr.tesCaseArea", (uri, testCase?) => treeViewController.tesCaseArea(uri, testCase)), commands.registerCommand("lcpr.submitSolution", (uri?: Uri) => treeViewController.submitSolution(uri)), diff --git a/src/model/Model.ts b/src/model/Model.ts index ff34052..24e71b3 100644 --- a/src/model/Model.ts +++ b/src/model/Model.ts @@ -28,6 +28,31 @@ export enum UserStatus { SignedOut = 2, } +export enum TestSolutionType { + Type_1 = 1, + Type_2 = 2, + Type_3 = 3, + Type_4 = 4, + Type_5 = 5, + Type_6 = 6, +} + +export interface ITestSolutionData { + filePath: string; + testString: undefined | string; + allCase: undefined | boolean; + type: TestSolutionType; + result: string | undefined; +} + +export const defaultTestSolutionData: ITestSolutionData = { + filePath: "", + testString: undefined, + allCase: undefined, + type: TestSolutionType.Type_1, + result: undefined, +}; + export const loginArgsMapping: Map = new Map([ ["LeetCode", "-l"], ["Cookie", "-c"], diff --git a/src/service/EventService.ts b/src/service/EventService.ts index 2429022..24320f5 100644 --- a/src/service/EventService.ts +++ b/src/service/EventService.ts @@ -9,12 +9,13 @@ import { EventEmitter } from "events"; -import { IProblem, UserStatus } from "../model/Model"; +import { IProblem, ITestSolutionData, UserStatus } from "../model/Model"; import { ISubmitEvent } from "../model/Model"; import { statusBarService } from "../service/StatusBarService"; import { treeDataService } from "../service/TreeDataService"; import { bricksDataService } from "./BricksDataService"; import { statusBarTimeService } from "./StatusBarTimeService"; +import { submissionService } from "./SubmissionService"; class EventService extends EventEmitter { constructor() { @@ -56,6 +57,13 @@ class EventService extends EventEmitter { this.on("groupUpdate", () => { bricksDataService.refresh(); }); + + this.on("testSolutionResult", (resultString: string, tsd: ITestSolutionData) => { + submissionService.show(resultString, tsd); + }); + this.on("submitSolutionResult", (resultString: string) => { + submissionService.show(resultString); + }); } } diff --git a/src/service/FileButtonService.ts b/src/service/FileButtonService.ts index 33d1e23..f92edb4 100644 --- a/src/service/FileButtonService.ts +++ b/src/service/FileButtonService.ts @@ -73,6 +73,16 @@ export class FileButtonService implements vscode.CodeLensProvider { ); } + if (shortcuts.indexOf("retest") >= 0) { + temp_result.push( + new vscode.CodeLens(range, { + title: "ReTest", + command: "lcpr.reTestSolution", + arguments: [document.uri], + }) + ); + } + if (shortcuts.indexOf("star") >= 0 && node) { temp_result.push( new vscode.CodeLens(range, { diff --git a/src/service/SubmissionService.ts b/src/service/SubmissionService.ts index dd487e7..97d5cb7 100644 --- a/src/service/SubmissionService.ts +++ b/src/service/SubmissionService.ts @@ -10,16 +10,23 @@ import { ViewColumn, commands } from "vscode"; import { BaseWebViewService } from "./BaseWebviewService"; import { markdownService } from "./MarkdownService"; -import { ISubmitEvent } from "../model/Model"; +import { ISubmitEvent, ITestSolutionData } from "../model/Model"; import { IWebViewOption } from "../model/Model"; import { promptHintMessage } from "../utils/OutputUtils"; import { isAnswerDiffColor } from "../utils/ConfigUtils"; import { statusBarTimeService } from "../service/StatusBarTimeService"; +import { eventService } from "./EventService"; class SubmissionService extends BaseWebViewService { protected readonly viewType: string = "leetcode.submission"; private result: IResult; - public show(resultString: string): void { + private tempTestCase: Map = new Map(); + + public getTSDByQid(qid: string): ITestSolutionData | undefined { + return this.tempTestCase.get(qid); + } + + public show(resultString: string, tsd?: ITestSolutionData): void { this.result = this.parseResult(resultString); const temp = this.getSubmitEvent(); @@ -29,6 +36,13 @@ class SubmissionService extends BaseWebViewService { } this.showWebviewInternal(); this.showKeybindingsHint(); + + let submit_event: ISubmitEvent = this.getSubmitEvent(); + if (tsd != undefined) { + let qid = submit_event?.qid?.toString(); + this.tempTestCase.set(qid, tsd); + } + eventService.emit("submit", submit_event); } public getSubmitEvent(): ISubmitEvent { return this.result.system_message as unknown as ISubmitEvent; diff --git a/src/utils/ConfigUtils.ts b/src/utils/ConfigUtils.ts index 23a36dc..6b25b3d 100644 --- a/src/utils/ConfigUtils.ts +++ b/src/utils/ConfigUtils.ts @@ -115,6 +115,7 @@ export function getEditorShortcuts(): string[] { "case", "allcase", "test", + "retest", "solution", "debug", ]);