Skip to content
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

add outputPath Config #123

Merged
merged 15 commits into from
Feb 16, 2019
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@
"leetcode-cn"
],
"description": "Endpoint of the user account."
},
"leetcode.outputFolder": {
"type": "string",
"scope": "application",
"description": "Specify the relative path to save the problem files."
}
}
}
Expand Down
59 changes: 49 additions & 10 deletions src/commands/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// Licensed under the MIT license.

import * as fse from "fs-extra";
import * as path from "path";
import * as vscode from "vscode";
import { LeetCodeNode } from "../explorer/LeetCodeNode";
import { leetCodeChannel } from "../leetCodeChannel";
import { leetCodeExecutor } from "../leetCodeExecutor";
import { leetCodeManager } from "../leetCodeManager";
import { IProblem, IQuickItemEx, languages, ProblemState } from "../shared";
Expand All @@ -16,15 +18,15 @@ export async function showProblem(node?: LeetCodeNode): Promise<void> {
if (!node) {
return;
}
await showProblemInternal(node.id);
await showProblemInternal(node);
Copy link
Member

Choose a reason for hiding this comment

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

Though type casting from LeetCodeNode to IProblem is fine. Let's add getter in LeetCodeNode to get the data, which is typed as IProblem.

It looks like I need to do some refactors here later after this PR, The definition of LeetCodeNode and IProblem is overlapped.

}

export async function searchProblem(): Promise<void> {
if (!leetCodeManager.getUser()) {
promptForSignIn();
return;
}
const choice: IQuickItemEx<string> | undefined = await vscode.window.showQuickPick(
const choice: IQuickItemEx<IProblem> | undefined = await vscode.window.showQuickPick(
parseProblemsToPicks(list.listProblems()),
{
matchOnDetail: true,
Expand All @@ -37,7 +39,7 @@ export async function searchProblem(): Promise<void> {
await showProblemInternal(choice.value);
}

async function showProblemInternal(id: string): Promise<void> {
async function showProblemInternal(node: IProblem): Promise<void> {
try {
const leetCodeConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("leetcode");
let defaultLanguage: string | undefined = leetCodeConfig.get<string>("defaultLanguage");
Expand All @@ -49,9 +51,21 @@ async function showProblemInternal(id: string): Promise<void> {
return;
}

const outDir: string = await selectWorkspaceFolder();
let outDir: string = await selectWorkspaceFolder();
let relativePath: string = (leetCodeConfig.get<string>("outputFolder") || "").trim();
const matchResult: RegExpMatchArray | null = relativePath.match(/\$\{(.*?)\}/);
if (matchResult) {
const resolvedPath: string | undefined = await resolveRelativePath(matchResult[1].toLocaleLowerCase(), node, language);
if (!resolvedPath) {
leetCodeChannel.appendLine("Showing problem canceled by user.");
return;
}
relativePath = resolvedPath;
}

outDir = path.join(outDir, relativePath);
await fse.ensureDir(outDir);
const result: string = await leetCodeExecutor.showProblem(id, language, outDir);
const result: string = await leetCodeExecutor.showProblem(node.id, language, outDir);
const reg: RegExp = /\* Source Code:\s*(.*)/;
const match: RegExpMatchArray | null = result.match(reg);
if (match && match.length >= 2) {
Expand All @@ -76,17 +90,17 @@ async function showProblemInternal(id: string): Promise<void> {
}
}
} catch (error) {
await promptForOpenOutputChannel("Failed to fetch the problem information. Please open the output channel for details.", DialogType.error);
await promptForOpenOutputChannel("Failed to show the problem. Please open the output channel for details.", DialogType.error);
}
}

async function parseProblemsToPicks(p: Promise<IProblem[]>): Promise<Array<IQuickItemEx<string>>> {
return new Promise(async (resolve: (res: Array<IQuickItemEx<string>>) => void): Promise<void> => {
const picks: Array<IQuickItemEx<string>> = (await p).map((problem: IProblem) => Object.assign({}, {
async function parseProblemsToPicks(p: Promise<IProblem[]>): Promise<Array<IQuickItemEx<IProblem>>> {
return new Promise(async (resolve: (res: Array<IQuickItemEx<IProblem>>) => void): Promise<void> => {
const picks: Array<IQuickItemEx<IProblem>> = (await p).map((problem: IProblem) => Object.assign({}, {
label: `${parseProblemDecorator(problem.state, problem.locked)}${problem.id}.${problem.name}`,
description: "",
detail: `AC rate: ${problem.passRate}, Difficulty: ${problem.difficulty}`,
value: problem.id,
value: problem,
}));
resolve(picks);
});
Expand All @@ -102,3 +116,28 @@ function parseProblemDecorator(state: ProblemState, locked: boolean): string {
return locked ? "$(lock) " : "";
}
}

async function resolveRelativePath(value: string, node: IProblem, selectedLanguage: string): Promise<string | undefined> {
switch (value) {
case "tag":
if (node.tags.length === 1) {
return node.tags[0];
}
return await vscode.window.showQuickPick(
node.tags,
{
matchOnDetail: true,
placeHolder: "Multiple tags available, please select one",
ignoreFocusOut: true,
},
);
case "language":
return selectedLanguage;
case "difficulty":
return node.difficulty;
default:
const errorMsg: string = `The config '${value}' is not supported.`;
leetCodeChannel.appendLine(errorMsg);
throw new Error(errorMsg);
}
}