Skip to content

Apply mdEngine to preview webview #257

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 12 commits into from
Apr 14, 2019
2 changes: 1 addition & 1 deletion src/commands/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ 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 });
await vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false, viewColumn: vscode.ViewColumn.One });
} catch (error) {
await promptForOpenOutputChannel("Failed to show the problem. 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 @@ -52,7 +52,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.preview(node)),
vscode.commands.registerCommand("leetcode.previewProblem", (node: LeetCodeNode) => leetCodePreviewProvider.show(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
2 changes: 1 addition & 1 deletion src/leetCodeExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class LeetCodeExecutor implements Disposable {
const filePath: string = path.join(outDir, fileName);

if (!await fse.pathExists(filePath)) {
const codeTemplate: string = await this.executeCommandWithProgressEx("Fetching problem data...", this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "show", problemNode.id, "-cx", "-l", language]);
const codeTemplate: string = await this.executeCommandWithProgressEx("Fetching problem data...", this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "show", problemNode.id, "-c", "-l", language]);
await fse.writeFile(filePath, codeTemplate);
}

Expand Down
140 changes: 104 additions & 36 deletions src/webview/leetCodePreviewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import { commands, Disposable, ExtensionContext, ViewColumn, WebviewPanel, window } from "vscode";
import { leetCodeExecutor } from "../leetCodeExecutor";
import { IProblem } from "../shared";
import { markdownEngine } from "./markdownEngine";

class LeetCodePreviewProvider implements Disposable {

private context: ExtensionContext;
Expand All @@ -14,22 +16,29 @@ class LeetCodePreviewProvider implements Disposable {
this.context = context;
}

public async preview(node: IProblem): Promise<void> {
public async show(node: IProblem): Promise<void> {
// Fetch problem first before creating webview panel
const descString: string = await leetCodeExecutor.getDescription(node);

this.node = node;
if (!this.panel) {
this.panel = window.createWebviewPanel("leetcode.preview", "Preview Problem", ViewColumn.Active, {
this.panel = window.createWebviewPanel("leetcode.preview", "Preview Problem", ViewColumn.One, {
enableScripts: true,
enableCommandUris: true,
enableFindWidget: true,
retainContextWhenHidden: true,
localResourceRoots: markdownEngine.localResourceRoots,
});

this.panel.webview.onDidReceiveMessage(async (message: IWebViewMessage) => {
switch (message.command) {
case "ShowProblem":
case "ShowProblem": {
await commands.executeCommand("leetcode.showProblem", this.node);
this.dispose();
return;
await commands.executeCommand("workbench.action.focusSecondEditorGroup");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This line relates to (maybe) a bug in vscode, and I have reported in a issue:
microsoft/vscode#71608, which makes me have to create column 2 first before moving (by calling reveal(ViewColumn.Two)) the webview panel to it.

I've successfully build the vscode repository locally in the aftertoon, and I'd like to have a try to debug the vscode myself.

commands.executeCommand("workbench.action.toggleSidebarVisibility");
this.panel!.reveal(ViewColumn.Two, true);
break;
}
}
}, this, this.context.subscriptions);

Expand All @@ -38,9 +47,10 @@ class LeetCodePreviewProvider implements Disposable {
}, null, this.context.subscriptions);
}

this.panel.webview.html = await this.provideHtmlContent(node);
const description: IDescription = this.parseDescription(descString, node);
this.panel.webview.html = this.getWebViewContent(description);
this.panel.title = `${node.name}: Preview`;
this.panel.reveal();
this.panel.reveal(ViewColumn.One);
}

public dispose(): void {
Expand All @@ -49,21 +59,35 @@ class LeetCodePreviewProvider implements Disposable {
}
}

public async provideHtmlContent(node: IProblem): Promise<string> {
return await this.renderHTML(node);
private parseDescription(descString: string, problem: IProblem): IDescription {
const [
/* title */, ,
url, ,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Each comma without variable before it means ignore this line (normally empty line, or something not needed)

/* tags */, ,
/* langs */, ,
category,
difficulty,
accepted,
submissions,
/* testcase */, ,
...body
] = descString.split("\n");
return {
title: problem.name,
url,
tags: problem.tags,
companies: problem.companies,
category: category.slice(2),
difficulty: difficulty.slice(2),
accepted: accepted.split(": ")[1],
submissions: submissions.split(": ")[1],
body: body.join("\n").replace(/<pre>\s*([^]+?)\s*<\/pre>/g, "<pre><code>$1</code></pre>"),
};
}

private async renderHTML(node: IProblem): Promise<string> {
const description: string = await leetCodeExecutor.getDescription(node);
const descriptionHTML: string = description.replace(/\n/g, "<br>");
const htmlTemplate: string = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Preview Problem</title>
</head>
private getWebViewContent(desc: IDescription): string {
const mdStyles: string = markdownEngine.getStyles();
const buttonStyle: string = `
<style>
#solve {
position: fixed;
Expand All @@ -82,31 +106,75 @@ class LeetCodePreviewProvider implements Disposable {
border: 0;
}
</style>
`;
const { title, url, category, difficulty, accepted, submissions, body } = desc;
const head: string = markdownEngine.render(`# [${title}](${url})`);
const info: string = markdownEngine.render([
`| Category | Difficulty | Accepted | Submissions |`,
`| :------: | :--------: | :------: | :---------: |`,
`| ${category} | ${difficulty} | ${accepted} | ${submissions} |`,
].join("\n"));
const tags: string = [
`<details>`,
`<summary><strong>Tags</strong></summary>`,
markdownEngine.render(
desc.tags
.map((t: string) => `[\`${t}\`](https://leetcode.com/tag/${t})`)
.join(" | "),
),
`</details>`,
].join("\n");
const companies: string = [
`<details>`,
`<summary><strong>Companies</strong></summary>`,
markdownEngine.render(
desc.companies
.map((c: string) => `\`${c}\``)
.join(" | "),
),
`</details>`,
].join("\n");
return `
<!DOCTYPE html>
<html>
<head>
${mdStyles}
${buttonStyle}
</head>
<body>
<div >
${ descriptionHTML}
</div>
${head}
${info}
${tags}
${companies}
${body}
<button id="solve">Code Now</button>
<script>
(function() {
const vscode = acquireVsCodeApi();
let button = document.getElementById('solve');
button.onclick = solveHandler;
function solveHandler() {
vscode.postMessage({
command: 'ShowProblem',
});
}
}());
const vscode = acquireVsCodeApi();
const button = document.getElementById('solve');
button.onclick = () => vscode.postMessage({
command: 'ShowProblem',
});
</script>
</body>
</html>
</html>
`;
return htmlTemplate;
}

}
export interface IWebViewMessage {

interface IDescription {
title: string;
url: string;
tags: string[];
companies: string[];
category: string;
difficulty: string;
accepted: string;
submissions: string;
body: string;
}

interface IWebViewMessage {
command: string;
}

Expand Down