Skip to content

Enhanced Tree view #94

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 14 commits into from
Feb 2, 2019
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@
{
"title": "LeetCode",
"properties": {
"leetcode.hideSolved": {
"type": "boolean",
"default": false,
"scope": "application",
"description": "Hide solved problems."
},
"leetcode.showLocked": {
"type": "boolean",
"default": false,
Expand Down Expand Up @@ -264,6 +270,7 @@
},
"dependencies": {
"fs-extra": "^6.0.1",
"leetcode-cli": "2.6.1"
"leetcode-cli": "2.6.1",
"require-from-string": "^2.0.2"
}
}
29 changes: 19 additions & 10 deletions src/commands/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ import { leetCodeManager } from "../leetCodeManager";
import { ProblemState, UserStatus } from "../shared";
import { DialogType, promptForOpenOutputChannel } from "../utils/uiUtils";

export interface IProblem {
favorite: boolean;
locked: boolean;
state: ProblemState;
id: string;
name: string;
difficulty: string;
passRate: string;
}
// tslint:disable-next-line:typedef
export const IProblemDefault = {
favorite: false,
locked: false,
state: ProblemState.Unknown,
id: "",
name: "",
difficulty: "",
passRate: "",
companies: [] as string[],
tags: [] as string[],
};

export type IProblem = typeof IProblemDefault;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to put the IProblem definition and IProblemDefault to other places instead of in this file.
Let's put them into shared.ts, I'll refactor this part later.


export async function listProblems(): Promise<IProblem[]> {
try {
Expand All @@ -28,17 +33,21 @@ export async function listProblems(): Promise<IProblem[]> {
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+ %)\)/;
const { companies, tags } = await leetCodeExecutor.getCompaniesAndTags();
for (const line of lines) {
const match: RegExpMatchArray | null = line.match(reg);
if (match && match.length === 8) {
const id: string = match[4].trim();
problems.push({
id,
favorite: match[1].trim().length > 0,
locked: match[2].trim().length > 0,
state: parseProblemState(match[3]),
id: match[4].trim(),
name: match[5].trim(),
difficulty: match[6].trim(),
passRate: match[7].trim(),
companies: companies[id] || ["Unknown"],
tags: tags[id] || ["Unknown"],
});
}
}
Expand Down
43 changes: 33 additions & 10 deletions src/leetCodeExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license.

import * as cp from "child_process";
import * as fs from "fs";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use fs-extra

import * as path from "path";
import * as vscode from "vscode";
import { Endpoint } from "./shared";
Expand All @@ -10,28 +11,31 @@ import { DialogOptions, openUrl } from "./utils/uiUtils";
import * as wsl from "./utils/wslUtils";

class LeetCodeExecutor {
private leetCodeBinaryPath: string;
private leetCodeBinaryPathInWsl: string;
private leetCodeRootPath: string;
private leetCodeRootPathInWsl: string;

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

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

public async getLeetCodeBinaryPath(): Promise<string> { // wrapped by ""
return `"${path.join(await this.getLeetCodeRootPath(), "bin", "leetcode")}"`;
}

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",
Expand All @@ -42,6 +46,12 @@ class LeetCodeExecutor {
}
return false;
}
try { // Check company plugin
await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "plugin", "-e", "company"]);
} catch (error) { // Download company plugin and activate
await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "plugin", "-i", "company"]);
}
return true;
}

public async deleteCache(): Promise<string> {
Expand Down Expand Up @@ -100,6 +110,19 @@ class LeetCodeExecutor {
}
}

public async getCompaniesAndTags(): Promise<{ companies: { [key: string]: string[] }, tags: { [key: string]: string[] } }> {
// preprocess the plugin source
const componiesTagsPath: string = path.join(await leetCodeExecutor.getLeetCodeRootPath(), "lib", "plugins", "company.js");
const componiesTagsSrc: string = (await fs.readFileSync(componiesTagsPath, "utf8")).replace(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use readFile from fs-extra.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something went wrong with my usage. fs-extra is correct.

"module.exports = plugin",
"module.exports = { COMPONIES, TAGS }",
);
// require plugin from modified string
const requireFromString: (src: string, path: string) => any = require("require-from-string");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we import the module?

For example, npm i --save-dev @types/require-from-string
Then import it instead of using require

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the definitelyTyped indeed provides type for require-from-string.
import * as requireFromString from "require-from-string"; successfully worked.

const { COMPONIES, TAGS } = requireFromString(componiesTagsSrc, componiesTagsPath);
return { companies: COMPONIES, tags: TAGS };
}

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);
Expand Down
Loading