Skip to content

Commit c9092e1

Browse files
authored
Implement classpath document preview page (#578)
1 parent 2dc9201 commit c9092e1

File tree

8 files changed

+562
-2
lines changed

8 files changed

+562
-2
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
"onCommand:java.welcome",
4848
"onWebviewPanel:java.welcome",
4949
"onCommand:java.classpathConfiguration",
50-
"onWebviewPanel:java.classpathConfiguration"
50+
"onWebviewPanel:java.classpathConfiguration",
51+
"onWebviewPanel:java.markdownPreview"
5152
],
5253
"contributes": {
5354
"commands": [
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import * as vscode from "vscode";
5+
import * as fse from "fs-extra";
6+
import * as path from "path";
7+
import { getExtensionContext } from "../utils";
8+
9+
class MarkdownPreviewProvider implements vscode.Disposable {
10+
private panel: vscode.WebviewPanel | undefined;
11+
// a cache maps document path to rendered html
12+
private documentCache: Map<string, string> = new Map<string, string>();
13+
private disposables: vscode.Disposable[] = [];
14+
15+
public async show(markdownFilePath: string, title: string, context: vscode.ExtensionContext, webviewPanel?: vscode.WebviewPanel): Promise<void> {
16+
if (webviewPanel) {
17+
this.panel = webviewPanel;
18+
}
19+
20+
if (!this.panel) {
21+
this.panel = vscode.window.createWebviewPanel("java.markdownPreview", title, vscode.ViewColumn.Beside, {
22+
localResourceRoots: [
23+
vscode.Uri.file(path.join(context.extensionPath, "webview-resources")),
24+
vscode.Uri.file(path.dirname(markdownFilePath)),
25+
],
26+
retainContextWhenHidden: true,
27+
enableFindWidget: true,
28+
enableCommandUris: true,
29+
enableScripts: true,
30+
});
31+
}
32+
33+
this.disposables.push(this.panel.onDidDispose(() => {
34+
this.panel = undefined;
35+
}));
36+
37+
this.panel.iconPath = {
38+
light: vscode.Uri.file(path.join(context.extensionPath, "caption.light.svg")),
39+
dark: vscode.Uri.file(path.join(context.extensionPath, "caption.dark.svg"))
40+
};
41+
this.panel.webview.html = await this.getHtmlContent(this.panel.webview, markdownFilePath, title, context);
42+
this.panel.title = title;
43+
this.panel.reveal(this.panel.viewColumn);
44+
}
45+
46+
public dispose(): void {
47+
if (this.panel) {
48+
this.panel.dispose();
49+
}
50+
for (const disposable of this.disposables) {
51+
disposable.dispose();
52+
}
53+
}
54+
55+
protected async getHtmlContent(webview: vscode.Webview, markdownFilePath: string, title: string, context: vscode.ExtensionContext): Promise<string> {
56+
const nonce: string = this.getNonce();
57+
const styles: string = this.getStyles(webview, context);
58+
let body: string | undefined = this.documentCache.get(markdownFilePath);
59+
if (!body) {
60+
let markdownString: string = await fse.readFile(markdownFilePath, "utf8");
61+
body = await vscode.commands.executeCommand("markdown.api.render", markdownString);
62+
this.documentCache.set(markdownFilePath, body as string);
63+
}
64+
return `
65+
<!DOCTYPE html>
66+
<html>
67+
<head>
68+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; img-src 'self' ${webview.cspSource} https: data:; script-src 'nonce-${nonce}';"/>
69+
<meta charset="UTF-8">
70+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
71+
${styles}
72+
<base href="${webview.asWebviewUri(vscode.Uri.file(markdownFilePath))}">
73+
</head>
74+
<body class="vscode-body scrollBeyondLastLine wordWrap showEditorSelection">
75+
${body}
76+
<script nonce="${nonce}">
77+
(function() {
78+
const vscode = acquireVsCodeApi();
79+
vscode.setState({
80+
markdownUri: "${vscode.Uri.file(markdownFilePath).toString()}",
81+
title: "${title}",
82+
});
83+
})();
84+
</script>
85+
</body>
86+
</html>
87+
`;
88+
}
89+
90+
protected getStyles(webview: vscode.Webview, context: vscode.ExtensionContext): string {
91+
const styles: vscode.Uri[] = [
92+
vscode.Uri.file(path.join(context.extensionPath, "webview-resources", "highlight.css")),
93+
vscode.Uri.file(path.join(context.extensionPath, "webview-resources", "markdown.css")),
94+
];
95+
return styles.map((styleUri: vscode.Uri) => `<link rel="stylesheet" type="text/css" href="${webview.asWebviewUri(styleUri).toString()}">`).join("\n");
96+
}
97+
98+
private getNonce(): string {
99+
let text = "";
100+
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
101+
for (let i = 0; i < 32; i++) {
102+
text += possible.charAt(Math.floor(Math.random() * possible.length));
103+
}
104+
return text;
105+
}
106+
}
107+
108+
export const markdownPreviewProvider: MarkdownPreviewProvider = new MarkdownPreviewProvider();
109+
110+
export class MarkdownPreviewSerializer implements vscode.WebviewPanelSerializer {
111+
async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) {
112+
if (state.markdownUri && state.title) {
113+
markdownPreviewProvider.show(vscode.Uri.parse(state.markdownUri).fsPath, state.title, getExtensionContext(), webviewPanel);
114+
} else {
115+
webviewPanel.dispose();
116+
}
117+
}
118+
}

src/commands/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
import * as vscode from "vscode";
5+
import * as path from "path";
56
import { instrumentCommand, webviewCmdLinkHandler } from "../utils";
67
import { createMavenProjectCmdHandler, createSpringBootProjectCmdHandler, createQuarkusProjectCmdHandler, createMicroProfileStarterProjectCmdHandler, showExtensionCmdHandler, openUrlCmdHandler, showReleaseNotesHandler, installExtensionCmdHandler } from "./handler";
78
import { overviewCmdHandler } from "../overview";
@@ -11,6 +12,9 @@ import { javaExtGuideCmdHandler } from "../ext-guide";
1112
import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper";
1213
import { showWelcomeWebview } from "../welcome";
1314
import { showClasspathConfigurationPage } from "../classpath/classpathConfigurationView";
15+
import { markdownPreviewProvider } from "../classpath/markdownPreviewProvider";
16+
import { getExpService } from "../exp";
17+
import { TreatmentVariables } from "../exp/TrearmentVariables";
1418

1519
export function initialize(context: vscode.ExtensionContext) {
1620
context.subscriptions.push(vscode.commands.registerCommand("java.overview", instrumentCommand(context, "java.overview", instrumentCommand(context, "java.helper.overview", overviewCmdHandler))));
@@ -27,5 +31,15 @@ export function initialize(context: vscode.ExtensionContext) {
2731
context.subscriptions.push(vscode.commands.registerCommand("java.extGuide", instrumentCommand(context, "java.extGuide", javaExtGuideCmdHandler)));
2832
context.subscriptions.push(instrumentOperationAsVsCodeCommand("java.webview.runCommand", webviewCmdLinkHandler));
2933
context.subscriptions.push(vscode.commands.registerCommand("java.welcome", (options) => showWelcomeWebview(context, options)));
30-
context.subscriptions.push(vscode.commands.registerCommand("java.classpathConfiguration", () => showClasspathConfigurationPage(context)));
34+
context.subscriptions.push(vscode.commands.registerCommand("java.classpathConfiguration", async () => {
35+
const showCustomizedView: boolean = await getExpService()?.getTreatmentVariableAsync(TreatmentVariables.VSCodeConfig, TreatmentVariables.CustomizedClasspathConfigurationView, true /*checkCache*/) || false;
36+
if (showCustomizedView) {
37+
showClasspathConfigurationPage(context);
38+
} else {
39+
// To filter the setting in the workspace scope, see: https://github.com/microsoft/vscode/issues/90086#issuecomment-803510704
40+
await vscode.commands.executeCommand("workbench.action.openSettings", "java.project.sourcePaths");
41+
await vscode.commands.executeCommand("workbench.action.openWorkspaceSettings");
42+
markdownPreviewProvider.show(context.asAbsolutePath(path.join("webview-resources", "classpathConfiguration.md")), "Classpath Settings", context);
43+
}
44+
}));
3145
}

src/exp/TrearmentVariables.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
export class TreatmentVariables {
55
public static readonly VSCodeConfig = 'vscode';
66
public static readonly PresentWelcomePageByDefault = 'presentWelcomePageByDefault';
7+
public static readonly CustomizedClasspathConfigurationView = 'customizedClasspathConfigurationView';
78
}

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { JavaGettingStartedViewSerializer } from "./getting-started";
1616
import { JavaExtGuideViewSerializer } from "./ext-guide";
1717
import { ClassPathConfigurationViewSerializer } from "./classpath/classpathConfigurationView";
1818
import { TreatmentVariables } from "./exp/TrearmentVariables";
19+
import { MarkdownPreviewSerializer } from "./classpath/markdownPreviewProvider";
1920

2021
export async function activate(context: vscode.ExtensionContext) {
2122
syncState(context);
@@ -37,6 +38,7 @@ async function initializeExtension(_operationId: string, context: vscode.Extensi
3738
context.subscriptions.push(vscode.window.registerWebviewPanelSerializer("java.gettingStarted", new JavaGettingStartedViewSerializer()));
3839
context.subscriptions.push(vscode.window.registerWebviewPanelSerializer("java.welcome", new WelcomeViewSerializer()));
3940
context.subscriptions.push(vscode.window.registerWebviewPanelSerializer("java.classpathConfiguration", new ClassPathConfigurationViewSerializer()));
41+
context.subscriptions.push(vscode.window.registerWebviewPanelSerializer("java.markdownPreview", new MarkdownPreviewSerializer()));
4042

4143
const config = vscode.workspace.getConfiguration("java.help");
4244

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Classpath Settings
2+
3+
There are several settings that can help configure the classpath components of your unmanaged folder.
4+
> Note: For project with build tools, like Maven and Gradle, please configure the related entries in the [pom.xml](https://maven.apache.org/pom.html#directories) or [build.gradle](https://docs.gradle.org/current/userguide/java_plugin.html#source_sets) file.
5+
6+
* `java.project.sourcePaths`: Relative paths to the workspace where stores the source files.
7+
> Note: The setting is `Only` effective in the `WORKSPACE` scope, and it will `NOT` affect Maven or Gradle project.
8+
9+
* `java.project.outputPath`: A relative path to the workspace where stores the compiled output.
10+
> Note: The setting is `Only` effective in the `WORKSPACE` scope, and it will `NOT` affect Maven or Gradle project.
11+
12+
* `java.configuration.runtimes`: Map Java Execution Environments to local JDKs.
13+
> Note: This setting is only available in the `USER` scope.
14+
15+
* `java.project.referencedLibraries`: Configure glob patterns for referencing local libraries to a Java project.

webview-resources/highlight.css

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs2015.css
3+
*/
4+
/*
5+
* Visual Studio 2015 dark style
6+
* Author: Nicolas LLOBERA <[email protected]>
7+
*/
8+
9+
10+
.hljs-keyword,
11+
.hljs-literal,
12+
.hljs-symbol,
13+
.hljs-name {
14+
color: #569CD6;
15+
}
16+
.hljs-link {
17+
color: #569CD6;
18+
text-decoration: underline;
19+
}
20+
21+
.hljs-built_in,
22+
.hljs-type {
23+
color: #4EC9B0;
24+
}
25+
26+
.hljs-number,
27+
.hljs-class {
28+
color: #B8D7A3;
29+
}
30+
31+
.hljs-string,
32+
.hljs-meta-string {
33+
color: #D69D85;
34+
}
35+
36+
.hljs-regexp,
37+
.hljs-template-tag {
38+
color: #9A5334;
39+
}
40+
41+
.hljs-subst,
42+
.hljs-function,
43+
.hljs-title,
44+
.hljs-params,
45+
.hljs-formula {
46+
color: #DCDCDC;
47+
}
48+
49+
.hljs-comment,
50+
.hljs-quote {
51+
color: #57A64A;
52+
font-style: italic;
53+
}
54+
55+
.hljs-doctag {
56+
color: #608B4E;
57+
}
58+
59+
.hljs-meta,
60+
.hljs-meta-keyword,
61+
.hljs-tag {
62+
color: #9B9B9B;
63+
}
64+
65+
.hljs-variable,
66+
.hljs-template-variable {
67+
color: #BD63C5;
68+
}
69+
70+
.hljs-attr,
71+
.hljs-attribute,
72+
.hljs-builtin-name {
73+
color: #9CDCFE;
74+
}
75+
76+
.hljs-section {
77+
color: gold;
78+
}
79+
80+
.hljs-emphasis {
81+
font-style: italic;
82+
}
83+
84+
.hljs-strong {
85+
font-weight: bold;
86+
}
87+
88+
/*.hljs-code {
89+
font-family:'Monospace';
90+
}*/
91+
92+
.hljs-bullet,
93+
.hljs-selector-tag,
94+
.hljs-selector-id,
95+
.hljs-selector-class,
96+
.hljs-selector-attr,
97+
.hljs-selector-pseudo {
98+
color: #D7BA7D;
99+
}
100+
101+
.hljs-addition {
102+
background-color: var(--vscode-diffEditor-insertedTextBackground, rgba(155, 185, 85, 0.2));
103+
color: rgb(155, 185, 85);
104+
display: inline-block;
105+
width: 100%;
106+
}
107+
108+
.hljs-deletion {
109+
background: var(--vscode-diffEditor-removedTextBackground, rgba(255, 0, 0, 0.2));
110+
color: rgb(255, 0, 0);
111+
display: inline-block;
112+
width: 100%;
113+
}
114+
115+
116+
/*
117+
From https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs.css
118+
*/
119+
/*
120+
121+
Visual Studio-like style based on original C# coloring by Jason Diamond <[email protected]>
122+
123+
*/
124+
125+
.vscode-light .hljs-function,
126+
.vscode-light .hljs-params,
127+
.vscode-light .hljs-number,
128+
.vscode-light .hljs-class {
129+
color: inherit;
130+
}
131+
132+
.vscode-light .hljs-comment,
133+
.vscode-light .hljs-quote,
134+
.vscode-light .hljs-number,
135+
.vscode-light .hljs-class,
136+
.vscode-light .hljs-variable {
137+
color: #008000;
138+
}
139+
140+
.vscode-light .hljs-keyword,
141+
.vscode-light .hljs-selector-tag,
142+
.vscode-light .hljs-name,
143+
.vscode-light .hljs-tag {
144+
color: #00f;
145+
}
146+
147+
.vscode-light .hljs-built_in,
148+
.vscode-light .hljs-builtin-name {
149+
color: #007acc;
150+
}
151+
152+
.vscode-light .hljs-string,
153+
.vscode-light .hljs-section,
154+
.vscode-light .hljs-attribute,
155+
.vscode-light .hljs-literal,
156+
.vscode-light .hljs-template-tag,
157+
.vscode-light .hljs-template-variable,
158+
.vscode-light .hljs-type {
159+
color: #a31515;
160+
}
161+
162+
.vscode-light .hljs-selector-attr,
163+
.vscode-light .hljs-selector-pseudo,
164+
.vscode-light .hljs-meta,
165+
.vscode-light .hljs-meta-keyword {
166+
color: #2b91af;
167+
}
168+
169+
.vscode-light .hljs-title,
170+
.vscode-light .hljs-doctag {
171+
color: #808080;
172+
}
173+
174+
.vscode-light .hljs-attr {
175+
color: #f00;
176+
}
177+
178+
.vscode-light .hljs-symbol,
179+
.vscode-light .hljs-bullet,
180+
.vscode-light .hljs-link {
181+
color: #00b0e8;
182+
}
183+
184+
185+
.vscode-light .hljs-emphasis {
186+
font-style: italic;
187+
}
188+
189+
.vscode-light .hljs-strong {
190+
font-weight: bold;
191+
}

0 commit comments

Comments
 (0)