Skip to content

Commit

Permalink
Add side mode to webview (#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vigilans authored and jdneo committed Apr 22, 2019
1 parent 8d3e09f commit 52a84d3
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 34 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
| `leetcode.outputFolder`| Specify the relative path to save the problem files. Besides using customized path, there are also several reserved words which can be used here: <ul><li>`${tag}`: Categorize the problem according to their tags.<li>`${language}`: Categorize the problem according to their language.</li><li>`${difficulty}`: Categorize the problem according to their difficulty.</li></ul> | N/A |
| `leetcode.enableStatusBar` | Specify whether the LeetCode status bar will be shown or not. | `true` |
| `leetcode.enableShortcuts` | Specify whether the submit and test shortcuts in editor or not. | `true` |
| `leetcode.enableSideMode` | Specify whether `preview`, `solution` and `submission` tab should be grouped into the second editor column when solving a problem. | `true` |
| `leetcode.nodePath` | Specify the `Node.js` executable path. | `node` |

## Want Help?
Expand Down
1 change: 1 addition & 0 deletions docs/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
| `leetcode.outputFolder` | 指定保存文件时所用的相对文件夹路径。除了用户自定义路径外,也可以使用保留项,包括:<ul><li>`${tag}`: 根据题目的类别进行分类。<li>`${language}`: 根据题目的语言进行分类。</li><li>`${difficulty}`: 根据题目的难度进行分类。</li></ul> | N/A |
| `leetcode.enableStatusBar` | 指定是否在 VS Code 下方显示插件状态栏。 | `true` |
| `leetcode.enableShortcuts` | 指定是否在 VS Code 编辑文件下方显示提交和测试的快捷按钮。 | `true` |
| `leetcode.enableSideMode` | 指定在解决一道题时,是否将`问题预览``高票答案``提交结果`窗口集中在编辑器的第二栏。 | `true` |
| `leetcode.nodePath` | 指定 `Node.js` 可执行文件的路径。 | `node` |

## 需要帮助?
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

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

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,12 @@
"scope": "application",
"description": "Show the submit and test shortcuts in editor or not."
},
"leetcode.enableSideMode": {
"type": "boolean",
"default": true,
"scope": "application",
"description": "Determine whether to group all webview pages into the second editor column when solving problems."
},
"leetcode.nodePath": {
"type": "string",
"default": "node",
Expand Down Expand Up @@ -338,6 +344,6 @@
"markdown-it": "^8.4.2",
"require-from-string": "^2.0.2",
"unescape-js": "^1.1.1",
"vsc-leetcode-cli": "2.6.3"
"vsc-leetcode-cli": "2.6.4"
}
}
23 changes: 20 additions & 3 deletions src/commands/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ import { IProblem, IQuickItemEx, languages, ProblemState } from "../shared";
import { DialogOptions, DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils";
import { selectWorkspaceFolder } from "../utils/workspaceUtils";
import * as wsl from "../utils/wslUtils";
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<void> {
const descString: string = await leetCodeExecutor.getDescription(node);
leetCodePreviewProvider.show(descString, node, isSideMode);
}

export async function showProblem(node?: LeetCodeNode): Promise<void> {
if (!node) {
return;
Expand Down Expand Up @@ -51,7 +57,7 @@ export async function showSolution(node?: LeetCodeNode): Promise<void> {
}
try {
const solution: string = await leetCodeExecutor.showSolution(node, language);
await leetCodeSolutionProvider.show(unescapeJS(solution), node);
leetCodeSolutionProvider.show(unescapeJS(solution), node);
} 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 @@ -95,7 +101,7 @@ async function showProblemInternal(node: IProblem): Promise<void> {
// SUGGESTION: group config retriving into one file
const leetCodeConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("leetcode");
let outDir: string = await selectWorkspaceFolder();
let relativePath: string = (leetCodeConfig.get<string>("outputFolder") || "").trim();
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);
Expand All @@ -111,12 +117,23 @@ async function showProblemInternal(node: IProblem): Promise<void> {

const originFilePath: string = await leetCodeExecutor.showProblem(node, language, outDir);
const filePath: string = wsl.useWsl() ? await wsl.toWinPath(originFilePath) : originFilePath;
await vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false, viewColumn: vscode.ViewColumn.One });
await Promise.all([
vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false, viewColumn: vscode.ViewColumn.One }),
movePreviewAsideIfNeeded(node),
]);
} catch (error) {
await promptForOpenOutputChannel("Failed to show the problem. Please open the output channel for details.", DialogType.error);
}
}

async function movePreviewAsideIfNeeded(node: IProblem): Promise<void> {
if (vscode.workspace.getConfiguration("leetcode").get<boolean>("enableSideMode", true)) {
return previewProblem(node, true);
} else {
return Promise.resolve();
}
}

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({}, {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function submitSolution(uri?: vscode.Uri): Promise<void> {

try {
const result: string = await leetCodeExecutor.submitSolution(filePath);
await leetCodeSubmissionProvider.show(result);
leetCodeSubmissionProvider.show(result);
} catch (error) {
await promptForOpenOutputChannel("Failed to submit the solution. Please open the output channel for details.", DialogType.error);
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export async function testSolution(uri?: vscode.Uri): Promise<void> {
if (!result) {
return;
}
await leetCodeSubmissionProvider.show(result);
leetCodeSubmissionProvider.show(result);
} catch (error) {
await promptForOpenOutputChannel("Failed to test the solution. Please open the output channel for details.", DialogType.error);
}
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
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.show(node)),
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)),
Expand Down
17 changes: 12 additions & 5 deletions src/webview/LeetCodeWebview.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (c) jdneo. All rights reserved.
// Licensed under the MIT license.

import { ConfigurationChangeEvent, Disposable, ViewColumn, WebviewPanel, window, workspace } from "vscode";
import { commands, ConfigurationChangeEvent, Disposable, ViewColumn, WebviewPanel, window, workspace } from "vscode";
import { markdownEngine } from "./markdownEngine";

export abstract class LeetCodeWebview implements Disposable {

protected readonly viewType: string = "leetcode.webview";
protected panel: WebviewPanel | undefined;
private listeners: Disposable[] = [];

Expand All @@ -16,9 +17,9 @@ export abstract class LeetCodeWebview implements Disposable {
}

protected showWebviewInternal(): void {
const { viewType, title, viewColumn, preserveFocus } = this.getWebviewOption();
const { title, viewColumn, preserveFocus } = this.getWebviewOption();
if (!this.panel) {
this.panel = window.createWebviewPanel(viewType, title, { viewColumn, preserveFocus }, {
this.panel = window.createWebviewPanel(this.viewType, title, { viewColumn, preserveFocus }, {
enableScripts: true,
enableCommandUris: true,
enableFindWidget: true,
Expand All @@ -30,7 +31,14 @@ export abstract class LeetCodeWebview implements Disposable {
workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.listeners);
} else {
this.panel.title = title;
this.panel.reveal(viewColumn, preserveFocus);
if (viewColumn === ViewColumn.Two) {
// Make sure second group exists. See vscode#71608 issue
commands.executeCommand("workbench.action.focusSecondEditorGroup").then(() => {
this.panel!.reveal(viewColumn, preserveFocus);
});
} else {
this.panel.reveal(viewColumn, preserveFocus);
}
}
this.panel.webview.html = this.getWebviewContent();
}
Expand All @@ -57,7 +65,6 @@ export abstract class LeetCodeWebview implements Disposable {
}

export interface ILeetCodeWebviewOption {
viewType: string;
title: string;
viewColumn: ViewColumn;
preserveFocus?: boolean;
Expand Down
44 changes: 33 additions & 11 deletions src/webview/leetCodePreviewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,44 @@
// Licensed under the MIT license.

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

class LeetCodePreviewProvider extends LeetCodeWebview {

protected readonly viewType: string = "leetcode.preview";
private node: IProblem;
private description: IDescription;
private sideMode: boolean = false;

public async show(node: IProblem): Promise<void> {
this.description = this.parseDescription(await leetCodeExecutor.getDescription(node), node);
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 = isSideMode;
this.showWebviewInternal();
if (this.sideMode) {
this.hideSideBar(); // For better view area
}
}

protected getWebviewOption(): ILeetCodeWebviewOption {
return {
viewType: "leetcode.preview",
title: `${this.node.name}: Preview`,
viewColumn: ViewColumn.One,
};
if (!this.sideMode) {
return {
title: `${this.node.name}: Preview`,
viewColumn: ViewColumn.One,
};
} else {
return {
title: "Description",
viewColumn: ViewColumn.Two,
preserveFocus: true,
};
}
}

protected getWebviewContent(): string {
Expand Down Expand Up @@ -84,18 +100,18 @@ class LeetCodePreviewProvider extends LeetCodeWebview {
<html>
<head>
${markdownEngine.getStyles()}
${button.style}
${!this.sideMode ? button.style : ""}
</head>
<body>
${head}
${info}
${tags}
${companies}
${body}
${button.element}
${!this.sideMode ? button.element : ""}
<script>
const vscode = acquireVsCodeApi();
${button.script}
${!this.sideMode ? button.script : ""}
</script>
</body>
</html>
Expand All @@ -106,6 +122,7 @@ class LeetCodePreviewProvider extends LeetCodeWebview {
super.onDidDisposeWebview();
delete this.node;
delete this.description;
this.sideMode = false;
}

protected async onDidReceiveMessage(message: IWebViewMessage): Promise<void> {
Expand All @@ -117,6 +134,11 @@ class LeetCodePreviewProvider extends LeetCodeWebview {
}
}

private async hideSideBar(): Promise<void> {
await commands.executeCommand("workbench.action.focusSideBar");
await commands.executeCommand("workbench.action.toggleSidebarVisibility");
}

private parseDescription(descString: string, problem: IProblem): IDescription {
const [
/* title */, ,
Expand Down
21 changes: 15 additions & 6 deletions src/webview/leetCodeSolutionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@

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 solution: Solution;

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

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

protected getWebviewContent(): string {
Expand Down
4 changes: 2 additions & 2 deletions src/webview/leetCodeSubmissionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import { markdownEngine } from "./markdownEngine";

class LeetCodeSubmissionProvider extends LeetCodeWebview {

protected readonly viewType: string = "leetcode.submission";
private result: string;

public async show(result: string): Promise<void> {
public show(result: string): void {
this.result = result;
this.showWebviewInternal();
}

protected getWebviewOption(): ILeetCodeWebviewOption {
return {
viewType: "leetcode.submission",
title: "Submission",
viewColumn: ViewColumn.Two,
};
Expand Down

0 comments on commit 52a84d3

Please sign in to comment.