Skip to content

Centralize the logic about wsl #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/commands/list.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use strict";

import * as vscode from "vscode";
import { leetCodeExecutor } from "../leetCodeExecutor";
import { leetCodeManager } from "../leetCodeManager";
import { leetCodeBinaryPath, ProblemState, UserStatus } from "../shared";
import { executeCommand } from "../utils/cpUtils";
import { ProblemState, UserStatus } from "../shared";
import { DialogType, promptForOpenOutputChannel } from "../utils/uiUtils";

export interface IProblem {
Expand All @@ -22,8 +22,8 @@ export async function listProblems(): Promise<IProblem[]> {
return [];
}
const leetCodeConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("leetcode");
const showLocked: boolean | undefined = leetCodeConfig.get<boolean>("showLocked");
const result: string = await executeCommand("node", showLocked ? [leetCodeBinaryPath, "list"] : [leetCodeBinaryPath, "list", "-q", "L"]);
const showLocked: boolean = !!leetCodeConfig.get<boolean>("showLocked");
const result: string = await leetCodeExecutor.listProblems(showLocked);
const problems: IProblem[] = [];
const lines: string[] = result.split("\n");
const reg: RegExp = /^(.)\s(.{1,2})\s(.)\s\[\s*(\d*)\]\s*(.*)\s*(Easy|Medium|Hard)\s*\((\s*\d+\.\d+ %)\)/;
Expand Down
10 changes: 5 additions & 5 deletions src/commands/session.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use strict";

import * as vscode from "vscode";
import { leetCodeExecutor } from "../leetCodeExecutor";
import { leetCodeManager } from "../leetCodeManager";
import { IQuickItemEx, leetCodeBinaryPath } from "../shared";
import { executeCommand } from "../utils/cpUtils";
import { IQuickItemEx } from "../shared";
import { DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils";

export async function getSessionList(): Promise<ISession[]> {
Expand All @@ -12,7 +12,7 @@ export async function getSessionList(): Promise<ISession[]> {
promptForSignIn();
return [];
}
const result: string = await executeCommand("node", [leetCodeBinaryPath, "session"]);
const result: string = await leetCodeExecutor.listSessions();
const lines: string[] = result.split("\n");
const sessions: ISession[] = [];
const reg: RegExp = /(.?)\s*(\d+)\s+(.*)\s+(\d+ \(\s*\d+\.\d+ %\))\s+(\d+ \(\s*\d+\.\d+ %\))/;
Expand Down Expand Up @@ -41,7 +41,7 @@ export async function selectSession(): Promise<void> {
return;
}
try {
await executeCommand("node", [leetCodeBinaryPath, "session", "-e", choice.value]);
await leetCodeExecutor.enableSession(choice.value);
vscode.window.showInformationMessage(`Successfully switched to session '${choice.label}'.`);
await vscode.commands.executeCommand("leetcode.refreshExplorer");
} catch (error) {
Expand Down Expand Up @@ -81,7 +81,7 @@ export async function createSession(): Promise<void> {
return;
}
try {
await executeCommand("node", [leetCodeBinaryPath, "session", "-c", session]);
await leetCodeExecutor.createSession(session);
vscode.window.showInformationMessage("New session created, you can switch to it by clicking the status bar.");
} catch (error) {
await promptForOpenOutputChannel("Failed to create session. Please open the output channel for details.", DialogType.error);
Expand Down
8 changes: 4 additions & 4 deletions src/commands/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import * as fse from "fs-extra";
import * as vscode from "vscode";
import { leetCodeExecutor } from "../leetCodeExecutor";
import { LeetCodeNode } from "../leetCodeExplorer";
import { leetCodeManager } from "../leetCodeManager";
import { IQuickItemEx, languages, leetCodeBinaryPath, ProblemState } from "../shared";
import { executeCommandWithProgress } from "../utils/cpUtils";
import { IQuickItemEx, languages, ProblemState } from "../shared";
import { DialogOptions, DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils";
import { selectWorkspaceFolder } from "../utils/workspaceUtils";
import * as wsl from "../utils/wslUtils";
Expand Down Expand Up @@ -50,11 +50,11 @@ async function showProblemInternal(id: string): Promise<void> {

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

await vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false });
} else {
Expand Down
5 changes: 2 additions & 3 deletions src/commands/submit.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"use strict";

import * as vscode from "vscode";
import { leetCodeExecutor } from "../leetCodeExecutor";
import { leetCodeManager } from "../leetCodeManager";
import { leetCodeBinaryPath } from "../shared";
import { executeCommandWithProgress } from "../utils/cpUtils";
import { DialogType, promptForOpenOutputChannel, promptForSignIn, showResultFile } from "../utils/uiUtils";
import { getActivefilePath } from "../utils/workspaceUtils";

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

try {
const result: string = await executeCommandWithProgress("Submitting to LeetCode...", "node", [leetCodeBinaryPath, "submit", `"${filePath}"`]);
const result: string = await leetCodeExecutor.submitSolution(filePath);
await showResultFile(result);
} catch (error) {
await promptForOpenOutputChannel("Failed to submit the solution. Please open the output channel for details.", DialogType.error);
Expand Down
10 changes: 5 additions & 5 deletions src/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import * as fse from "fs-extra";
import * as vscode from "vscode";
import { leetCodeExecutor } from "../leetCodeExecutor";
import { leetCodeManager } from "../leetCodeManager";
import { IQuickItemEx, leetCodeBinaryPath, UserStatus } from "../shared";
import { executeCommandWithProgress } from "../utils/cpUtils";
import { IQuickItemEx, UserStatus } from "../shared";
import { DialogType, promptForOpenOutputChannel, showFileSelectDialog, showResultFile } from "../utils/uiUtils";
import { getActivefilePath } from "../utils/workspaceUtils";

Expand Down Expand Up @@ -47,7 +47,7 @@ export async function testSolution(uri?: vscode.Uri): Promise<void> {
let result: string | undefined;
switch (choice.value) {
case ":default":
result = await executeCommandWithProgress("Submitting to LeetCode...", "node", [leetCodeBinaryPath, "test", `"${filePath}"`]);
result = await leetCodeExecutor.testSolution(filePath);
break;
case ":direct":
const testString: string | undefined = await vscode.window.showInputBox({
Expand All @@ -57,15 +57,15 @@ export async function testSolution(uri?: vscode.Uri): Promise<void> {
ignoreFocusOut: true,
});
if (testString) {
result = await executeCommandWithProgress("Submitting to LeetCode...", "node", [leetCodeBinaryPath, "test", `"${filePath}"`, "-t", `"${testString.replace(/"/g, "")}"`]);
result = await leetCodeExecutor.testSolution(filePath, testString.replace(/"/g, ""));
}
break;
case ":file":
const testFile: vscode.Uri[] | undefined = await showFileSelectDialog();
if (testFile && testFile.length) {
const input: string = await fse.readFile(testFile[0].fsPath, "utf-8");
if (input.trim()) {
result = await executeCommandWithProgress("Submitting to LeetCode...", "node", [leetCodeBinaryPath, "test", `"${filePath}"`, "-t", `"${input.replace(/"/g, "").replace(/\r?\n/g, "\\n")}"`]);
result = await leetCodeExecutor.testSolution(filePath, input.replace(/"/g, "").replace(/\r?\n/g, "\\n"));
} else {
vscode.window.showErrorMessage("The selected test file must not be empty.");
}
Expand Down
4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import * as show from "./commands/show";
import * as submit from "./commands/submit";
import * as test from "./commands/test";
import { leetCodeChannel } from "./leetCodeChannel";
import { leetCodeExecutor } from "./leetCodeExecutor";
import { LeetCodeNode, LeetCodeTreeDataProvider } from "./leetCodeExplorer";
import { leetCodeManager } from "./leetCodeManager";
import { leetCodeStatusBarItem } from "./leetCodeStatusBarItem";
import { isNodeInstalled } from "./utils/nodeUtils";

export async function activate(context: vscode.ExtensionContext): Promise<void> {
if (!await isNodeInstalled()) {
if (!await leetCodeExecutor.meetRequirements()) {
return;
}
leetCodeManager.getLoginStatus();
Expand Down
127 changes: 127 additions & 0 deletions src/leetCodeExecutor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"use strict";

import * as cp from "child_process";
import * as opn from "opn";
import * as path from "path";
import * as vscode from "vscode";
import { executeCommand, executeCommandWithProgress } from "./utils/cpUtils";
import { DialogOptions } from "./utils/uiUtils";
import * as wsl from "./utils/wslUtils";

export interface ILeetCodeExecutor {
meetRequirements(): Promise<boolean>;
getLeetCodeBinaryPath(): Promise<string>;

/* section for user command */
getUserInfo(): Promise<string>;
signOut(): Promise<string>;
// TODO: implement login when leetcode-cli support login in batch mode.
// signIn(): Promise<string>;

/* section for problem command */
listProblems(showLocked: boolean): Promise<string>;
showProblem(id: string, language: string, outdir: string): Promise<string>;

/* section for session command */
listSessions(): Promise<string>;
enableSession(name: string): Promise<string>;
createSession(name: string): Promise<string>;

/* section for solution command */
submitSolution(filePath: string): Promise<string>;
testSolution(filePath: string, testString?: string): Promise<string>;
}

class LeetCodeExecutor implements ILeetCodeExecutor {
private leetCodeBinaryPath: string;
private leetCodeBinaryPathInWsl: string;

constructor() {
this.leetCodeBinaryPath = path.join(__dirname, "..", "..", "node_modules", "leetcode-cli", "bin", "leetcode");
this.leetCodeBinaryPathInWsl = "";
}

public async getLeetCodeBinaryPath(): Promise<string> {
if (wsl.useWsl()) {
if (!this.leetCodeBinaryPathInWsl) {
this.leetCodeBinaryPathInWsl = `${await wsl.toWslPath(this.leetCodeBinaryPath)}`;
}
return `"${this.leetCodeBinaryPathInWsl}"`;
}
return `"${this.leetCodeBinaryPath}"`;
}

public async meetRequirements(): Promise<boolean> {
try {
await this.executeCommandEx("node", ["-v"]);
return true;
} catch (error) {
const choice: vscode.MessageItem | undefined = await vscode.window.showErrorMessage(
"LeetCode extension needs Node.js installed in environment path",
DialogOptions.open,
);
if (choice === DialogOptions.open) {
opn("https://nodejs.org");
}
return false;
}
}

public async getUserInfo(): Promise<string> {
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "user"]);
}

public async signOut(): Promise<string> {
return await await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "user", "-L"]);
}

public async listProblems(showLocked: boolean): Promise<string> {
return await this.executeCommandEx("node", showLocked ?
[await this.getLeetCodeBinaryPath(), "list"] :
[await this.getLeetCodeBinaryPath(), "list", "-q", "L"],
);
}

public async showProblem(id: string, language: string, outdir: string): Promise<string> {
return await this.executeCommandWithProgressEx("Fetching problem data...", "node", [await this.getLeetCodeBinaryPath(), "show", id, "-gx", "-l", language, "-o", `"${outdir}"`]);
}

public async listSessions(): Promise<string> {
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "session"]);
}

public async enableSession(name: string): Promise<string> {
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "session", "-e", name]);
}

public async createSession(name: string): Promise<string> {
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "session", "-c", name]);
}

public async submitSolution(filePath: string): Promise<string> {
return await this.executeCommandWithProgressEx("Submitting to LeetCode...", "node", [await this.getLeetCodeBinaryPath(), "submit", `"${filePath}"`]);
}

public async testSolution(filePath: string, testString?: string): Promise<string> {
if (testString) {
return await this.executeCommandWithProgressEx("Submitting to LeetCode...", "node", [await this.getLeetCodeBinaryPath(), "test", `"${filePath}"`, "-t", `"${testString}"`]);
}
return await this.executeCommandWithProgressEx("Submitting to LeetCode...", "node", [await this.getLeetCodeBinaryPath(), "test", `"${filePath}"`]);
}

private async executeCommandEx(command: string, args: string[], options: cp.SpawnOptions = { shell: true }): Promise<string> {
if (wsl.useWsl()) {
return await executeCommand("wsl", [command].concat(args), options);
}
return await executeCommand(command, args, options);
}

private async executeCommandWithProgressEx(message: string, command: string, args: string[], options: cp.SpawnOptions = { shell: true }): Promise<string> {
if (wsl.useWsl()) {
return await executeCommandWithProgress(message, "wsl", [command].concat(args), options);
}
return await executeCommandWithProgress(message, command, args, options);
}
}

export const leetCodeExecutor: ILeetCodeExecutor = new LeetCodeExecutor();
9 changes: 5 additions & 4 deletions src/leetCodeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import * as cp from "child_process";
import { EventEmitter } from "events";
import * as vscode from "vscode";
import { leetCodeChannel } from "./leetCodeChannel";
import { leetCodeExecutor } from "./leetCodeExecutor";
import { UserStatus } from "./shared";
import { leetCodeBinaryPath } from "./shared";
import { executeCommand } from "./utils/cpUtils";
import { DialogType, promptForOpenOutputChannel } from "./utils/uiUtils";
import * as wsl from "./utils/wslUtils";

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

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

const leetCodeBinaryPath: string = await leetCodeExecutor.getLeetCodeBinaryPath();

const childProc: cp.ChildProcess = wsl.useWsl()
? cp.spawn("wsl", ["node", leetCodeBinaryPath, "user", "-l"], { shell: true })
: cp.spawn("node", [leetCodeBinaryPath, "user", "-l"], { shell: true });
Expand Down Expand Up @@ -102,7 +103,7 @@ class LeetCodeManager extends EventEmitter implements ILeetCodeManager {

public async signOut(): Promise<void> {
try {
await executeCommand("node", [leetCodeBinaryPath, "user", "-L"]);
await leetCodeExecutor.signOut();
vscode.window.showInformationMessage("Successfully signed out.");
this.currentUser = undefined;
this.userStatus = UserStatus.SignedOut;
Expand Down
10 changes: 0 additions & 10 deletions src/shared.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
"use strict";

import * as path from "path";
import * as vscode from "vscode";
import * as wsl from "./utils/wslUtils";

let binPath: string = path.join(__dirname, "..", "..", "node_modules", "leetcode-cli", "bin", "leetcode");

if (wsl.useWsl()) {
binPath = wsl.toWslPath(binPath);
}

export const leetCodeBinaryPath: string = `"${binPath}"`;

export interface IQuickItemEx<T> extends vscode.QuickPickItem {
value: T;
Expand Down
5 changes: 1 addition & 4 deletions src/utils/cpUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@
import * as cp from "child_process";
import * as vscode from "vscode";
import { leetCodeChannel } from "../leetCodeChannel";
import * as wsl from "./wslUtils";

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

const childProc: cp.ChildProcess = wsl.useWsl()
? cp.spawn("wsl", [command].concat(args), options)
: cp.spawn(command, args, options);
const childProc: cp.ChildProcess = cp.spawn(command, args, options);

childProc.stdout.on("data", (data: string | Buffer) => {
data = data.toString();
Expand Down
22 changes: 0 additions & 22 deletions src/utils/nodeUtils.ts

This file was deleted.

Loading