diff --git a/package.json b/package.json index fa39fb68..f86cfa7b 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "onCommand:leetcode.createSession", "onCommand:leetcode.refreshExplorer", "onCommand:leetcode.showProblem", + "onCommand:leetcode.previewProblem", "onCommand:leetcode.searchProblem", "onCommand:leetcode.testSolution", "onCommand:leetcode.submitSolution", @@ -94,6 +95,11 @@ "title": "Show Problem", "category": "LeetCode" }, + { + "command": "leetcode.previewProblem", + "title": "Preview Problem", + "category": "LeetCode" + }, { "command": "leetcode.searchProblem", "title": "Search Problem", @@ -164,12 +170,21 @@ "command": "leetcode.showProblem", "when": "view == leetCodeExplorer && viewItem == problem", "group": "leetcode@1" + }, + { + "command": "leetcode.previewProblem", + "when": "view == leetCodeExplorer && viewItem == problem", + "group": "leetcode@1" } ], "commandPalette": [ { "command": "leetcode.showProblem", "when": "never" + }, + { + "command": "leetcode.previewProblem", + "when": "never" } ], "explorer/context": [ diff --git a/src/explorer/LeetCodeNode.ts b/src/explorer/LeetCodeNode.ts index ad5211cb..3f685b70 100644 --- a/src/explorer/LeetCodeNode.ts +++ b/src/explorer/LeetCodeNode.ts @@ -1,6 +1,7 @@ // Copyright (c) jdneo. All rights reserved. // Licensed under the MIT license. +import { Command } from "vscode"; import { IProblem, ProblemState } from "../shared"; export class LeetCodeNode { @@ -48,4 +49,13 @@ export class LeetCodeNode { public get parentName(): string { return this.parentNodeName; } + + public get previewCommand(): Command { + return { + title: "Preview Problem", + command: "leetcode.previewProblem", + arguments: [this], + }; + } + } diff --git a/src/explorer/LeetCodeTreeDataProvider.ts b/src/explorer/LeetCodeTreeDataProvider.ts index da9ca751..8b5ed89d 100644 --- a/src/explorer/LeetCodeTreeDataProvider.ts +++ b/src/explorer/LeetCodeTreeDataProvider.ts @@ -52,6 +52,7 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider }); const leetCodeTreeDataProvider: LeetCodeTreeDataProvider = new LeetCodeTreeDataProvider(context); + leetCodePreviewProvider.initialize(context); leetCodeResultProvider.initialize(context); context.subscriptions.push( leetCodeStatusBarItem, leetCodeChannel, + leetCodePreviewProvider, leetCodeResultProvider, vscode.window.createTreeView("leetCodeExplorer", { treeDataProvider: leetCodeTreeDataProvider, showCollapseAll: true }), vscode.languages.registerCodeLensProvider({ scheme: "file" }, codeLensProvider), @@ -43,6 +46,7 @@ export async function activate(context: vscode.ExtensionContext): Promise vscode.commands.registerCommand("leetcode.signout", () => leetCodeManager.signOut()), vscode.commands.registerCommand("leetcode.selectSessions", () => session.selectSession()), vscode.commands.registerCommand("leetcode.createSession", () => session.createSession()), + vscode.commands.registerCommand("leetcode.previewProblem", (node: LeetCodeNode) => leetCodePreviewProvider.preview(node)), vscode.commands.registerCommand("leetcode.showProblem", (node: LeetCodeNode) => show.showProblem(node)), vscode.commands.registerCommand("leetcode.searchProblem", () => show.searchProblem()), vscode.commands.registerCommand("leetcode.refreshExplorer", () => leetCodeTreeDataProvider.refresh()), diff --git a/src/leetCodeExecutor.ts b/src/leetCodeExecutor.ts index 373bda11..f4080a95 100644 --- a/src/leetCodeExecutor.ts +++ b/src/leetCodeExecutor.ts @@ -75,18 +75,22 @@ class LeetCodeExecutor { ); } - public async showProblem(node: IProblem, language: string, outDir: string): Promise { - const fileName: string = genFileName(node, language); + public async showProblem(problemNode: IProblem, language: string, outDir: string): Promise { + const fileName: string = genFileName(problemNode, language); const filePath: string = path.join(outDir, fileName); if (!await fse.pathExists(filePath)) { - const codeTemplate: string = await this.executeCommandWithProgressEx("Fetching problem data...", "node", [await this.getLeetCodeBinaryPath(), "show", node.id, "-cx", "-l", language]); + const codeTemplate: string = await this.executeCommandWithProgressEx("Fetching problem data...", "node", [await this.getLeetCodeBinaryPath(), "show", problemNode.id, "-cx", "-l", language]); await fse.writeFile(filePath, codeTemplate); } return filePath; } + public async getDescription(problemNode: IProblem): Promise { + return await this.executeCommandWithProgressEx("Fetching problem description...", "node", [await this.getLeetCodeBinaryPath(), "show", problemNode.id, "-x"]); + } + public async listSessions(): Promise { return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "session"]); } diff --git a/src/leetCodePreviewProvider.ts b/src/leetCodePreviewProvider.ts new file mode 100644 index 00000000..5d418a8f --- /dev/null +++ b/src/leetCodePreviewProvider.ts @@ -0,0 +1,107 @@ +import { commands, Disposable, ExtensionContext, ViewColumn, WebviewPanel, window } from "vscode"; +import { leetCodeExecutor } from "./leetCodeExecutor"; +import { IProblem } from "./shared"; +class LeetCodePreviewProvider implements Disposable { + + private context: ExtensionContext; + private panel: WebviewPanel | undefined; + + public initialize(context: ExtensionContext): void { + this.context = context; + } + + public async preview(node: IProblem): Promise { + if (!this.panel) { + const panelType: string = "previewProblem"; + const panelTitle: string = node.name; + this.panel = window.createWebviewPanel(panelType, panelTitle, ViewColumn.Active, { + enableScripts: true, + enableCommandUris: true, + enableFindWidget: true, + retainContextWhenHidden: true, + }); + } + + this.panel.onDidDispose(() => { + this.panel = undefined; + }, null, this.context.subscriptions); + + this.panel.webview.onDidReceiveMessage(async (message: IWebViewMessage) => { + switch (message.command) { + case "ShowProblem": + await commands.executeCommand("leetcode.showProblem", node); + this.dispose(); + return; + } + }); + this.panel.webview.html = await this.provideHtmlContent(node); + } + + public dispose(): void { + if (this.panel) { + this.panel.dispose(); + } + } + + public async provideHtmlContent(node: IProblem): Promise { + return await this.renderHTML(node); + } + + private async renderHTML(node: IProblem): Promise { + const description: string = await leetCodeExecutor.getDescription(node); + const descriptionHTML: string = description.replace(/\n/g, "
"); + const htmlTemplate: string = ` + + + + + + Preview Problem + + + +
+ ${ descriptionHTML} +
+ + + + + `; + return htmlTemplate; + } + +} +export interface IWebViewMessage { + command: string; +} + +export const leetCodePreviewProvider: LeetCodePreviewProvider = new LeetCodePreviewProvider();