From 7239ff122e277781f8e91df8d50942ece3846417 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Wed, 8 May 2019 19:47:05 +0800 Subject: [PATCH 1/3] feat: Add 'show solution' in editor context menu & code lens --- package.json | 4 ++++ src/codelens/CustomCodeLensProvider.ts | 5 ++++ src/commands/show.ts | 29 +++++++++++------------ src/extension.ts | 2 +- src/leetCodeExecutor.ts | 4 ++-- src/utils/uiUtils.ts | 5 ++++ src/utils/workspaceUtils.ts | 4 ++++ src/webview/leetCodePreviewProvider.ts | 17 ++++---------- src/webview/leetCodeSolutionProvider.ts | 31 ++++++++++++------------- 9 files changed, 55 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 63f95049..753f79a1 100644 --- a/package.json +++ b/package.json @@ -221,6 +221,10 @@ { "command": "leetcode.submitSolution", "group": "leetcode@2" + }, + { + "command": "leetcode.showSolution", + "group": "leetcode@3" } ] }, diff --git a/src/codelens/CustomCodeLensProvider.ts b/src/codelens/CustomCodeLensProvider.ts index 001ccc0b..96493472 100644 --- a/src/codelens/CustomCodeLensProvider.ts +++ b/src/codelens/CustomCodeLensProvider.ts @@ -25,6 +25,11 @@ export class CustomCodeLensProvider implements vscode.CodeLensProvider { title: "Test", command: "leetcode.testSolution", }), + new vscode.CodeLens(range, { + title: "Solution", + command: "leetcode.showSolution", + arguments: [document.uri], + }), ]; } } diff --git a/src/commands/show.ts b/src/commands/show.ts index 24d3b50f..1e8dac7b 100644 --- a/src/commands/show.ts +++ b/src/commands/show.ts @@ -17,9 +17,9 @@ import { leetCodePreviewProvider } from "../webview/leetCodePreviewProvider"; import { leetCodeSolutionProvider } from "../webview/leetCodeSolutionProvider"; import * as list from "./list"; -export async function previewProblem(node: IProblem, isSideMode: boolean = false): Promise { +export async function previewProblem(node: IProblem): Promise { const descString: string = await leetCodeExecutor.getDescription(node); - leetCodePreviewProvider.show(descString, node, isSideMode); + leetCodePreviewProvider.show(descString, node); } export async function showProblem(node?: LeetCodeNode): Promise { @@ -47,17 +47,24 @@ export async function searchProblem(): Promise { await showProblemInternal(choice.value); } -export async function showSolution(node?: LeetCodeNode): Promise { - if (!node) { +export async function showSolution(input: LeetCodeNode | vscode.Uri): Promise { + 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(); 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); @@ -117,7 +124,7 @@ async function showProblemInternal(node: IProblem): Promise { const filePath: string = wsl.useWsl() ? await wsl.toWinPath(originFilePath) : originFilePath; await Promise.all([ vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false, viewColumn: vscode.ViewColumn.One }), - movePreviewAsideIfNeeded(node), + previewProblem(node), promptHintMessage( "hint.commentDescription", 'You can generate the code file with problem description in the comments by enabling "leetcode.showCommentDescription".', @@ -130,14 +137,6 @@ async function showProblemInternal(node: IProblem): Promise { } } -async function movePreviewAsideIfNeeded(node: IProblem): Promise { - if (vscode.workspace.getConfiguration("leetcode").get("enableSideMode", true)) { - return previewProblem(node, true); - } else { - return Promise.resolve(); - } -} - async function parseProblemsToPicks(p: Promise): Promise>> { return new Promise(async (resolve: (res: Array>) => void): Promise => { const picks: Array> = (await p).map((problem: IProblem) => Object.assign({}, { diff --git a/src/extension.ts b/src/extension.ts index 2ebefd9b..978d90ae 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -56,7 +56,7 @@ export async function activate(context: vscode.ExtensionContext): Promise 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)), diff --git a/src/leetCodeExecutor.ts b/src/leetCodeExecutor.ts index 27b896c1..ac110190 100644 --- a/src/leetCodeExecutor.ts +++ b/src/leetCodeExecutor.ts @@ -107,8 +107,8 @@ class LeetCodeExecutor implements Disposable { return filePath; } - public async showSolution(problemNode: IProblem, language: string): Promise { - 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 { + const solution: string = await this.executeCommandWithProgressEx("Fetching top voted solution from discussions...", this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "show", input, "--solution", "-l", language]); return solution; } diff --git a/src/utils/uiUtils.ts b/src/utils/uiUtils.ts index 845865e8..0774dc4a 100644 --- a/src/utils/uiUtils.ts +++ b/src/utils/uiUtils.ts @@ -96,6 +96,11 @@ export async function openUrl(url: string): Promise { vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(url)); } +export async function hideSideBar(): Promise { + await vscode.commands.executeCommand("workbench.action.focusSideBar"); + await vscode.commands.executeCommand("workbench.action.toggleSidebarVisibility"); +} + export enum DialogType { info = "info", warning = "warning", diff --git a/src/utils/workspaceUtils.ts b/src/utils/workspaceUtils.ts index 425074df..a21438b4 100644 --- a/src/utils/workspaceUtils.ts +++ b/src/utils/workspaceUtils.ts @@ -44,3 +44,7 @@ export async function getActiveFilePath(uri?: vscode.Uri): Promise("enableSideMode", true); +} diff --git a/src/webview/leetCodePreviewProvider.ts b/src/webview/leetCodePreviewProvider.ts index 8d186b29..cc48579d 100644 --- a/src/webview/leetCodePreviewProvider.ts +++ b/src/webview/leetCodePreviewProvider.ts @@ -3,6 +3,8 @@ import { commands, ViewColumn } from "vscode"; import { IProblem } from "../shared"; +import { hideSideBar } from "../utils/uiUtils"; +import { isSideViewEnabled } from "../utils/workspaceUtils"; import { ILeetCodeWebviewOption, LeetCodeWebview } from "./LeetCodeWebview"; import { markdownEngine } from "./markdownEngine"; @@ -13,17 +15,13 @@ class LeetCodePreviewProvider extends LeetCodeWebview { private description: IDescription; private sideMode: boolean = false; - public isSideMode(): boolean { - return this.sideMode; - } - - public show(descString: string, node: IProblem, isSideMode: boolean = false): void { + public show(descString: string, node: IProblem): void { this.description = this.parseDescription(descString, node); this.node = node; - this.sideMode = isSideMode; + this.sideMode = isSideViewEnabled(); this.showWebviewInternal(); if (this.sideMode) { - this.hideSideBar(); // For better view area + hideSideBar(); // For better view area } } @@ -134,11 +132,6 @@ class LeetCodePreviewProvider extends LeetCodeWebview { } } - private async hideSideBar(): Promise { - await commands.executeCommand("workbench.action.focusSideBar"); - await commands.executeCommand("workbench.action.toggleSidebarVisibility"); - } - private parseDescription(descString: string, problem: IProblem): IDescription { const [ /* title */, , diff --git a/src/webview/leetCodeSolutionProvider.ts b/src/webview/leetCodeSolutionProvider.ts index 952af2cd..2eb56bae 100644 --- a/src/webview/leetCodeSolutionProvider.ts +++ b/src/webview/leetCodeSolutionProvider.ts @@ -2,33 +2,33 @@ // Licensed under the MIT license. import { ViewColumn } from "vscode"; -import { IProblem } from "../shared"; -import { leetCodePreviewProvider } from "./leetCodePreviewProvider"; +import { isSideViewEnabled } from "../utils/workspaceUtils"; 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 (isSideViewEnabled()) { return { title: "Solution", viewColumn: ViewColumn.Two, preserveFocus: true, }; + } else { + return { + title: `Solution: ${this.problemName}`, + viewColumn: ViewColumn.One, + }; } } @@ -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; } } @@ -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(); From 855a648543222f788121d5aff86a4e860041b810 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Thu, 9 May 2019 10:09:26 +0800 Subject: [PATCH 2/3] shrink the scope of the PR --- src/codelens/CustomCodeLensProvider.ts | 2 ++ src/commands/show.ts | 12 +++++++++--- src/utils/uiUtils.ts | 5 ----- src/utils/workspaceUtils.ts | 4 ---- src/webview/leetCodePreviewProvider.ts | 17 ++++++++++++----- src/webview/leetCodeSolutionProvider.ts | 4 ++-- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/codelens/CustomCodeLensProvider.ts b/src/codelens/CustomCodeLensProvider.ts index 96493472..3ee7270d 100644 --- a/src/codelens/CustomCodeLensProvider.ts +++ b/src/codelens/CustomCodeLensProvider.ts @@ -20,10 +20,12 @@ 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", diff --git a/src/commands/show.ts b/src/commands/show.ts index 1e8dac7b..77256dbf 100644 --- a/src/commands/show.ts +++ b/src/commands/show.ts @@ -17,9 +17,9 @@ import { leetCodePreviewProvider } from "../webview/leetCodePreviewProvider"; import { leetCodeSolutionProvider } from "../webview/leetCodeSolutionProvider"; import * as list from "./list"; -export async function previewProblem(node: IProblem): Promise { +export async function previewProblem(node: IProblem, isSideMode: boolean = false): Promise { const descString: string = await leetCodeExecutor.getDescription(node); - leetCodePreviewProvider.show(descString, node); + leetCodePreviewProvider.show(descString, node, isSideMode); } export async function showProblem(node?: LeetCodeNode): Promise { @@ -124,7 +124,7 @@ async function showProblemInternal(node: IProblem): Promise { const filePath: string = wsl.useWsl() ? await wsl.toWinPath(originFilePath) : originFilePath; await Promise.all([ vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false, viewColumn: vscode.ViewColumn.One }), - previewProblem(node), + movePreviewAsideIfNeeded(node), promptHintMessage( "hint.commentDescription", 'You can generate the code file with problem description in the comments by enabling "leetcode.showCommentDescription".', @@ -137,6 +137,12 @@ async function showProblemInternal(node: IProblem): Promise { } } +async function movePreviewAsideIfNeeded(node: IProblem): Promise { + if (vscode.workspace.getConfiguration("leetcode").get("enableSideMode", true)) { + return previewProblem(node, true); + } +} + async function parseProblemsToPicks(p: Promise): Promise>> { return new Promise(async (resolve: (res: Array>) => void): Promise => { const picks: Array> = (await p).map((problem: IProblem) => Object.assign({}, { diff --git a/src/utils/uiUtils.ts b/src/utils/uiUtils.ts index 0774dc4a..845865e8 100644 --- a/src/utils/uiUtils.ts +++ b/src/utils/uiUtils.ts @@ -96,11 +96,6 @@ export async function openUrl(url: string): Promise { vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(url)); } -export async function hideSideBar(): Promise { - await vscode.commands.executeCommand("workbench.action.focusSideBar"); - await vscode.commands.executeCommand("workbench.action.toggleSidebarVisibility"); -} - export enum DialogType { info = "info", warning = "warning", diff --git a/src/utils/workspaceUtils.ts b/src/utils/workspaceUtils.ts index a21438b4..425074df 100644 --- a/src/utils/workspaceUtils.ts +++ b/src/utils/workspaceUtils.ts @@ -44,7 +44,3 @@ export async function getActiveFilePath(uri?: vscode.Uri): Promise("enableSideMode", true); -} diff --git a/src/webview/leetCodePreviewProvider.ts b/src/webview/leetCodePreviewProvider.ts index cc48579d..8d186b29 100644 --- a/src/webview/leetCodePreviewProvider.ts +++ b/src/webview/leetCodePreviewProvider.ts @@ -3,8 +3,6 @@ import { commands, ViewColumn } from "vscode"; import { IProblem } from "../shared"; -import { hideSideBar } from "../utils/uiUtils"; -import { isSideViewEnabled } from "../utils/workspaceUtils"; import { ILeetCodeWebviewOption, LeetCodeWebview } from "./LeetCodeWebview"; import { markdownEngine } from "./markdownEngine"; @@ -15,13 +13,17 @@ class LeetCodePreviewProvider extends LeetCodeWebview { private description: IDescription; private sideMode: boolean = false; - public show(descString: string, node: IProblem): void { + public isSideMode(): boolean { + return this.sideMode; + } + + public show(descString: string, node: IProblem, isSideMode: boolean = false): void { this.description = this.parseDescription(descString, node); this.node = node; - this.sideMode = isSideViewEnabled(); + this.sideMode = isSideMode; this.showWebviewInternal(); if (this.sideMode) { - hideSideBar(); // For better view area + this.hideSideBar(); // For better view area } } @@ -132,6 +134,11 @@ class LeetCodePreviewProvider extends LeetCodeWebview { } } + private async hideSideBar(): Promise { + await commands.executeCommand("workbench.action.focusSideBar"); + await commands.executeCommand("workbench.action.toggleSidebarVisibility"); + } + private parseDescription(descString: string, problem: IProblem): IDescription { const [ /* title */, , diff --git a/src/webview/leetCodeSolutionProvider.ts b/src/webview/leetCodeSolutionProvider.ts index 2eb56bae..0681c291 100644 --- a/src/webview/leetCodeSolutionProvider.ts +++ b/src/webview/leetCodeSolutionProvider.ts @@ -2,7 +2,7 @@ // Licensed under the MIT license. import { ViewColumn } from "vscode"; -import { isSideViewEnabled } from "../utils/workspaceUtils"; +import { leetCodePreviewProvider } from "./leetCodePreviewProvider"; import { ILeetCodeWebviewOption, LeetCodeWebview } from "./LeetCodeWebview"; import { markdownEngine } from "./markdownEngine"; @@ -18,7 +18,7 @@ class LeetCodeSolutionProvider extends LeetCodeWebview { } protected getWebviewOption(): ILeetCodeWebviewOption { - if (isSideViewEnabled()) { + if (!leetCodePreviewProvider.isSideMode()) { return { title: "Solution", viewColumn: ViewColumn.Two, From 1e13e1c30b4cc468f0c7617b279fb300182aa983 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Thu, 9 May 2019 10:10:35 +0800 Subject: [PATCH 3/3] Correct the if check --- src/webview/leetCodeSolutionProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webview/leetCodeSolutionProvider.ts b/src/webview/leetCodeSolutionProvider.ts index 0681c291..59c548d6 100644 --- a/src/webview/leetCodeSolutionProvider.ts +++ b/src/webview/leetCodeSolutionProvider.ts @@ -18,7 +18,7 @@ class LeetCodeSolutionProvider extends LeetCodeWebview { } protected getWebviewOption(): ILeetCodeWebviewOption { - if (!leetCodePreviewProvider.isSideMode()) { + if (leetCodePreviewProvider.isSideMode()) { return { title: "Solution", viewColumn: ViewColumn.Two,