Skip to content

feat: Add 'show solution' in editor context menu & code lens #313

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 3 commits into from
May 10, 2019
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
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@
{
"command": "leetcode.submitSolution",
"group": "leetcode@2"
},
{
"command": "leetcode.showSolution",
"group": "leetcode@3"
}
]
},
Expand Down
7 changes: 7 additions & 0 deletions src/codelens/CustomCodeLensProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ export class CustomCodeLensProvider implements vscode.CodeLensProvider {
new vscode.CodeLens(range, {
title: "Submit",
command: "leetcode.submitSolution",
arguments: [document.uri],
}),
new vscode.CodeLens(range, {
title: "Test",
command: "leetcode.testSolution",
arguments: [document.uri],
}),
new vscode.CodeLens(range, {
title: "Solution",
command: "leetcode.showSolution",
arguments: [document.uri],
}),
];
}
Expand Down
17 changes: 11 additions & 6 deletions src/commands/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,24 @@ export async function searchProblem(): Promise<void> {
await showProblemInternal(choice.value);
}

export async function showSolution(node?: LeetCodeNode): Promise<void> {
if (!node) {
export async function showSolution(input: LeetCodeNode | vscode.Uri): Promise<void> {
let problemInput: string | undefined;
if (input instanceof LeetCodeNode) {
problemInput = input.id;
} else if (input instanceof vscode.Uri) {
problemInput = `"${input.fsPath}"`;
} else {
vscode.window.showErrorMessage("Invalid input to fetch the solution data");
return;
}

const language: string | undefined = await fetchProblemLanguage();
Copy link
Contributor

Choose a reason for hiding this comment

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

We could utilize file extension (path.extName()) and shared.ts/langExt to determine the language, thus no need for re-selecting the language when showing solution from the code:

export const langExt: Map<string, string> = new Map([
    ["bash", "sh"],
    ["c", "c"],
    ["cpp", "cpp"],
    ["csharp", "cs"],
    ["golang", "go"],
    ["java", "java"],
    ["javascript", "js"],
    ["kotlin", "kt"],
    ["mysql", "sql"],
    ["php", "php"],
    ["python", "py"],
    ["python3", "py"],
    ["ruby", "rb"],
    ["rust", "rs"],
    ["scala", "scala"],
    ["swift", "swift"],
]);

(Yet this choice comes with a risk: currently showing solution for languages like scala has a high probability to fail. This feature could be added in later PR when logic of solution plugin is improved.)

Copy link
Member Author

Choose a reason for hiding this comment

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

another rick is that it's hard to differentiate py3 & py2 from the extName

if (!language) {
return;
}
try {
const solution: string = await leetCodeExecutor.showSolution(node, language);
leetCodeSolutionProvider.show(unescapeJS(solution), node);
const solution: string = await leetCodeExecutor.showSolution(problemInput, language);
leetCodeSolutionProvider.show(unescapeJS(solution));
} catch (error) {
leetCodeChannel.appendLine(error.toString());
await promptForOpenOutputChannel("Failed to fetch the top voted solution. Please open the output channel for details.", DialogType.error);
Expand Down Expand Up @@ -133,8 +140,6 @@ async function showProblemInternal(node: IProblem): Promise<void> {
async function movePreviewAsideIfNeeded(node: IProblem): Promise<void> {
if (vscode.workspace.getConfiguration("leetcode").get<boolean>("enableSideMode", true)) {
return previewProblem(node, true);
} else {
return Promise.resolve();
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
vscode.commands.registerCommand("leetcode.previewProblem", (node: LeetCodeNode) => show.previewProblem(node)),
vscode.commands.registerCommand("leetcode.showProblem", (node: LeetCodeNode) => show.showProblem(node)),
vscode.commands.registerCommand("leetcode.searchProblem", () => show.searchProblem()),
vscode.commands.registerCommand("leetcode.showSolution", (node: LeetCodeNode) => show.showSolution(node)),
vscode.commands.registerCommand("leetcode.showSolution", (input: LeetCodeNode | vscode.Uri) => show.showSolution(input)),
vscode.commands.registerCommand("leetcode.refreshExplorer", () => leetCodeTreeDataProvider.refresh()),
vscode.commands.registerCommand("leetcode.testSolution", (uri?: vscode.Uri) => test.testSolution(uri)),
vscode.commands.registerCommand("leetcode.submitSolution", (uri?: vscode.Uri) => submit.submitSolution(uri)),
Expand Down
4 changes: 2 additions & 2 deletions src/leetCodeExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ class LeetCodeExecutor implements Disposable {
return filePath;
}

public async showSolution(problemNode: IProblem, language: string): Promise<string> {
const solution: string = await this.executeCommandWithProgressEx("Fetching top voted solution from discussions...", this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "show", problemNode.id, "--solution", "-l", language]);
public async showSolution(input: string, language: string): Promise<string> {
const solution: string = await this.executeCommandWithProgressEx("Fetching top voted solution from discussions...", this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "show", input, "--solution", "-l", language]);
return solution;
}

Expand Down
29 changes: 14 additions & 15 deletions src/webview/leetCodeSolutionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,33 @@
// Licensed under the MIT license.

import { ViewColumn } from "vscode";
import { IProblem } from "../shared";
import { leetCodePreviewProvider } from "./leetCodePreviewProvider";
import { ILeetCodeWebviewOption, LeetCodeWebview } from "./LeetCodeWebview";
import { markdownEngine } from "./markdownEngine";

class LeetCodeSolutionProvider extends LeetCodeWebview {

protected readonly viewType: string = "leetcode.solution";
private problemName: string;
private solution: Solution;

public show(solutionString: string, problem: IProblem): void {
this.solution = this.parseSolution(solutionString, problem);
public show(solutionString: string): void {
this.solution = this.parseSolution(solutionString);
this.showWebviewInternal();
}

protected getWebviewOption(): ILeetCodeWebviewOption {
if (!leetCodePreviewProvider.isSideMode()) {
return {
title: `${this.solution.problem}: Solution`,
viewColumn: ViewColumn.One,
};
} else {
if (leetCodePreviewProvider.isSideMode()) {
return {
title: "Solution",
viewColumn: ViewColumn.Two,
preserveFocus: true,
};
} else {
return {
title: `Solution: ${this.problemName}`,
viewColumn: ViewColumn.One,
};
}
}

Expand Down Expand Up @@ -66,17 +66,17 @@ class LeetCodeSolutionProvider extends LeetCodeWebview {
delete this.solution;
}

private parseSolution(raw: string, problem: IProblem): Solution {
private parseSolution(raw: string): Solution {
raw = raw.slice(1); // skip first empty line
[this.problemName, raw] = raw.split(/\n\n([^]+)/); // parse problem name and skip one line
const solution: Solution = new Solution();
// [^] matches everything including \n, yet can be replaced by . in ES2018's `m` flag
raw = raw.slice(1); // skip first empty line
[solution.title, raw] = raw.split(/\n\n([^]+)/); // parse title and skip one line
[solution.url, raw] = raw.split(/\n\n([^]+)/); // parse url and skip one line
[solution.title, raw] = raw.split(/\n\n([^]+)/);
[solution.url, raw] = raw.split(/\n\n([^]+)/);
[solution.lang, raw] = raw.match(/\* Lang:\s+(.+)\n([^]+)/)!.slice(1);
[solution.author, raw] = raw.match(/\* Author:\s+(.+)\n([^]+)/)!.slice(1);
[solution.votes, raw] = raw.match(/\* Votes:\s+(\d+)\n\n([^]+)/)!.slice(1);
solution.body = raw;
solution.problem = problem.name;
return solution;
}
}
Expand All @@ -89,7 +89,6 @@ class Solution {
public author: string = "";
public votes: string = "";
public body: string = ""; // Markdown supported
public problem: string = "";
}

export const leetCodeSolutionProvider: LeetCodeSolutionProvider = new LeetCodeSolutionProvider();