Skip to content

Commit f58422f

Browse files
authored
support test solution (#16)
* support test solution * escape the line seperator * update readme
1 parent 5b4bff1 commit f58422f

File tree

11 files changed

+144
-20
lines changed

11 files changed

+144
-20
lines changed

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ All notable changes to the "leetcode" extension will be documented in this file.
33

44
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
55

6+
## [0.3.0]
7+
### Added
8+
- Test current solution file [(#15)](https://github.com/jdneo/vscode-leetcode/issues/15)
9+
610
## [0.2.1]
711
### Fixed
812
- Fix the wrong icon bug in LeetCode Explorer [(#9)](https://github.com/jdneo/vscode-leetcode/issues/9)

Diff for: README.md

+10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Solve LeetCode problems in VS Code.
1818
- Switch and create session
1919
- Show problems in explorer
2020
- Search problems by keywords
21+
- Test solutions by customized test case
2122
- Submit solutions to LeetCode
2223

2324
### Sign In and Sign Out
@@ -32,6 +33,9 @@ Solve LeetCode problems in VS Code.
3233
### Search Problems by Keywords
3334
![SearchProblem](resources/gif/searchproblem.gif)
3435

36+
### Test solutions by customized test case
37+
![TestSolution](resources/gif/testsolution.gif)
38+
3539
### Submit Solutions to LeetCode
3640
![SubmitSolution](resources/gif/solveproblem.gif)
3741

@@ -43,6 +47,7 @@ This extension provides several commands in the Command Palette (F1 or Ctrl + Sh
4347
- **LeetCode: Create new session** - Create a new session
4448
- **LeetCode: Refresh** - Refresh the LeetCode Explorer
4549
- **LeetCode: Search Problem** - Search for problems by keywords
50+
- **LeetCode: Test Current File** - Test the solution by customized test case
4651
- **LeetCode: Submit** - Submit the solution to LeetCode
4752

4853
## Known Issues:
@@ -68,6 +73,7 @@ This extension is based on [@skygragon](https://github.com/skygragon)'s [leetcod
6873
- 切换及创建 session
6974
- 在 Explorer 中展示题目
7075
- 根据关键字搜索题目
76+
- 用自定义测试用例测试答案
7177
- 向 LeetCode 提交答案
7278

7379
### 登入及登出
@@ -82,6 +88,9 @@ This extension is based on [@skygragon](https://github.com/skygragon)'s [leetcod
8288
### 根据关键字搜索题目
8389
![SearchProblem](resources/gif/searchproblem.gif)
8490

91+
### 用自定义测试用例测试答案
92+
![TestSolution](resources/gif/testsolution.gif)
93+
8594
### 向 LeetCode 提交答案
8695
![SubmitSolution](resources/gif/solveproblem.gif)
8796

@@ -93,6 +102,7 @@ This extension is based on [@skygragon](https://github.com/skygragon)'s [leetcod
93102
- **LeetCode: Create new session** - 创建一个新的答题进度存档
94103
- **LeetCode: Refresh** - 刷新左侧题目列表视图
95104
- **LeetCode: Search Problem** - 根据关键字搜索题目
105+
- **LeetCode: Test Current File** - 用自定义测试用例测试答案
96106
- **LeetCode: Submit** - 提交答案到 LeetCode
97107

98108
## 已知问题

Diff for: package.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vscode-leetcode",
33
"displayName": "LeetCode",
44
"description": "Solve LeetCode problems in VS Code",
5-
"version": "0.2.1",
5+
"version": "0.3.0",
66
"author": "Sheng Chen",
77
"publisher": "shengchen",
88
"icon": "resources/LeetCode.png",
@@ -32,6 +32,7 @@
3232
"onCommand:leetcode.refreshExplorer",
3333
"onCommand:leetcode.showProblem",
3434
"onCommand:leetcode.searchProblem",
35+
"onCommand:leetcode.testSolution",
3536
"onCommand:leetcode.submitSolution",
3637
"onView:leetCodeExplorer"
3738
],
@@ -78,6 +79,11 @@
7879
"category": "LeetCode",
7980
"icon": "resources/search.png"
8081
},
82+
{
83+
"command": "leetcode.testSolution",
84+
"title": "Test Current File",
85+
"category": "LeetCode"
86+
},
8187
{
8288
"command": "leetcode.submitSolution",
8389
"title": "Submit",

Diff for: resources/gif/testsolution.gif

351 KB
Loading

Diff for: src/commands/list.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function listProblems(channel: vscode.OutputChannel): Promise<IProb
3737
}
3838
return problems.reverse();
3939
} catch (error) {
40-
await promptForOpenOutputChannel("Failed to list problems. Please open the output channel for details", DialogType.error, channel);
40+
await promptForOpenOutputChannel("Failed to list problems. Please open the output channel for details.", DialogType.error, channel);
4141
return [];
4242
}
4343
}

Diff for: src/commands/session.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import * as vscode from "vscode";
44
import { leetCodeManager } from "../leetCodeManager";
55
import { IQuickItemEx, leetCodeBinaryPath } from "../shared";
6-
import * as cp from "../utils/cpUtils";
6+
import { executeCommand } from "../utils/cpUtils";
77
import { DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils";
88

99
export async function getSessionList(channel: vscode.OutputChannel): Promise<ISession[]> {
@@ -12,7 +12,7 @@ export async function getSessionList(channel: vscode.OutputChannel): Promise<ISe
1212
promptForSignIn();
1313
return [];
1414
}
15-
const result: string = await cp.executeCommand(channel, "node", [leetCodeBinaryPath, "session"]);
15+
const result: string = await executeCommand(channel, "node", [leetCodeBinaryPath, "session"]);
1616
const lines: string[] = result.split("\n");
1717
const sessions: ISession[] = [];
1818
const reg: RegExp = /(.?)\s*(\d+)\s+(.*)\s+(\d+ \(\s*\d+\.\d+ %\))\s+(\d+ \(\s*\d+\.\d+ %\))/;
@@ -41,11 +41,11 @@ export async function selectSession(channel: vscode.OutputChannel): Promise<void
4141
return;
4242
}
4343
try {
44-
await cp.executeCommand(channel, "node", [leetCodeBinaryPath, "session", "-e", choice.value]);
44+
await executeCommand(channel, "node", [leetCodeBinaryPath, "session", "-e", choice.value]);
4545
vscode.window.showInformationMessage(`Successfully switched to session '${choice.label}'.`);
4646
await vscode.commands.executeCommand("leetcode.refreshExplorer");
4747
} catch (error) {
48-
await promptForOpenOutputChannel("Failed to switch session. Please open the output channel for details", DialogType.error, channel);
48+
await promptForOpenOutputChannel("Failed to switch session. Please open the output channel for details.", DialogType.error, channel);
4949
}
5050
}
5151

@@ -67,7 +67,7 @@ async function parseSessionsToPicks(channel: vscode.OutputChannel): Promise<Arra
6767
});
6868
resolve(picks);
6969
} catch (error) {
70-
return await promptForOpenOutputChannel("Failed to list sessions. Please open the output channel for details", DialogType.error, channel);
70+
return await promptForOpenOutputChannel("Failed to list sessions. Please open the output channel for details.", DialogType.error, channel);
7171
}
7272
});
7373
}
@@ -81,10 +81,10 @@ export async function createSession(channel: vscode.OutputChannel): Promise<void
8181
return;
8282
}
8383
try {
84-
await cp.executeCommand(channel, "node", [leetCodeBinaryPath, "session", "-c", session]);
84+
await executeCommand(channel, "node", [leetCodeBinaryPath, "session", "-c", session]);
8585
vscode.window.showInformationMessage("New session created, you can switch to it by clicking the status bar.");
8686
} catch (error) {
87-
await promptForOpenOutputChannel("Failed to create session. Please open the output channel for details", DialogType.error, channel);
87+
await promptForOpenOutputChannel("Failed to create session. Please open the output channel for details.", DialogType.error, channel);
8888
}
8989
}
9090

Diff for: src/commands/show.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ async function showProblemInternal(channel: vscode.OutputChannel, id: string): P
5555
if (match && match.length >= 2) {
5656
await vscode.window.showTextDocument(vscode.Uri.file(match[1].trim()), { preview: false });
5757
} else {
58-
throw new Error("Failed to fetch the problem information");
58+
throw new Error("Failed to fetch the problem information.");
5959
}
6060

6161
if (!defaultLanguage && leetCodeConfig.get<boolean>("showSetDefaultLanguageHint")) {
@@ -72,7 +72,7 @@ async function showProblemInternal(channel: vscode.OutputChannel, id: string): P
7272
}
7373
}
7474
} catch (error) {
75-
await promptForOpenOutputChannel("Failed to fetch the problem information. Please open the output channel for details", DialogType.error, channel);
75+
await promptForOpenOutputChannel("Failed to fetch the problem information. Please open the output channel for details.", DialogType.error, channel);
7676
}
7777
}
7878

Diff for: src/commands/submit.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
"use strict";
22

3-
import * as fse from "fs-extra";
4-
import * as os from "os";
5-
import * as path from "path";
63
import * as vscode from "vscode";
74
import { leetCodeManager } from "../leetCodeManager";
85
import { leetCodeBinaryPath } from "../shared";
96
import { executeCommand } from "../utils/cpUtils";
10-
import { DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils";
7+
import { DialogType, promptForOpenOutputChannel, promptForSignIn, showResultFile } from "../utils/uiUtils";
118

129
export async function submitSolution(channel: vscode.OutputChannel): Promise<void> {
1310
if (!leetCodeManager.getUser()) {
@@ -25,11 +22,8 @@ export async function submitSolution(channel: vscode.OutputChannel): Promise<voi
2522
const filePath: string = textEditor.document.uri.fsPath;
2623
try {
2724
const result: string = await executeCommand(channel, "node", [leetCodeBinaryPath, "submit", filePath]);
28-
const resultPath: string = path.join(os.homedir(), ".leetcode", "Result");
29-
await fse.ensureFile(resultPath);
30-
await fse.writeFile(resultPath, result);
31-
await vscode.window.showTextDocument(vscode.Uri.file(resultPath));
25+
await showResultFile(result);
3226
} catch (error) {
33-
await promptForOpenOutputChannel("Failed to submit the solution. Please open the output channel for details", DialogType.error, channel);
27+
await promptForOpenOutputChannel("Failed to submit the solution. Please open the output channel for details.", DialogType.error, channel);
3428
}
3529
}

Diff for: src/commands/test.ts

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"use strict";
2+
3+
import * as fse from "fs-extra";
4+
import * as vscode from "vscode";
5+
import { leetCodeManager } from "../leetCodeManager";
6+
import { IQuickItemEx, leetCodeBinaryPath, UserStatus } from "../shared";
7+
import { executeCommand } from "../utils/cpUtils";
8+
import { DialogType, promptForOpenOutputChannel, showFileSelectDialog, showResultFile } from "../utils/uiUtils";
9+
10+
export async function testSolution(channel: vscode.OutputChannel): Promise<void> {
11+
try {
12+
if (leetCodeManager.getStatus() === UserStatus.SignedOut) {
13+
return;
14+
}
15+
16+
const activeText: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
17+
if (!activeText) {
18+
vscode.window.showErrorMessage("Please open a LeetCode solution file first.");
19+
return;
20+
}
21+
22+
const filePath = activeText.document.uri.fsPath;
23+
const picks: Array<IQuickItemEx<string>> = [];
24+
picks.push(
25+
{
26+
label: "$(three-bars) Default test cases",
27+
description: "",
28+
detail: "Test with the default cases",
29+
value: ":default",
30+
},
31+
{
32+
label: "$(pencil) Write directly...",
33+
description: "",
34+
detail: "Write test cases in input box",
35+
value: ":direct",
36+
},
37+
{
38+
label: "$(file-text) Browse...",
39+
description: "",
40+
detail: "Test with the writen cases in file",
41+
value: ":file",
42+
},
43+
);
44+
const choice: IQuickItemEx<string> | undefined = await vscode.window.showQuickPick(picks);
45+
if (!choice) {
46+
return;
47+
}
48+
49+
let result: string | undefined;
50+
switch (choice.value) {
51+
case ":default":
52+
result = await executeCommand(channel, "node", [leetCodeBinaryPath, "test", filePath]);
53+
break;
54+
case ":direct":
55+
const testString: string | undefined = await vscode.window.showInputBox({
56+
prompt: "Enter the test cases.",
57+
validateInput: (s: string) => s && s.trim() ? undefined : "Test case must not be empty.",
58+
placeHolder: "Example: [1,2,3]\\n4",
59+
ignoreFocusOut: true,
60+
});
61+
if (testString) {
62+
result = await executeCommand(channel, "node", [leetCodeBinaryPath, "test", filePath, "-t", `"${testString.replace(/"/g, "")}"`]);
63+
}
64+
break;
65+
case ":file":
66+
const testFile: vscode.Uri[] | undefined = await showFileSelectDialog();
67+
if (testFile && testFile.length) {
68+
const input: string = await fse.readFile(testFile[0].fsPath, "utf-8");
69+
if (input.trim()) {
70+
result = await executeCommand(channel, "node", [leetCodeBinaryPath, "test", filePath, "-t", `"${input.replace(/"/g, "").replace(/\r?\n/g, "\\n")}"`]);
71+
} else {
72+
vscode.window.showErrorMessage("The selected test file must not be empty.");
73+
}
74+
}
75+
break;
76+
default:
77+
break;
78+
}
79+
if (!result) {
80+
return;
81+
}
82+
await showResultFile(result);
83+
} catch (error) {
84+
await promptForOpenOutputChannel("Failed to test the solution. Please open the output channel for details.", DialogType.error, channel);
85+
}
86+
}

Diff for: src/extension.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as vscode from "vscode";
44
import * as session from "./commands/session";
55
import * as show from "./commands/show";
66
import * as submit from "./commands/submit";
7+
import * as test from "./commands/test";
78
import { LeetCodeNode, LeetCodeTreeDataProvider } from "./leetCodeExplorer";
89
import { leetCodeManager } from "./leetCodeManager";
910
import { leetCodeStatusBarItem } from "./leetCodeStatusBarItem";
@@ -26,6 +27,7 @@ export async function activate(context: vscode.ExtensionContext) {
2627
vscode.commands.registerCommand("leetcode.showProblem", (node: LeetCodeNode) => show.showProblem(channel, node)),
2728
vscode.commands.registerCommand("leetcode.searchProblem", () => show.searchProblem(channel)),
2829
vscode.commands.registerCommand("leetcode.refreshExplorer", () => leetCodeTreeDataProvider.refresh()),
30+
vscode.commands.registerCommand("leetcode.testSolution", () => test.testSolution(channel)),
2931
vscode.commands.registerCommand("leetcode.submitSolution", () => submit.submitSolution(channel)),
3032
);
3133

Diff for: src/utils/uiUtils.ts

+22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
"use strict";
22

3+
import * as fse from "fs-extra";
34
import * as opn from "opn";
5+
import * as os from "os";
6+
import * as path from "path";
47
import * as vscode from "vscode";
58

69
export namespace DialogOptions {
@@ -51,6 +54,25 @@ export async function promptForSignIn(): Promise<void> {
5154
}
5255
}
5356

57+
export async function showFileSelectDialog(): Promise<vscode.Uri[] | undefined> {
58+
const defaultUri: vscode.Uri | undefined = vscode.workspace.rootPath ? vscode.Uri.file(vscode.workspace.rootPath) : undefined;
59+
const options: vscode.OpenDialogOptions = {
60+
defaultUri,
61+
canSelectFiles: true,
62+
canSelectFolders: false,
63+
canSelectMany: false,
64+
openLabel: "Select",
65+
};
66+
return await vscode.window.showOpenDialog(options);
67+
}
68+
69+
export async function showResultFile(result: string): Promise<void> {
70+
const resultPath: string = path.join(os.homedir(), ".leetcode", "Result");
71+
await fse.ensureFile(resultPath);
72+
await fse.writeFile(resultPath, result);
73+
await vscode.window.showTextDocument(vscode.Uri.file(resultPath));
74+
}
75+
5476
export enum DialogType {
5577
info = "info",
5678
warning = "warning",

0 commit comments

Comments
 (0)