"use strict";
|
|
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
});
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const vscode = require("vscode");
|
|
const path = require("path");
|
|
const dispose_1 = require("../util/dispose");
|
|
const nls = require("vscode-nls");
|
|
const topmostLineMonitor_1 = require("../util/topmostLineMonitor");
|
|
const file_1 = require("../util/file");
|
|
const extension_1 = require("../extension");
|
|
const localize = nls.loadMessageBundle();
|
|
class HTMLPreview {
|
|
constructor(webview, resource, locked, _contentProvider, _previewConfigurations, _logger, topmostLineMonitor) {
|
|
this._contentProvider = _contentProvider;
|
|
this._previewConfigurations = _previewConfigurations;
|
|
this._logger = _logger;
|
|
this.line = undefined;
|
|
this.disposables = [];
|
|
this.firstUpdate = true;
|
|
this.forceUpdate = false;
|
|
this.isScrolling = false;
|
|
this._disposed = false;
|
|
this._onDisposeEmitter = new vscode.EventEmitter();
|
|
this.onDispose = this._onDisposeEmitter.event;
|
|
this._onDidChangeViewStateEmitter = new vscode.EventEmitter();
|
|
this.onDidChangeViewState = this._onDidChangeViewStateEmitter.event;
|
|
this._resource = resource;
|
|
this._locked = locked;
|
|
this.editor = webview;
|
|
this.editor.onDidDispose(() => {
|
|
this.dispose();
|
|
}, null, this.disposables);
|
|
this.editor.onDidChangeViewState(e => {
|
|
this._onDidChangeViewStateEmitter.fire(e);
|
|
}, null, this.disposables);
|
|
this.editor.webview.onDidReceiveMessage(e => {
|
|
if (e.source !== this._resource.toString()) {
|
|
return;
|
|
}
|
|
switch (e.type) {
|
|
case 'command':
|
|
vscode.commands.executeCommand(e.body.command, ...e.body.args);
|
|
break;
|
|
case 'revealLine':
|
|
this.onDidScrollPreview(e.body.line);
|
|
break;
|
|
case 'didClick':
|
|
this.onDidClickPreview(e.body.line);
|
|
break;
|
|
}
|
|
}, null, this.disposables);
|
|
vscode.workspace.onDidChangeTextDocument(event => {
|
|
if (this.isPreviewOf(event.document.uri)) {
|
|
this.refresh();
|
|
}
|
|
}, null, this.disposables);
|
|
topmostLineMonitor.onDidChangeTopmostLine(event => {
|
|
if (this.isPreviewOf(event.resource)) {
|
|
this.updateForView(event.resource, event.line);
|
|
}
|
|
}, null, this.disposables);
|
|
vscode.window.onDidChangeTextEditorSelection(event => {
|
|
if (this.isPreviewOf(event.textEditor.document.uri)) {
|
|
this.postMessage({
|
|
type: 'onDidChangeTextEditorSelection',
|
|
line: event.selections[0].active.line,
|
|
source: this.resource.toString()
|
|
});
|
|
}
|
|
}, null, this.disposables);
|
|
vscode.window.onDidChangeActiveTextEditor(editor => {
|
|
if (editor && file_1.isHTMLFile(editor.document) && !this._locked) {
|
|
this.update(editor.document.uri);
|
|
}
|
|
}, null, this.disposables);
|
|
}
|
|
static revive(webview, state, contentProvider, previewConfigurations, logger, topmostLineMonitor) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const resource = vscode.Uri.parse(state.resource);
|
|
const locked = state.locked;
|
|
const line = state.line;
|
|
const preview = new HTMLPreview(webview, resource, locked, contentProvider, previewConfigurations, logger, topmostLineMonitor);
|
|
preview.editor.webview.options = HTMLPreview.getWebviewOptions(resource);
|
|
if (!isNaN(line)) {
|
|
preview.line = line;
|
|
}
|
|
yield preview.doUpdate();
|
|
return preview;
|
|
});
|
|
}
|
|
static create(resource, previewColumn, locked, contentProvider, previewConfigurations, logger, topmostLineMonitor) {
|
|
const webview = vscode.window.createWebviewPanel(HTMLPreview.viewType, HTMLPreview.getPreviewTitle(resource, locked), previewColumn, Object.assign({ enableFindWidget: true }, HTMLPreview.getWebviewOptions(resource)));
|
|
return new HTMLPreview(webview, resource, locked, contentProvider, previewConfigurations, logger, topmostLineMonitor);
|
|
}
|
|
get resource() {
|
|
return this._resource;
|
|
}
|
|
get state() {
|
|
return {
|
|
resource: this.resource.toString(),
|
|
locked: this._locked,
|
|
line: this.line
|
|
};
|
|
}
|
|
dispose() {
|
|
if (this._disposed) {
|
|
return;
|
|
}
|
|
this._disposed = true;
|
|
this._onDisposeEmitter.fire();
|
|
this._onDisposeEmitter.dispose();
|
|
this._onDidChangeViewStateEmitter.dispose();
|
|
this.editor.dispose();
|
|
dispose_1.disposeAll(this.disposables);
|
|
}
|
|
update(resource) {
|
|
const editor = vscode.window.activeTextEditor;
|
|
if (editor && editor.document.uri.fsPath === resource.fsPath) {
|
|
this.line = topmostLineMonitor_1.getVisibleLine(editor);
|
|
}
|
|
// If we have changed resources, cancel any pending updates
|
|
const isResourceChange = resource.fsPath !== this._resource.fsPath;
|
|
if (isResourceChange) {
|
|
clearTimeout(this.throttleTimer);
|
|
this.throttleTimer = undefined;
|
|
}
|
|
this._resource = resource;
|
|
// Schedule update if none is pending
|
|
if (!this.throttleTimer) {
|
|
if (isResourceChange || this.firstUpdate) {
|
|
this.doUpdate();
|
|
}
|
|
else {
|
|
this.throttleTimer = setTimeout(() => this.doUpdate(), 300);
|
|
}
|
|
}
|
|
this.firstUpdate = false;
|
|
}
|
|
refresh() {
|
|
this.forceUpdate = true;
|
|
this.update(this._resource);
|
|
}
|
|
updateConfiguration() {
|
|
if (this._previewConfigurations.hasConfigurationChanged(this._resource)) {
|
|
this.refresh();
|
|
}
|
|
}
|
|
get position() {
|
|
return this.editor.viewColumn;
|
|
}
|
|
matchesResource(otherResource, otherPosition, otherLocked) {
|
|
if (this.position !== otherPosition) {
|
|
return false;
|
|
}
|
|
if (this._locked) {
|
|
return otherLocked && this.isPreviewOf(otherResource);
|
|
}
|
|
else {
|
|
return !otherLocked;
|
|
}
|
|
}
|
|
matches(otherPreview) {
|
|
return this.matchesResource(otherPreview._resource, otherPreview.position, otherPreview._locked);
|
|
}
|
|
reveal(viewColumn) {
|
|
this.editor.reveal(viewColumn);
|
|
}
|
|
toggleLock() {
|
|
this._locked = !this._locked;
|
|
this.editor.title = HTMLPreview.getPreviewTitle(this._resource, this._locked);
|
|
}
|
|
get iconPath() {
|
|
const root = path.join(extension_1.getExtensionPath(), 'media');
|
|
return {
|
|
light: vscode.Uri.file(path.join(root, 'Preview.svg')),
|
|
dark: vscode.Uri.file(path.join(root, 'Preview_inverse.svg'))
|
|
};
|
|
}
|
|
isPreviewOf(resource) {
|
|
return this._resource.fsPath === resource.fsPath;
|
|
}
|
|
static getPreviewTitle(resource, locked) {
|
|
return locked
|
|
? localize('lockedPreviewTitle', '[Preview] {0}', path.basename(resource.fsPath))
|
|
: localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath));
|
|
}
|
|
updateForView(resource, topLine) {
|
|
if (!this.isPreviewOf(resource)) {
|
|
return;
|
|
}
|
|
if (this.isScrolling) {
|
|
this.isScrolling = false;
|
|
return;
|
|
}
|
|
if (typeof topLine === 'number') {
|
|
this._logger.log('updateForView', { htmlFile: resource });
|
|
this.line = topLine;
|
|
this.postMessage({
|
|
type: 'updateView',
|
|
line: topLine,
|
|
source: resource.toString()
|
|
});
|
|
}
|
|
}
|
|
postMessage(msg) {
|
|
if (!this._disposed) {
|
|
this.editor.webview.postMessage(msg);
|
|
}
|
|
}
|
|
doUpdate() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const resource = this._resource;
|
|
clearTimeout(this.throttleTimer);
|
|
this.throttleTimer = undefined;
|
|
const document = yield vscode.workspace.openTextDocument(resource);
|
|
if (!this.forceUpdate && this.currentVersion && this.currentVersion.resource.fsPath === resource.fsPath && this.currentVersion.version === document.version) {
|
|
if (this.line) {
|
|
this.updateForView(resource, this.line);
|
|
}
|
|
return;
|
|
}
|
|
this.forceUpdate = false;
|
|
this.currentVersion = { resource, version: document.version };
|
|
const content = this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state);
|
|
if (this._resource === resource) {
|
|
this.editor.title = HTMLPreview.getPreviewTitle(this._resource, this._locked);
|
|
this.editor.iconPath = this.iconPath;
|
|
this.editor.webview.options = HTMLPreview.getWebviewOptions(resource);
|
|
this.editor.webview.html = content;
|
|
}
|
|
});
|
|
}
|
|
static getWebviewOptions(resource) {
|
|
return {
|
|
enableScripts: true,
|
|
enableCommandUris: true,
|
|
localResourceRoots: HTMLPreview.getLocalResourceRoots(resource)
|
|
};
|
|
}
|
|
static getLocalResourceRoots(resource) {
|
|
const baseRoots = [vscode.Uri.file(extension_1.getExtensionPath() + "/media")];
|
|
const folder = vscode.workspace.getWorkspaceFolder(resource);
|
|
if (folder) {
|
|
return baseRoots.concat(folder.uri);
|
|
}
|
|
if (!resource.scheme || resource.scheme === 'file') {
|
|
return baseRoots.concat(vscode.Uri.file(path.dirname(resource.fsPath)));
|
|
}
|
|
return baseRoots;
|
|
}
|
|
onDidScrollPreview(line) {
|
|
this.line = line;
|
|
for (const editor of vscode.window.visibleTextEditors) {
|
|
if (!this.isPreviewOf(editor.document.uri)) {
|
|
continue;
|
|
}
|
|
this.isScrolling = true;
|
|
const sourceLine = Math.floor(line);
|
|
const fraction = line - sourceLine;
|
|
const text = editor.document.lineAt(sourceLine).text;
|
|
const start = Math.floor(fraction * text.length);
|
|
editor.revealRange(new vscode.Range(sourceLine, start, sourceLine + 1, 0), vscode.TextEditorRevealType.AtTop);
|
|
}
|
|
}
|
|
onDidClickPreview(line) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
for (const visibleEditor of vscode.window.visibleTextEditors) {
|
|
if (this.isPreviewOf(visibleEditor.document.uri)) {
|
|
const editor = yield vscode.window.showTextDocument(visibleEditor.document, visibleEditor.viewColumn);
|
|
const position = new vscode.Position(line, 0);
|
|
editor.selection = new vscode.Selection(position, position);
|
|
return;
|
|
}
|
|
}
|
|
vscode.workspace.openTextDocument(this._resource).then(vscode.window.showTextDocument);
|
|
});
|
|
}
|
|
}
|
|
HTMLPreview.viewType = 'html.preview';
|
|
exports.HTMLPreview = HTMLPreview;
|