Skip to content
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

Webview API prototype 2 #44165

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions extensions/markdown/src/commands/refreshPreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { Command } from '../commandManager';
import { isMarkdownFile, getMarkdownUri, MarkdownPreviewWebviewManager } from '../features/previewContentProvider';
import { MarkdownPreviewWebviewManager } from '../features/previewContentProvider';

export class RefreshPreviewCommand implements Command {
public readonly id = 'markdown.refreshPreview';
Expand All @@ -14,14 +13,7 @@ export class RefreshPreviewCommand implements Command {
private readonly webviewManager: MarkdownPreviewWebviewManager
) { }

public execute(resource: string | undefined) {
if (resource) {
const source = vscode.Uri.parse(resource);
this.webviewManager.update(source);
} else if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) {
this.webviewManager.update(getMarkdownUri(vscode.window.activeTextEditor.document.uri));
} else {
this.webviewManager.updateAll();
}
public execute() {
this.webviewManager.refresh();
}
}
3 changes: 2 additions & 1 deletion extensions/markdown/src/commands/showPreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ function showPreview(
return;
}

const view = webviewManager.create(
const view = webviewManager.preview(
resource,
(vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) || vscode.ViewColumn.One,
getViewColumn(sideBySide) || vscode.ViewColumn.Active);

telemetryReporter.sendTelemetryEvent('openPreview', {
Expand Down
15 changes: 1 addition & 14 deletions extensions/markdown/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { loadDefaultTelemetryReporter } from './telemetryReporter';
import { loadMarkdownExtensions } from './markdownExtensions';
import LinkProvider from './features/documentLinkProvider';
import MDDocumentSymbolProvider from './features/documentSymbolProvider';
import { MarkdownContentProvider, getMarkdownUri, isMarkdownFile, MarkdownPreviewWebviewManager } from './features/previewContentProvider';
import { MarkdownContentProvider, MarkdownPreviewWebviewManager } from './features/previewContentProvider';


export function activate(context: vscode.ExtensionContext) {
Expand Down Expand Up @@ -55,17 +55,4 @@ export function activate(context: vscode.ExtensionContext) {
logger.updateConfiguration();
webviewManager.updateConfiguration();
}));

context.subscriptions.push(vscode.window.onDidChangeTextEditorSelection(event => {
if (isMarkdownFile(event.textEditor.document)) {
const markdownFile = getMarkdownUri(event.textEditor.document.uri);
logger.log('updatePreviewForSelection', { markdownFile: markdownFile.toString() });

vscode.commands.executeCommand('_workbench.htmlPreview.postMessage',
markdownFile,
{
line: event.selections[0].active.line
});
}
}));
}
128 changes: 72 additions & 56 deletions extensions/markdown/src/features/previewContentProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,7 @@ const previewStrings = {
};

export function isMarkdownFile(document: vscode.TextDocument) {
return document.languageId === 'markdown'
&& document.uri.scheme !== MarkdownContentProvider.scheme; // prevent processing of own documents
}

export function getMarkdownUri(uri: vscode.Uri) {
if (uri.scheme === MarkdownContentProvider.scheme) {
return uri;
}

return uri.with({
scheme: MarkdownContentProvider.scheme,
path: uri.path + '.rendered',
query: uri.toString()
});
return document.languageId === 'markdown';
}

export class MarkdownPreviewConfig {
Expand Down Expand Up @@ -137,8 +124,6 @@ export class PreviewConfigManager {
}

export class MarkdownContentProvider {
public static readonly scheme = 'markdown';

private extraStyles: Array<vscode.Uri> = [];
private extraScripts: Array<vscode.Uri> = [];

Expand Down Expand Up @@ -296,21 +281,42 @@ export class MarkdownContentProvider {
}
}

interface MarkdownPreview {
resource: vscode.Uri;
webview: vscode.Webview;
ofColumn: vscode.ViewColumn;
}

export class MarkdownPreviewWebviewManager {
private readonly webviews = new Map<string, vscode.Webview>();
private static webviewId = 'vscode-markdown-preview';

private previews: MarkdownPreview[] = [];
private readonly previewConfigurations = new PreviewConfigManager();

private readonly disposables: vscode.Disposable[] = [];

public constructor(
private readonly contentProvider: MarkdownContentProvider
) {
vscode.workspace.onDidSaveTextDocument(document => {
this.update(document.uri);
vscode.workspace.onDidChangeTextDocument(event => {
this.update(event.document, undefined);
}, null, this.disposables);

vscode.workspace.onDidChangeTextDocument(event => {
this.update(event.document.uri);
vscode.window.onDidChangeActiveEditor(editor => {
vscode.commands.executeCommand('setContext', 'markdownPreview', editor && editor.editorType === 'webview' && editor.id === MarkdownPreviewWebviewManager.webviewId);

if (editor && editor.editorType === 'texteditor') {
this.update(editor.document, editor.viewColumn);
}
}, null, this.disposables);

vscode.window.onDidCloseWebview(webview => {
if (webview.id === MarkdownPreviewWebviewManager.webviewId) {
const existing = this.previews.findIndex(preview => preview.webview === webview);
if (existing >= 0) {
this.previews.splice(existing, 1);
}
}
}, null, this.disposables);
}

Expand All @@ -321,60 +327,66 @@ export class MarkdownPreviewWebviewManager {
item.dispose();
}
}
this.webviews.clear();
this.previews = [];
}

public update(uri: vscode.Uri) {
const webview = this.webviews.get(uri.fsPath);
if (webview) {
this.contentProvider.provideTextDocumentContent(uri, this.previewConfigurations).then(x => webview.html = x);
private update(document: vscode.TextDocument, viewColumn: vscode.ViewColumn | undefined) {
if (!isMarkdownFile(document)) {
return;
}

for (const preview of this.previews) {
if (preview.resource.fsPath === document.uri.fsPath || viewColumn && preview.ofColumn === viewColumn) {
preview.webview.title = this.getPreviewTitle(document.uri);
preview.resource = document.uri;
this.contentProvider.provideTextDocumentContent(document.uri, this.previewConfigurations).then(x => preview.webview.html = x);
}
}
}

public updateAll() {
for (const resource of this.webviews.keys()) {
const sourceUri = vscode.Uri.parse(resource);
this.update(sourceUri);
public refresh() {
for (const preview of this.previews) {
this.contentProvider.provideTextDocumentContent(preview.resource, this.previewConfigurations).then(x => preview.webview.html = x);
}
}

public updateConfiguration() {
for (const resource of this.webviews.keys()) {
const sourceUri = vscode.Uri.parse(resource);
if (this.previewConfigurations.shouldUpdateConfiguration(sourceUri)) {
this.update(sourceUri);
for (const preview of this.previews) {
if (this.previewConfigurations.shouldUpdateConfiguration(preview.resource)) {
this.contentProvider.provideTextDocumentContent(preview.resource, this.previewConfigurations).then(x => preview.webview.html = x);
}
}
}

public create(
public preview(
resource: vscode.Uri,
viewColumn: vscode.ViewColumn
resourceColumn: vscode.ViewColumn,
previewColumn: vscode.ViewColumn
) {
const view = vscode.window.createWebview(
localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)),
viewColumn,
{
enableScripts: true,
localResourceRoots: this.getLocalResourceRoots(resource)
});
const webview: vscode.Webview = vscode.window.getOrCreateWebview(
MarkdownPreviewWebviewManager.webviewId,
previewColumn);

this.contentProvider.provideTextDocumentContent(resource, this.previewConfigurations).then(x => view.html = x);

view.onMessage(e => {
vscode.commands.executeCommand(e.command, ...e.args);
});
webview.options = {
enableScripts: true,
localResourceRoots: this.getLocalResourceRoots(resource)
};
webview.title = this.getPreviewTitle(resource);

const existing = this.previews.find(preview => preview.webview.viewColumn === webview.viewColumn);
if (existing) {
existing.resource = resource;
} else {
webview.onMessage(e => {
vscode.commands.executeCommand(e.command, ...e.args);
});

view.onBecameActive(() => {
vscode.commands.executeCommand('setContext', 'markdownPreview', true);
});
this.previews.push({ webview, resource, ofColumn: resourceColumn });
}

view.onBecameInactive(() => {
vscode.commands.executeCommand('setContext', 'markdownPreview', false);
});
this.contentProvider.provideTextDocumentContent(resource, this.previewConfigurations).then(x => webview.html = x);

this.webviews.set(resource.fsPath, view);
return view;
return webview;
}

private getLocalResourceRoots(
Expand All @@ -391,4 +403,8 @@ export class MarkdownPreviewWebviewManager {

return [];
}

private getPreviewTitle(resource: vscode.Uri): string {
return localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath));
}
}
11 changes: 4 additions & 7 deletions extensions/markdown/src/security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import * as vscode from 'vscode';

import { getMarkdownUri, MarkdownPreviewWebviewManager } from './features/previewContentProvider';
import { MarkdownPreviewWebviewManager } from './features/previewContentProvider';

import * as nls from 'vscode-nls';

Expand Down Expand Up @@ -143,15 +143,12 @@ export class PreviewSecuritySelector {
return;
}

const sourceUri = getMarkdownUri(resource);
if (selection.type === 'toggle') {
this.cspArbiter.setShouldDisableSecurityWarning(!this.cspArbiter.shouldDisableSecurityWarnings());
this.webviewManager.update(sourceUri);
return;
} else {
await this.cspArbiter.setSecurityLevelForResource(resource, selection.type);
}

await this.cspArbiter.setSecurityLevelForResource(resource, selection.type);

this.webviewManager.update(sourceUri);
this.webviewManager.refresh();
}
}
4 changes: 4 additions & 0 deletions src/vs/vscode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,10 @@ declare module 'vscode' {
* Represents an editor that is attached to a [document](#TextDocument).
*/
export interface TextEditor {
/**
* Type identifying the editor as a text editor.
*/
readonly editorType: 'texteditor';

/**
* The document associated with this text editor. The document will be the same for the entire lifetime of this text editor.
Expand Down
37 changes: 23 additions & 14 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,17 @@ declare module 'vscode' {
*/
export interface Webview {
/**
* Title of the webview.
* Type identifying the editor as a webview editor.
*/
readonly editorType: 'webview';

/**
* Unique internal identifer of the webview.
*/
readonly id: string;

/**
* Title of the webview shown in UI.
*/
title: string;

Expand All @@ -463,16 +473,6 @@ declare module 'vscode' {
*/
readonly onMessage: Event<any>;

/**
* Fired when the webview becomes the active editor.
*/
readonly onBecameActive: Event<void>;

/**
* Fired when the webview stops being the active editor
*/
readonly onBecameInactive: Event<void>;

/**
* Post a message to the webview content.
*
Expand All @@ -492,10 +492,19 @@ declare module 'vscode' {
/**
* Create and show a new webview.
*
* @param title Title of the webview.
* @param id Unique identifier for the webview.
* @param column Editor column to show the new webview in.
* @param options Webview content options.
*/
export function createWebview(title: string, column: ViewColumn, options: WebviewOptions): Webview;
export function getOrCreateWebview(id: string, column: ViewColumn): Webview;

/**
* Event fired when the active editor changes.
*/
export const onDidChangeActiveEditor: Event<TextEditor | Webview | undefined>;

/**
* Event fired when a webview is closed.
*/
export const onDidCloseWebview: Event<Webview>;
}
}
Loading