Skip to content

Commit 4eefc70

Browse files
Vigilansjdneo
authored andcommitted
Apply markdown engine to result provider (LeetCode-OpenSource#232)
1 parent 3028be4 commit 4eefc70

6 files changed

+82
-33
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@
321321
"dependencies": {
322322
"fs-extra": "^6.0.1",
323323
"highlight.js": "^9.15.6",
324+
"lodash": "^4.17.11",
324325
"lodash.kebabcase": "^4.1.1",
325326
"markdown-it": "^8.4.2",
326327
"require-from-string": "^2.0.2",

src/explorer/LeetCodeTreeDataProvider.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) jdneo. All rights reserved.
22
// Licensed under the MIT license.
33

4+
import * as _ from "lodash";
45
import * as os from "os";
56
import * as path from "path";
67
import * as vscode from "vscode";
@@ -201,10 +202,10 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
201202
private addProblemToTreeData(problem: IProblem): void {
202203
this.putProblemToMap(this.treeData.Difficulty, problem.difficulty, problem);
203204
for (const tag of problem.tags) {
204-
this.putProblemToMap(this.treeData.Tag, this.beautifyCategoryName(tag), problem);
205+
this.putProblemToMap(this.treeData.Tag, _.startCase(tag), problem);
205206
}
206207
for (const company of problem.companies) {
207-
this.putProblemToMap(this.treeData.Company, this.beautifyCategoryName(company), problem);
208+
this.putProblemToMap(this.treeData.Company, _.startCase(company), problem);
208209
}
209210
}
210211

@@ -217,10 +218,6 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
217218
}
218219
}
219220

220-
private beautifyCategoryName(name: string): string {
221-
return name.split("-").map((c: string) => c[0].toUpperCase() + c.slice(1)).join(" ");
222-
}
223-
224221
private getSubCategoryNodes(map: Map<string, IProblem[]>, category: Category): LeetCodeNode[] {
225222
const subCategoryNodes: LeetCodeNode[] = Array.from(map.keys()).map((subCategory: string) => {
226223
return new LeetCodeNode(Object.assign({}, defaultProblem, {

src/webview/leetCodePreviewProvider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class LeetCodePreviewProvider implements Disposable {
3939
}
4040

4141
this.panel.webview.html = await this.provideHtmlContent(node);
42-
this.panel.title = node.name;
42+
this.panel.title = `${node.name}: Preview`;
4343
this.panel.reveal();
4444
}
4545

src/webview/leetCodeResultProvider.ts

+64-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Copyright (c) jdneo. All rights reserved.
22
// Licensed under the MIT license.
33

4+
import * as _ from "lodash";
45
import { Disposable, ExtensionContext, ViewColumn, WebviewPanel, window } from "vscode";
6+
import { markdownEngine } from "./markdownEngine";
57

68
class LeetCodeResultProvider implements Disposable {
79

@@ -12,19 +14,21 @@ class LeetCodeResultProvider implements Disposable {
1214
this.context = context;
1315
}
1416

15-
public async show(result: string): Promise<void> {
17+
public async show(resultString: string): Promise<void> {
1618
if (!this.panel) {
17-
this.panel = window.createWebviewPanel("leetcode.result", "LeetCode Results", ViewColumn.Two, {
19+
this.panel = window.createWebviewPanel("leetcode.result", "Submission Result", ViewColumn.Two, {
1820
retainContextWhenHidden: true,
1921
enableFindWidget: true,
22+
localResourceRoots: markdownEngine.localResourceRoots,
2023
});
2124

2225
this.panel.onDidDispose(() => {
2326
this.panel = undefined;
2427
}, null, this.context.subscriptions);
2528
}
2629

27-
this.panel.webview.html = await this.provideHtmlContent(result);
30+
const result: IResult = this.parseResult(resultString);
31+
this.panel.webview.html = this.getWebViewContent(result);
2832
this.panel.reveal(ViewColumn.Two);
2933
}
3034

@@ -34,19 +38,67 @@ class LeetCodeResultProvider implements Disposable {
3438
}
3539
}
3640

37-
private async provideHtmlContent(result: string): Promise<string> {
38-
return `<!DOCTYPE html>
39-
<html lang="en">
41+
private parseResult(raw: string): IResult {
42+
raw = raw.concat(" √ "); // Append a dummy sentinel to the end of raw string
43+
const regSplit: RegExp = / [×vx] ([^]+?)\n(?= [×vx] )/g;
44+
const regKeyVal: RegExp = /(.+?): ([^]*)/;
45+
const result: IResult = { messages: [] };
46+
let entry: RegExpExecArray | null;
47+
do {
48+
entry = regSplit.exec(raw);
49+
if (!entry) {
50+
continue;
51+
}
52+
const kvMatch: RegExpExecArray | null = regKeyVal.exec(entry[1]);
53+
if (kvMatch) {
54+
const key: string = _.startCase(kvMatch[1]);
55+
let value: string = kvMatch[2];
56+
if (!result[key]) {
57+
result[key] = [];
58+
}
59+
if (key === "Testcase") {
60+
value = value.slice(1, -1).replace("\\n", "\n");
61+
}
62+
result[key].push(value);
63+
} else {
64+
result.messages.push(entry[1]);
65+
}
66+
} while (entry);
67+
return result;
68+
}
69+
70+
private getWebViewContent(result: IResult): string {
71+
const styles: string = markdownEngine.getStylesHTML();
72+
const title: string = `## ${result.messages[0]}`;
73+
const messages: string[] = result.messages.slice(1).map((m: string) => `* ${m}`);
74+
const sections: string[] = Object.keys(result).filter((k: string) => k !== "messages").map((key: string) => [
75+
`### ${key}`,
76+
"```",
77+
result[key].join("\n\n"),
78+
"```",
79+
].join("\n"));
80+
const body: string = markdownEngine.render([
81+
title,
82+
...messages,
83+
...sections,
84+
].join("\n"));
85+
return `
86+
<!DOCTYPE html>
87+
<html>
4088
<head>
41-
<meta charset="UTF-8">
42-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
43-
<title>LeetCode Results</title>
89+
${styles}
4490
</head>
45-
<body>
46-
<pre>${result.trim()}</pre>
91+
<body class="vscode-body 'scrollBeyondLastLine' 'wordWrap' 'showEditorSelection'" style="tab-size:4">
92+
${body}
4793
</body>
48-
</html>`;
94+
</html>
95+
`;
4996
}
5097
}
5198

99+
interface IResult {
100+
[key: string]: string[];
101+
messages: string[];
102+
}
103+
52104
export const leetCodeResultProvider: LeetCodeResultProvider = new LeetCodeResultProvider();

src/webview/leetCodeSolutionProvider.ts

+10-13
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,33 @@
33

44
import { Disposable, ExtensionContext, ViewColumn, WebviewPanel, window } from "vscode";
55
import { IProblem } from "../shared";
6-
import { MarkdownEngine } from "./MarkdownEngine";
6+
import { markdownEngine } from "./markdownEngine";
77

88
class LeetCodeSolutionProvider implements Disposable {
99

1010
private context: ExtensionContext;
1111
private panel: WebviewPanel | undefined;
12-
private mdEngine: MarkdownEngine;
13-
private solution: Solution;
1412

1513
public initialize(context: ExtensionContext): void {
1614
this.context = context;
17-
this.mdEngine = new MarkdownEngine();
1815
}
1916

2017
public async show(solutionString: string, problem: IProblem): Promise<void> {
2118
if (!this.panel) {
2219
this.panel = window.createWebviewPanel("leetCode.solution", "Top Voted Solution", ViewColumn.Active, {
2320
retainContextWhenHidden: true,
2421
enableFindWidget: true,
25-
localResourceRoots: this.mdEngine.localResourceRoots,
22+
localResourceRoots: markdownEngine.localResourceRoots,
2623
});
2724

2825
this.panel.onDidDispose(() => {
2926
this.panel = undefined;
3027
}, null, this.context.subscriptions);
3128
}
3229

33-
this.solution = this.parseSolution(solutionString);
34-
this.panel.title = problem.name;
35-
this.panel.webview.html = this.getWebViewContent(this.solution);
30+
const solution: Solution = this.parseSolution(solutionString);
31+
this.panel.title = `${problem.name}: Solution`;
32+
this.panel.webview.html = this.getWebViewContent(solution);
3633
this.panel.reveal(ViewColumn.Active);
3734
}
3835

@@ -56,17 +53,17 @@ class LeetCodeSolutionProvider implements Disposable {
5653
}
5754

5855
private getWebViewContent(solution: Solution): string {
59-
const styles: string = this.mdEngine.getStylesHTML();
56+
const styles: string = markdownEngine.getStylesHTML();
6057
const { title, url, lang, author, votes } = solution;
61-
const head: string = this.mdEngine.render(`# [${title}](${url})`);
58+
const head: string = markdownEngine.render(`# [${title}](${url})`);
6259
const auth: string = `[${author}](https://leetcode.com/${author}/)`;
63-
const info: string = this.mdEngine.render([
60+
const info: string = markdownEngine.render([
6461
`| Language | Author | Votes |`,
6562
`| :------: | :------: | :------: |`,
6663
`| ${lang} | ${auth} | ${votes} |`,
6764
].join("\n"));
68-
const body: string = this.mdEngine.render(solution.body, {
69-
lang: this.solution.lang,
65+
const body: string = markdownEngine.render(solution.body, {
66+
lang: solution.lang,
7067
host: "https://discuss.leetcode.com/",
7168
});
7269
return `

src/webview/MarkdownEngine.ts renamed to src/webview/markdownEngine.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as path from "path";
88
import * as vscode from "vscode";
99
import { leetCodeChannel } from "../leetCodeChannel";
1010

11-
export class MarkdownEngine {
11+
class MarkdownEngine {
1212

1313
private readonly engine: MarkdownIt;
1414
private readonly extRoot: string; // root path of vscode built-in markdown extension
@@ -106,3 +106,5 @@ export class MarkdownEngine {
106106
};
107107
}
108108
}
109+
110+
export const markdownEngine: MarkdownEngine = new MarkdownEngine();

0 commit comments

Comments
 (0)