Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 781feb2

Browse files
Vigilansjdneo
authored andcommitted
Extract leetcode webview base class & add md settings listener (#270)
1 parent ecac298 commit 781feb2

File tree

9 files changed

+226
-192
lines changed

9 files changed

+226
-192
lines changed

‎src/commands/submit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { leetCodeExecutor } from "../leetCodeExecutor";
66
import { leetCodeManager } from "../leetCodeManager";
77
import { DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils";
88
import { getActiveFilePath } from "../utils/workspaceUtils";
9-
import { leetCodeResultProvider } from "../webview/leetCodeResultProvider";
9+
import { leetCodeSubmissionProvider } from "../webview/leetCodeSubmissionProvider";
1010

1111
export async function submitSolution(uri?: vscode.Uri): Promise<void> {
1212
if (!leetCodeManager.getUser()) {
@@ -21,7 +21,7 @@ export async function submitSolution(uri?: vscode.Uri): Promise<void> {
2121

2222
try {
2323
const result: string = await leetCodeExecutor.submitSolution(filePath);
24-
await leetCodeResultProvider.show(result);
24+
await leetCodeSubmissionProvider.show(result);
2525
} catch (error) {
2626
await promptForOpenOutputChannel("Failed to submit the solution. Please open the output channel for details.", DialogType.error);
2727
}

‎src/commands/test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { isWindows, usingCmd } from "../utils/osUtils";
1010
import { DialogType, promptForOpenOutputChannel, showFileSelectDialog } from "../utils/uiUtils";
1111
import { getActiveFilePath } from "../utils/workspaceUtils";
1212
import * as wsl from "../utils/wslUtils";
13-
import { leetCodeResultProvider } from "../webview/leetCodeResultProvider";
13+
import { leetCodeSubmissionProvider } from "../webview/leetCodeSubmissionProvider";
1414

1515
export async function testSolution(uri?: vscode.Uri): Promise<void> {
1616
try {
@@ -81,7 +81,7 @@ export async function testSolution(uri?: vscode.Uri): Promise<void> {
8181
if (!result) {
8282
return;
8383
}
84-
await leetCodeResultProvider.show(result);
84+
await leetCodeSubmissionProvider.show(result);
8585
} catch (error) {
8686
await promptForOpenOutputChannel("Failed to test the solution. Please open the output channel for details.", DialogType.error);
8787
}

‎src/extension.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ import { leetCodeManager } from "./leetCodeManager";
1818
import { leetCodeStatusBarController } from "./statusbar/leetCodeStatusBarController";
1919
import { DialogType, promptForOpenOutputChannel } from "./utils/uiUtils";
2020
import { leetCodePreviewProvider } from "./webview/leetCodePreviewProvider";
21-
import { leetCodeResultProvider } from "./webview/leetCodeResultProvider";
2221
import { leetCodeSolutionProvider } from "./webview/leetCodeSolutionProvider";
22+
import { leetCodeSubmissionProvider } from "./webview/leetCodeSubmissionProvider";
23+
import { markdownEngine } from "./webview/markdownEngine";
2324

2425
export async function activate(context: vscode.ExtensionContext): Promise<void> {
2526
try {
@@ -33,17 +34,15 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
3334
});
3435

3536
const leetCodeTreeDataProvider: LeetCodeTreeDataProvider = new LeetCodeTreeDataProvider(context);
36-
leetCodePreviewProvider.initialize(context);
37-
leetCodeResultProvider.initialize(context);
38-
leetCodeSolutionProvider.initialize(context);
3937

4038
context.subscriptions.push(
4139
leetCodeStatusBarController,
4240
leetCodeChannel,
4341
leetCodePreviewProvider,
44-
leetCodeResultProvider,
42+
leetCodeSubmissionProvider,
4543
leetCodeSolutionProvider,
4644
leetCodeExecutor,
45+
markdownEngine,
4746
vscode.window.createTreeView("leetCodeExplorer", { treeDataProvider: leetCodeTreeDataProvider, showCollapseAll: true }),
4847
vscode.languages.registerCodeLensProvider({ scheme: "file" }, codeLensProvider),
4948
vscode.commands.registerCommand("leetcode.deleteCache", () => cache.deleteCache()),

‎src/webview/LeetCodeWebview.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) jdneo. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import { ConfigurationChangeEvent, Disposable, ViewColumn, WebviewPanel, window, workspace } from "vscode";
5+
import { markdownEngine } from "./markdownEngine";
6+
7+
export abstract class LeetCodeWebview implements Disposable {
8+
9+
protected panel: WebviewPanel | undefined;
10+
private listeners: Disposable[] = [];
11+
12+
public dispose(): void {
13+
if (this.panel) {
14+
this.panel.dispose();
15+
}
16+
}
17+
18+
protected showWebviewInternal(): void {
19+
const { viewType, title, viewColumn, preserveFocus } = this.getWebviewOption();
20+
if (!this.panel) {
21+
this.panel = window.createWebviewPanel(viewType, title, { viewColumn, preserveFocus }, {
22+
enableScripts: true,
23+
enableCommandUris: true,
24+
enableFindWidget: true,
25+
retainContextWhenHidden: true,
26+
localResourceRoots: markdownEngine.localResourceRoots,
27+
});
28+
this.panel.onDidDispose(this.onDidDisposeWebview, this, this.listeners);
29+
this.panel.webview.onDidReceiveMessage(this.onDidReceiveMessage, this, this.listeners);
30+
workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.listeners);
31+
} else {
32+
this.panel.title = title;
33+
this.panel.reveal(viewColumn, preserveFocus);
34+
}
35+
this.panel.webview.html = this.getWebviewContent();
36+
}
37+
38+
protected onDidDisposeWebview(): void {
39+
this.panel = undefined;
40+
for (const listener of this.listeners) {
41+
listener.dispose();
42+
}
43+
this.listeners = [];
44+
}
45+
46+
protected async onDidChangeConfiguration(event: ConfigurationChangeEvent): Promise<void> {
47+
if (this.panel && event.affectsConfiguration("markdown")) {
48+
this.panel.webview.html = this.getWebviewContent();
49+
}
50+
}
51+
52+
protected async onDidReceiveMessage(_message: any): Promise<void> { /* no special rule */ }
53+
54+
protected abstract getWebviewOption(): ILeetCodeWebviewOption;
55+
56+
protected abstract getWebviewContent(): string;
57+
}
58+
59+
export interface ILeetCodeWebviewOption {
60+
viewType: string;
61+
title: string;
62+
viewColumn: ViewColumn;
63+
preserveFocus?: boolean;
64+
}

‎src/webview/leetCodePreviewProvider.ts

Lines changed: 69 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,39 @@
11
// Copyright (c) jdneo. All rights reserved.
22
// Licensed under the MIT license.
33

4-
import { commands, Disposable,ExtensionContext,ViewColumn,WebviewPanel,window } from "vscode";
4+
import { commands, ViewColumn } from "vscode";
55
import { leetCodeExecutor } from "../leetCodeExecutor";
66
import { IProblem } from "../shared";
7+
import { ILeetCodeWebviewOption, LeetCodeWebview } from "./LeetCodeWebview";
78
import { markdownEngine } from "./markdownEngine";
89

9-
class LeetCodePreviewProvider implementsDisposable {
10+
class LeetCodePreviewProvider extendsLeetCodeWebview {
1011

11-
private context: ExtensionContext;
1212
private node: IProblem;
13-
private panel: WebviewPanel | undefined;
14-
15-
public initialize(context: ExtensionContext): void {
16-
this.context = context;
17-
}
13+
private description: IDescription;
1814

1915
public async show(node: IProblem): Promise<void> {
20-
// Fetch problem first before creating webview panel
21-
const descString: string = await leetCodeExecutor.getDescription(node);
22-
16+
this.description = this.parseDescription(await leetCodeExecutor.getDescription(node), node);
2317
this.node = node;
24-
if (!this.panel) {
25-
this.panel = window.createWebviewPanel("leetcode.preview", "Preview Problem", ViewColumn.One, {
26-
enableScripts: true,
27-
enableCommandUris: true,
28-
enableFindWidget: true,
29-
retainContextWhenHidden: true,
30-
localResourceRoots: markdownEngine.localResourceRoots,
31-
});
32-
33-
this.panel.webview.onDidReceiveMessage(async (message: IWebViewMessage) => {
34-
switch (message.command) {
35-
case "ShowProblem": {
36-
await commands.executeCommand("leetcode.showProblem", this.node);
37-
break;
38-
}
39-
}
40-
}, this, this.context.subscriptions);
41-
42-
this.panel.onDidDispose(() => {
43-
this.panel = undefined;
44-
}, null, this.context.subscriptions);
45-
}
46-
47-
const description: IDescription = this.parseDescription(descString, node);
48-
this.panel.webview.html = this.getWebViewContent(description);
49-
this.panel.title = `${node.name}: Preview`;
50-
this.panel.reveal(ViewColumn.One);
18+
this.showWebviewInternal();
5119
}
5220

53-
public dispose(): void {
54-
if (this.panel) {
55-
this.panel.dispose();
56-
}
57-
}
58-
59-
private parseDescription(descString: string, problem: IProblem): IDescription {
60-
const [
61-
/* title */, ,
62-
url, ,
63-
/* tags */, ,
64-
/* langs */, ,
65-
category,
66-
difficulty,
67-
likes,
68-
dislikes,
69-
/* accepted */,
70-
/* submissions */,
71-
/* testcase */, ,
72-
...body
73-
] = descString.split("\n");
21+
protected getWebviewOption(): ILeetCodeWebviewOption {
7422
return {
75-
title: problem.name,
76-
url,
77-
tags: problem.tags,
78-
companies: problem.companies,
79-
category: category.slice(2),
80-
difficulty: difficulty.slice(2),
81-
likes: likes.split(": ")[1].trim(),
82-
dislikes: dislikes.split(": ")[1].trim(),
83-
body: body.join("\n").replace(/<pre>\s*([^]+?)\s*<\/pre>/g, "<pre><code>1ドル</code></pre>"),
23+
viewType: "leetcode.preview",
24+
title: `${this.node.name}: Preview`,
25+
viewColumn: ViewColumn.One,
8426
};
8527
}
8628

87-
private getWebViewContent(desc: IDescription): string {
88-
const mdStyles: string = markdownEngine.getStyles();
89-
const buttonStyle: string = `
90-
<style>
29+
protected getWebviewContent(): string {
30+
const button: { element: string, script: string, style: string } = {
31+
element: `<button id="solve">Code Now</button>`,
32+
script: `const button = document.getElementById('solve');
33+
button.onclick = () => vscode.postMessage({
34+
command: 'ShowProblem',
35+
});`,
36+
style: `<style>
9137
#solve {
9238
position: fixed;
9339
bottom: 1rem;
@@ -104,9 +50,9 @@ class LeetCodePreviewProvider implements Disposable {
10450
#solve:active {
10551
border: 0;
10652
}
107-
</style>
108-
`;
109-
const { title, url, category, difficulty, likes, dislikes, body } = desc;
53+
</style>`,
54+
};
55+
const { title, url, category, difficulty, likes, dislikes, body } = this.description;
11056
const head: string = markdownEngine.render(`# [${title}](${url})`);
11157
const info: string = markdownEngine.render([
11258
`| Category | Difficulty | Likes | Dislikes |`,
@@ -117,7 +63,7 @@ class LeetCodePreviewProvider implements Disposable {
11763
`<details>`,
11864
`<summary><strong>Tags</strong></summary>`,
11965
markdownEngine.render(
120-
desc.tags
66+
this.description.tags
12167
.map((t: string) => `[\`${t}\`](https://leetcode.com/tag/${t})`)
12268
.join(" | "),
12369
),
@@ -127,7 +73,7 @@ class LeetCodePreviewProvider implements Disposable {
12773
`<details>`,
12874
`<summary><strong>Companies</strong></summary>`,
12975
markdownEngine.render(
130-
desc.companies
76+
this.description.companies
13177
.map((c: string) => `\`${c}\``)
13278
.join(" | "),
13379
),
@@ -137,28 +83,67 @@ class LeetCodePreviewProvider implements Disposable {
13783
<!DOCTYPE html>
13884
<html>
13985
<head>
140-
${mdStyles}
141-
${buttonStyle}
86+
${markdownEngine.getStyles()}
87+
${button.style}
14288
</head>
14389
<body>
14490
${head}
14591
${info}
14692
${tags}
14793
${companies}
14894
${body}
149-
<button id="solve">Code Now</button>
95+
${button.element}
15096
<script>
15197
const vscode = acquireVsCodeApi();
152-
const button = document.getElementById('solve');
153-
button.onclick = () => vscode.postMessage({
154-
command: 'ShowProblem',
155-
});
98+
${button.script}
15699
</script>
157100
</body>
158101
</html>
159102
`;
160103
}
161104

105+
protected onDidDisposeWebview(): void {
106+
super.onDidDisposeWebview();
107+
delete this.node;
108+
delete this.description;
109+
}
110+
111+
protected async onDidReceiveMessage(message: IWebViewMessage): Promise<void> {
112+
switch (message.command) {
113+
case "ShowProblem": {
114+
await commands.executeCommand("leetcode.showProblem", this.node);
115+
break;
116+
}
117+
}
118+
}
119+
120+
private parseDescription(descString: string, problem: IProblem): IDescription {
121+
const [
122+
/* title */, ,
123+
url, ,
124+
/* tags */, ,
125+
/* langs */, ,
126+
category,
127+
difficulty,
128+
likes,
129+
dislikes,
130+
/* accepted */,
131+
/* submissions */,
132+
/* testcase */, ,
133+
...body
134+
] = descString.split("\n");
135+
return {
136+
title: problem.name,
137+
url,
138+
tags: problem.tags,
139+
companies: problem.companies,
140+
category: category.slice(2),
141+
difficulty: difficulty.slice(2),
142+
likes: likes.split(": ")[1].trim(),
143+
dislikes: dislikes.split(": ")[1].trim(),
144+
body: body.join("\n").replace(/<pre>\s*([^]+?)\s*<\/pre>/g, "<pre><code>1ドル</code></pre>"),
145+
};
146+
}
162147
}
163148

164149
interface IDescription {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /