Skip to content

Commit fc86213

Browse files
committed
Add custom HTML content views
This change adds new custom HTML content views which are driven by the new HtmlContentView APIs in the PowerShellEditorServices.VSCode module. These views allow the user to show a custom view with rendered HTML content that can either be set or appended to using PowerShell cmdlets. Resolves #191
1 parent e5b745c commit fc86213

File tree

2 files changed

+246
-1
lines changed

2 files changed

+246
-1
lines changed

src/features/CustomViews.ts

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
5+
import vscode = require('vscode');
6+
import { IFeature } from '../feature';
7+
import { LanguageClient, RequestType, NotificationType } from 'vscode-languageclient';
8+
9+
export class CustomViewsFeature implements IFeature {
10+
11+
private commands: vscode.Disposable[] = [];
12+
private languageClient: LanguageClient;
13+
private contentProvider: PowerShellContentProvider;
14+
15+
constructor() {
16+
this.contentProvider = new PowerShellContentProvider();
17+
this.commands.push(
18+
vscode.workspace.registerTextDocumentContentProvider(
19+
"powershell",
20+
this.contentProvider));
21+
}
22+
23+
public setLanguageClient(languageClient: LanguageClient) {
24+
25+
languageClient.onRequest(
26+
NewCustomViewRequest.type,
27+
args => {
28+
this.contentProvider.createView(
29+
args.id,
30+
args.title,
31+
args.viewType);
32+
});
33+
34+
languageClient.onRequest(
35+
ShowCustomViewRequest.type,
36+
args => {
37+
this.contentProvider.showView(
38+
args.id,
39+
args.viewColumn);
40+
});
41+
42+
languageClient.onRequest(
43+
CloseCustomViewRequest.type,
44+
args => {
45+
this.contentProvider.closeView(args.id);
46+
});
47+
48+
languageClient.onRequest(
49+
SetHtmlContentViewRequest.type,
50+
args => {
51+
this.contentProvider.setHtmlContentView(
52+
args.id,
53+
args.htmlBodyContent);
54+
});
55+
56+
languageClient.onRequest(
57+
AppendHtmlOutputViewRequest.type,
58+
args => {
59+
this.contentProvider.appendHtmlOutputView(
60+
args.id,
61+
args.appendedHtmlBodyContent);
62+
});
63+
64+
this.languageClient = languageClient;
65+
}
66+
67+
public dispose() {
68+
this.commands.forEach(d => d.dispose());
69+
}
70+
}
71+
72+
class PowerShellContentProvider implements vscode.TextDocumentContentProvider {
73+
74+
private count: number = 1;
75+
private viewIndex: { [id: string]: CustomView } = {};
76+
private didChangeEvent: vscode.EventEmitter<vscode.Uri> = new vscode.EventEmitter<vscode.Uri>();
77+
78+
public provideTextDocumentContent(uri: vscode.Uri): string {
79+
return this.viewIndex[uri.toString()].getContent();
80+
}
81+
82+
public createView(id: string, title: string, viewType: CustomViewType) {
83+
let view = undefined;
84+
switch (viewType) {
85+
case CustomViewType.HtmlContent:
86+
view = new HtmlContentView(id, title);
87+
};
88+
89+
this.viewIndex[this.getUri(view.id)] = view;
90+
}
91+
92+
public showView(id: string, viewColumn: vscode.ViewColumn) {
93+
let uriString = this.getUri(id);
94+
let view: CustomView = this.viewIndex[uriString];
95+
96+
vscode.commands.executeCommand(
97+
"vscode.previewHtml",
98+
uriString,
99+
viewColumn,
100+
view.title);
101+
}
102+
103+
public closeView(id: string) {
104+
let uriString = this.getUri(id);
105+
let view: CustomView = this.viewIndex[uriString];
106+
107+
vscode.workspace.textDocuments.some(
108+
doc => {
109+
if (doc.uri.toString() === uriString) {
110+
vscode.window
111+
.showTextDocument(doc)
112+
.then(editor => vscode.commands.executeCommand("workbench.action.closeActiveEditor"))
113+
114+
return true;
115+
}
116+
117+
return false;
118+
}
119+
)
120+
}
121+
122+
public setHtmlContentView(id: string, content: string) {
123+
let uriString = this.getUri(id);
124+
let view: CustomView = this.viewIndex[uriString];
125+
126+
if (view.viewType === CustomViewType.HtmlContent) {
127+
(<HtmlContentView>view).setContent(content);
128+
this.didChangeEvent.fire(vscode.Uri.parse(uriString));
129+
}
130+
}
131+
132+
public appendHtmlOutputView(id: string, content: string) {
133+
let uriString = this.getUri(id);
134+
let view: CustomView = this.viewIndex[uriString];
135+
136+
if (view.viewType === CustomViewType.HtmlContent) {
137+
(<HtmlContentView>view).appendContent(content);
138+
this.didChangeEvent.fire(vscode.Uri.parse(uriString));
139+
}
140+
}
141+
142+
private getUri(id: string) {
143+
return `powershell://views/${id}`;
144+
}
145+
146+
public onDidChange: vscode.Event<vscode.Uri> = this.didChangeEvent.event;
147+
}
148+
149+
abstract class CustomView {
150+
151+
constructor(
152+
public id: string,
153+
public title: string,
154+
public viewType: CustomViewType)
155+
{
156+
}
157+
158+
abstract getContent(): string;
159+
}
160+
161+
class HtmlContentView extends CustomView {
162+
163+
private htmlContent: string = "";
164+
165+
constructor(
166+
id: string,
167+
title: string)
168+
{
169+
super(id, title, CustomViewType.HtmlContent);
170+
}
171+
172+
setContent(htmlContent: string) {
173+
this.htmlContent = htmlContent;
174+
}
175+
176+
appendContent(content: string) {
177+
this.htmlContent += content;
178+
}
179+
180+
getContent(): string {
181+
// Return an HTML page which disables JavaScript in content by default
182+
return `<html><head><meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src *; style-src 'self'; script-src 'none';"></head><body>${this.htmlContent}</body></html>`;
183+
}
184+
}
185+
186+
enum CustomViewType {
187+
HtmlContent = 1,
188+
}
189+
190+
namespace NewCustomViewRequest {
191+
export const type =
192+
new RequestType<NewCustomViewRequestArguments, void, void, void>(
193+
'powerShell/newCustomView');
194+
}
195+
196+
interface NewCustomViewRequestArguments {
197+
id: string;
198+
title: string;
199+
viewType: CustomViewType;
200+
}
201+
202+
namespace ShowCustomViewRequest {
203+
export const type =
204+
new RequestType<ShowCustomViewRequestArguments, void, void, void>(
205+
'powerShell/showCustomView');
206+
}
207+
208+
interface ShowCustomViewRequestArguments {
209+
id: string;
210+
viewColumn: vscode.ViewColumn;
211+
}
212+
213+
namespace CloseCustomViewRequest {
214+
export const type =
215+
new RequestType<CloseCustomViewRequestArguments, void, void, void>(
216+
'powerShell/closeCustomView');
217+
}
218+
219+
interface CloseCustomViewRequestArguments {
220+
id: string;
221+
}
222+
223+
namespace SetHtmlContentViewRequest {
224+
export const type =
225+
new RequestType<SetHtmlContentViewRequestArguments, void, void, void>(
226+
'powerShell/setHtmlViewContent');
227+
}
228+
229+
interface SetHtmlContentViewRequestArguments {
230+
id: string;
231+
htmlBodyContent: string;
232+
}
233+
234+
namespace AppendHtmlOutputViewRequest {
235+
export const type =
236+
new RequestType<AppendHtmlOutputViewRequestArguments, void, void, void>(
237+
'powerShell/appendHtmlViewContent');
238+
}
239+
240+
interface AppendHtmlOutputViewRequestArguments {
241+
id: string;
242+
appendedHtmlBodyContent: string;
243+
}

src/main.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { PowerShellLanguageId } from './utils';
1515
import { ConsoleFeature } from './features/Console';
1616
import { ExamplesFeature } from './features/Examples';
1717
import { OpenInISEFeature } from './features/OpenInISE';
18+
import { CustomViewsFeature } from './features/CustomViews';
1819
import { ExpandAliasFeature } from './features/ExpandAlias';
1920
import { ShowHelpFeature } from './features/ShowOnlineHelp';
2021
import { CodeActionsFeature } from './features/CodeActions';
@@ -121,7 +122,8 @@ export function activate(context: vscode.ExtensionContext): void {
121122
new DebugSessionFeature(sessionManager),
122123
new PickPSHostProcessFeature(),
123124
new SpecifyScriptArgsFeature(context),
124-
new HelpCompletionFeature()
125+
new HelpCompletionFeature(),
126+
new CustomViewsFeature()
125127
];
126128

127129
sessionManager.setExtensionFeatures(extensionFeatures);

0 commit comments

Comments
 (0)