Skip to content

Commit 45bd545

Browse files
authored
Centralize the logic about wsl (LeetCode-OpenSource#55)
1 parent eef7106 commit 45bd545

File tree

12 files changed

+160
-69
lines changed

12 files changed

+160
-69
lines changed

src/commands/list.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"use strict";
22

33
import * as vscode from "vscode";
4+
import { leetCodeExecutor } from "../leetCodeExecutor";
45
import { leetCodeManager } from "../leetCodeManager";
5-
import { leetCodeBinaryPath, ProblemState, UserStatus } from "../shared";
6-
import { executeCommand } from "../utils/cpUtils";
6+
import { ProblemState, UserStatus } from "../shared";
77
import { DialogType, promptForOpenOutputChannel } from "../utils/uiUtils";
88

99
export interface IProblem {
@@ -22,8 +22,8 @@ export async function listProblems(): Promise<IProblem[]> {
2222
return [];
2323
}
2424
const leetCodeConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("leetcode");
25-
const showLocked: boolean | undefined = leetCodeConfig.get<boolean>("showLocked");
26-
const result: string = await executeCommand("node", showLocked ? [leetCodeBinaryPath, "list"] : [leetCodeBinaryPath, "list", "-q", "L"]);
25+
const showLocked: boolean = !!leetCodeConfig.get<boolean>("showLocked");
26+
const result: string = await leetCodeExecutor.listProblems(showLocked);
2727
const problems: IProblem[] = [];
2828
const lines: string[] = result.split("\n");
2929
const reg: RegExp = /^(.)\s(.{1,2})\s(.)\s\[\s*(\d*)\]\s*(.*)\s*(Easy|Medium|Hard)\s*\((\s*\d+\.\d+ %)\)/;

src/commands/session.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"use strict";
22

33
import * as vscode from "vscode";
4+
import { leetCodeExecutor } from "../leetCodeExecutor";
45
import { leetCodeManager } from "../leetCodeManager";
5-
import { IQuickItemEx, leetCodeBinaryPath } from "../shared";
6-
import { executeCommand } from "../utils/cpUtils";
6+
import { IQuickItemEx } from "../shared";
77
import { DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils";
88

99
export async function getSessionList(): Promise<ISession[]> {
@@ -12,7 +12,7 @@ export async function getSessionList(): Promise<ISession[]> {
1212
promptForSignIn();
1313
return [];
1414
}
15-
const result: string = await executeCommand("node", [leetCodeBinaryPath, "session"]);
15+
const result: string = await leetCodeExecutor.listSessions();
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,7 +41,7 @@ export async function selectSession(): Promise<void> {
4141
return;
4242
}
4343
try {
44-
await executeCommand("node", [leetCodeBinaryPath, "session", "-e", choice.value]);
44+
await leetCodeExecutor.enableSession(choice.value);
4545
vscode.window.showInformationMessage(`Successfully switched to session '${choice.label}'.`);
4646
await vscode.commands.executeCommand("leetcode.refreshExplorer");
4747
} catch (error) {
@@ -81,7 +81,7 @@ export async function createSession(): Promise<void> {
8181
return;
8282
}
8383
try {
84-
await executeCommand("node", [leetCodeBinaryPath, "session", "-c", session]);
84+
await leetCodeExecutor.createSession(session);
8585
vscode.window.showInformationMessage("New session created, you can switch to it by clicking the status bar.");
8686
} catch (error) {
8787
await promptForOpenOutputChannel("Failed to create session. Please open the output channel for details.", DialogType.error);

src/commands/show.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
import * as fse from "fs-extra";
44
import * as vscode from "vscode";
5+
import { leetCodeExecutor } from "../leetCodeExecutor";
56
import { LeetCodeNode } from "../leetCodeExplorer";
67
import { leetCodeManager } from "../leetCodeManager";
7-
import { IQuickItemEx, languages, leetCodeBinaryPath, ProblemState } from "../shared";
8-
import { executeCommandWithProgress } from "../utils/cpUtils";
8+
import { IQuickItemEx, languages, ProblemState } from "../shared";
99
import { DialogOptions, DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils";
1010
import { selectWorkspaceFolder } from "../utils/workspaceUtils";
1111
import * as wsl from "../utils/wslUtils";
@@ -50,11 +50,11 @@ async function showProblemInternal(id: string): Promise<void> {
5050

5151
const outdir: string = await selectWorkspaceFolder();
5252
await fse.ensureDir(outdir);
53-
const result: string = await executeCommandWithProgress("Fetching problem data...", "node", [leetCodeBinaryPath, "show", id, "-gx", "-l", language, "-o", `"${outdir}"`]);
53+
const result: string = await leetCodeExecutor.showProblem(id, language, outdir);
5454
const reg: RegExp = /\* Source Code:\s*(.*)/;
5555
const match: RegExpMatchArray | null = result.match(reg);
5656
if (match && match.length >= 2) {
57-
const filePath: string = wsl.useWsl() ? wsl.toWinPath(match[1].trim()) : match[1].trim();
57+
const filePath: string = wsl.useWsl() ? await wsl.toWinPath(match[1].trim()) : match[1].trim();
5858

5959
await vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false });
6060
} else {

src/commands/submit.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
"use strict";
22

33
import * as vscode from "vscode";
4+
import { leetCodeExecutor } from "../leetCodeExecutor";
45
import { leetCodeManager } from "../leetCodeManager";
5-
import { leetCodeBinaryPath } from "../shared";
6-
import { executeCommandWithProgress } from "../utils/cpUtils";
76
import { DialogType, promptForOpenOutputChannel, promptForSignIn, showResultFile } from "../utils/uiUtils";
87
import { getActivefilePath } from "../utils/workspaceUtils";
98

@@ -19,7 +18,7 @@ export async function submitSolution(uri?: vscode.Uri): Promise<void> {
1918
}
2019

2120
try {
22-
const result: string = await executeCommandWithProgress("Submitting to LeetCode...", "node", [leetCodeBinaryPath, "submit", `"${filePath}"`]);
21+
const result: string = await leetCodeExecutor.submitSolution(filePath);
2322
await showResultFile(result);
2423
} catch (error) {
2524
await promptForOpenOutputChannel("Failed to submit the solution. Please open the output channel for details.", DialogType.error);

src/commands/test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import * as fse from "fs-extra";
44
import * as vscode from "vscode";
5+
import { leetCodeExecutor } from "../leetCodeExecutor";
56
import { leetCodeManager } from "../leetCodeManager";
6-
import { IQuickItemEx, leetCodeBinaryPath, UserStatus } from "../shared";
7-
import { executeCommandWithProgress } from "../utils/cpUtils";
7+
import { IQuickItemEx, UserStatus } from "../shared";
88
import { DialogType, promptForOpenOutputChannel, showFileSelectDialog, showResultFile } from "../utils/uiUtils";
99
import { getActivefilePath } from "../utils/workspaceUtils";
1010

@@ -47,7 +47,7 @@ export async function testSolution(uri?: vscode.Uri): Promise<void> {
4747
let result: string | undefined;
4848
switch (choice.value) {
4949
case ":default":
50-
result = await executeCommandWithProgress("Submitting to LeetCode...", "node", [leetCodeBinaryPath, "test", `"${filePath}"`]);
50+
result = await leetCodeExecutor.testSolution(filePath);
5151
break;
5252
case ":direct":
5353
const testString: string | undefined = await vscode.window.showInputBox({
@@ -57,15 +57,15 @@ export async function testSolution(uri?: vscode.Uri): Promise<void> {
5757
ignoreFocusOut: true,
5858
});
5959
if (testString) {
60-
result = await executeCommandWithProgress("Submitting to LeetCode...", "node", [leetCodeBinaryPath, "test", `"${filePath}"`, "-t", `"${testString.replace(/"/g, "")}"`]);
60+
result = await leetCodeExecutor.testSolution(filePath, testString.replace(/"/g, ""));
6161
}
6262
break;
6363
case ":file":
6464
const testFile: vscode.Uri[] | undefined = await showFileSelectDialog();
6565
if (testFile && testFile.length) {
6666
const input: string = await fse.readFile(testFile[0].fsPath, "utf-8");
6767
if (input.trim()) {
68-
result = await executeCommandWithProgress("Submitting to LeetCode...", "node", [leetCodeBinaryPath, "test", `"${filePath}"`, "-t", `"${input.replace(/"/g, "").replace(/\r?\n/g, "\\n")}"`]);
68+
result = await leetCodeExecutor.testSolution(filePath, input.replace(/"/g, "").replace(/\r?\n/g, "\\n"));
6969
} else {
7070
vscode.window.showErrorMessage("The selected test file must not be empty.");
7171
}

src/extension.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import * as show from "./commands/show";
66
import * as submit from "./commands/submit";
77
import * as test from "./commands/test";
88
import { leetCodeChannel } from "./leetCodeChannel";
9+
import { leetCodeExecutor } from "./leetCodeExecutor";
910
import { LeetCodeNode, LeetCodeTreeDataProvider } from "./leetCodeExplorer";
1011
import { leetCodeManager } from "./leetCodeManager";
1112
import { leetCodeStatusBarItem } from "./leetCodeStatusBarItem";
12-
import { isNodeInstalled } from "./utils/nodeUtils";
1313

1414
export async function activate(context: vscode.ExtensionContext): Promise<void> {
15-
if (!await isNodeInstalled()) {
15+
if (!await leetCodeExecutor.meetRequirements()) {
1616
return;
1717
}
1818
leetCodeManager.getLoginStatus();

src/leetCodeExecutor.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
"use strict";
2+
3+
import * as cp from "child_process";
4+
import * as opn from "opn";
5+
import * as path from "path";
6+
import * as vscode from "vscode";
7+
import { executeCommand, executeCommandWithProgress } from "./utils/cpUtils";
8+
import { DialogOptions } from "./utils/uiUtils";
9+
import * as wsl from "./utils/wslUtils";
10+
11+
export interface ILeetCodeExecutor {
12+
meetRequirements(): Promise<boolean>;
13+
getLeetCodeBinaryPath(): Promise<string>;
14+
15+
/* section for user command */
16+
getUserInfo(): Promise<string>;
17+
signOut(): Promise<string>;
18+
// TODO: implement login when leetcode-cli support login in batch mode.
19+
// signIn(): Promise<string>;
20+
21+
/* section for problem command */
22+
listProblems(showLocked: boolean): Promise<string>;
23+
showProblem(id: string, language: string, outdir: string): Promise<string>;
24+
25+
/* section for session command */
26+
listSessions(): Promise<string>;
27+
enableSession(name: string): Promise<string>;
28+
createSession(name: string): Promise<string>;
29+
30+
/* section for solution command */
31+
submitSolution(filePath: string): Promise<string>;
32+
testSolution(filePath: string, testString?: string): Promise<string>;
33+
}
34+
35+
class LeetCodeExecutor implements ILeetCodeExecutor {
36+
private leetCodeBinaryPath: string;
37+
private leetCodeBinaryPathInWsl: string;
38+
39+
constructor() {
40+
this.leetCodeBinaryPath = path.join(__dirname, "..", "..", "node_modules", "leetcode-cli", "bin", "leetcode");
41+
this.leetCodeBinaryPathInWsl = "";
42+
}
43+
44+
public async getLeetCodeBinaryPath(): Promise<string> {
45+
if (wsl.useWsl()) {
46+
if (!this.leetCodeBinaryPathInWsl) {
47+
this.leetCodeBinaryPathInWsl = `${await wsl.toWslPath(this.leetCodeBinaryPath)}`;
48+
}
49+
return `"${this.leetCodeBinaryPathInWsl}"`;
50+
}
51+
return `"${this.leetCodeBinaryPath}"`;
52+
}
53+
54+
public async meetRequirements(): Promise<boolean> {
55+
try {
56+
await this.executeCommandEx("node", ["-v"]);
57+
return true;
58+
} catch (error) {
59+
const choice: vscode.MessageItem | undefined = await vscode.window.showErrorMessage(
60+
"LeetCode extension needs Node.js installed in environment path",
61+
DialogOptions.open,
62+
);
63+
if (choice === DialogOptions.open) {
64+
opn("https://nodejs.org");
65+
}
66+
return false;
67+
}
68+
}
69+
70+
public async getUserInfo(): Promise<string> {
71+
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "user"]);
72+
}
73+
74+
public async signOut(): Promise<string> {
75+
return await await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "user", "-L"]);
76+
}
77+
78+
public async listProblems(showLocked: boolean): Promise<string> {
79+
return await this.executeCommandEx("node", showLocked ?
80+
[await this.getLeetCodeBinaryPath(), "list"] :
81+
[await this.getLeetCodeBinaryPath(), "list", "-q", "L"],
82+
);
83+
}
84+
85+
public async showProblem(id: string, language: string, outdir: string): Promise<string> {
86+
return await this.executeCommandWithProgressEx("Fetching problem data...", "node", [await this.getLeetCodeBinaryPath(), "show", id, "-gx", "-l", language, "-o", `"${outdir}"`]);
87+
}
88+
89+
public async listSessions(): Promise<string> {
90+
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "session"]);
91+
}
92+
93+
public async enableSession(name: string): Promise<string> {
94+
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "session", "-e", name]);
95+
}
96+
97+
public async createSession(name: string): Promise<string> {
98+
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "session", "-c", name]);
99+
}
100+
101+
public async submitSolution(filePath: string): Promise<string> {
102+
return await this.executeCommandWithProgressEx("Submitting to LeetCode...", "node", [await this.getLeetCodeBinaryPath(), "submit", `"${filePath}"`]);
103+
}
104+
105+
public async testSolution(filePath: string, testString?: string): Promise<string> {
106+
if (testString) {
107+
return await this.executeCommandWithProgressEx("Submitting to LeetCode...", "node", [await this.getLeetCodeBinaryPath(), "test", `"${filePath}"`, "-t", `"${testString}"`]);
108+
}
109+
return await this.executeCommandWithProgressEx("Submitting to LeetCode...", "node", [await this.getLeetCodeBinaryPath(), "test", `"${filePath}"`]);
110+
}
111+
112+
private async executeCommandEx(command: string, args: string[], options: cp.SpawnOptions = { shell: true }): Promise<string> {
113+
if (wsl.useWsl()) {
114+
return await executeCommand("wsl", [command].concat(args), options);
115+
}
116+
return await executeCommand(command, args, options);
117+
}
118+
119+
private async executeCommandWithProgressEx(message: string, command: string, args: string[], options: cp.SpawnOptions = { shell: true }): Promise<string> {
120+
if (wsl.useWsl()) {
121+
return await executeCommandWithProgress(message, "wsl", [command].concat(args), options);
122+
}
123+
return await executeCommandWithProgress(message, command, args, options);
124+
}
125+
}
126+
127+
export const leetCodeExecutor: ILeetCodeExecutor = new LeetCodeExecutor();

src/leetCodeManager.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import * as cp from "child_process";
44
import { EventEmitter } from "events";
55
import * as vscode from "vscode";
66
import { leetCodeChannel } from "./leetCodeChannel";
7+
import { leetCodeExecutor } from "./leetCodeExecutor";
78
import { UserStatus } from "./shared";
8-
import { leetCodeBinaryPath } from "./shared";
9-
import { executeCommand } from "./utils/cpUtils";
109
import { DialogType, promptForOpenOutputChannel } from "./utils/uiUtils";
1110
import * as wsl from "./utils/wslUtils";
1211

@@ -30,7 +29,7 @@ class LeetCodeManager extends EventEmitter implements ILeetCodeManager {
3029

3130
public async getLoginStatus(): Promise<void> {
3231
try {
33-
const result: string = await executeCommand("node", [leetCodeBinaryPath, "user"]);
32+
const result: string = await leetCodeExecutor.getUserInfo();
3433
this.currentUser = result.slice("You are now login as".length).trim();
3534
this.userStatus = UserStatus.SignedIn;
3635
} catch (error) {
@@ -46,6 +45,8 @@ class LeetCodeManager extends EventEmitter implements ILeetCodeManager {
4645
const userName: string | undefined = await new Promise(async (resolve: (res: string | undefined) => void, reject: (e: Error) => void): Promise<void> => {
4746
let result: string = "";
4847

48+
const leetCodeBinaryPath: string = await leetCodeExecutor.getLeetCodeBinaryPath();
49+
4950
const childProc: cp.ChildProcess = wsl.useWsl()
5051
? cp.spawn("wsl", ["node", leetCodeBinaryPath, "user", "-l"], { shell: true })
5152
: cp.spawn("node", [leetCodeBinaryPath, "user", "-l"], { shell: true });
@@ -102,7 +103,7 @@ class LeetCodeManager extends EventEmitter implements ILeetCodeManager {
102103

103104
public async signOut(): Promise<void> {
104105
try {
105-
await executeCommand("node", [leetCodeBinaryPath, "user", "-L"]);
106+
await leetCodeExecutor.signOut();
106107
vscode.window.showInformationMessage("Successfully signed out.");
107108
this.currentUser = undefined;
108109
this.userStatus = UserStatus.SignedOut;

src/shared.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
11
"use strict";
22

3-
import * as path from "path";
43
import * as vscode from "vscode";
5-
import * as wsl from "./utils/wslUtils";
6-
7-
let binPath: string = path.join(__dirname, "..", "..", "node_modules", "leetcode-cli", "bin", "leetcode");
8-
9-
if (wsl.useWsl()) {
10-
binPath = wsl.toWslPath(binPath);
11-
}
12-
13-
export const leetCodeBinaryPath: string = `"${binPath}"`;
144

155
export interface IQuickItemEx<T> extends vscode.QuickPickItem {
166
value: T;

src/utils/cpUtils.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@
33
import * as cp from "child_process";
44
import * as vscode from "vscode";
55
import { leetCodeChannel } from "../leetCodeChannel";
6-
import * as wsl from "./wslUtils";
76

87
export async function executeCommand(command: string, args: string[], options: cp.SpawnOptions = { shell: true }): Promise<string> {
98
return new Promise((resolve: (res: string) => void, reject: (e: Error) => void): void => {
109
let result: string = "";
1110

12-
const childProc: cp.ChildProcess = wsl.useWsl()
13-
? cp.spawn("wsl", [command].concat(args), options)
14-
: cp.spawn(command, args, options);
11+
const childProc: cp.ChildProcess = cp.spawn(command, args, options);
1512

1613
childProc.stdout.on("data", (data: string | Buffer) => {
1714
data = data.toString();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy