eae5d8c807
These conflicts will be resolved in the following commits. We do it this way so that PR review is possible.
243 lines
7.1 KiB
TypeScript
243 lines
7.1 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as vscode from 'vscode';
|
|
import { Logger } from '../logger';
|
|
import { MarkdownEngine } from '../markdownEngine';
|
|
import { MarkdownContributionProvider } from '../markdownExtensions';
|
|
import { Disposable, disposeAll } from '../util/dispose';
|
|
import { TopmostLineMonitor } from '../util/topmostLineMonitor';
|
|
import { DynamicMarkdownPreview, ManagedMarkdownPreview, StartingScrollFragment, StaticMarkdownPreview } from './preview';
|
|
import { MarkdownPreviewConfigurationManager } from './previewConfig';
|
|
import { MarkdownContentProvider } from './previewContentProvider';
|
|
|
|
export interface DynamicPreviewSettings {
|
|
readonly resourceColumn: vscode.ViewColumn;
|
|
readonly previewColumn: vscode.ViewColumn;
|
|
readonly locked: boolean;
|
|
}
|
|
|
|
class PreviewStore<T extends ManagedMarkdownPreview> extends Disposable {
|
|
|
|
private readonly _previews = new Set<T>();
|
|
|
|
public dispose(): void {
|
|
super.dispose();
|
|
for (const preview of this._previews) {
|
|
preview.dispose();
|
|
}
|
|
this._previews.clear();
|
|
}
|
|
|
|
[Symbol.iterator](): Iterator<T> {
|
|
return this._previews[Symbol.iterator]();
|
|
}
|
|
|
|
public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): T | undefined {
|
|
for (const preview of this._previews) {
|
|
if (preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked)) {
|
|
return preview;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
public add(preview: T) {
|
|
this._previews.add(preview);
|
|
}
|
|
|
|
public delete(preview: T) {
|
|
this._previews.delete(preview);
|
|
}
|
|
}
|
|
|
|
export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.CustomTextEditorProvider {
|
|
private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus';
|
|
|
|
private readonly _topmostLineMonitor = new TopmostLineMonitor();
|
|
private readonly _previewConfigurations = new MarkdownPreviewConfigurationManager();
|
|
|
|
private readonly _dynamicPreviews = this._register(new PreviewStore<DynamicMarkdownPreview>());
|
|
private readonly _staticPreviews = this._register(new PreviewStore<StaticMarkdownPreview>());
|
|
|
|
private _activePreview: ManagedMarkdownPreview | undefined = undefined;
|
|
|
|
private readonly customEditorViewType = 'vscode.markdown.preview.editor';
|
|
|
|
public constructor(
|
|
private readonly _contentProvider: MarkdownContentProvider,
|
|
private readonly _logger: Logger,
|
|
private readonly _contributions: MarkdownContributionProvider,
|
|
private readonly _engine: MarkdownEngine,
|
|
) {
|
|
super();
|
|
this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this));
|
|
this._register(vscode.window.registerCustomEditorProvider(this.customEditorViewType, this));
|
|
}
|
|
|
|
public refresh() {
|
|
for (const preview of this._dynamicPreviews) {
|
|
preview.refresh();
|
|
}
|
|
for (const preview of this._staticPreviews) {
|
|
preview.refresh();
|
|
}
|
|
}
|
|
|
|
public updateConfiguration() {
|
|
for (const preview of this._dynamicPreviews) {
|
|
preview.updateConfiguration();
|
|
}
|
|
for (const preview of this._staticPreviews) {
|
|
preview.updateConfiguration();
|
|
}
|
|
}
|
|
|
|
public openDynamicPreview(
|
|
resource: vscode.Uri,
|
|
settings: DynamicPreviewSettings
|
|
): void {
|
|
let preview = this._dynamicPreviews.get(resource, settings);
|
|
if (preview) {
|
|
preview.reveal(settings.previewColumn);
|
|
} else {
|
|
preview = this.createNewDynamicPreview(resource, settings);
|
|
}
|
|
|
|
preview.update(
|
|
resource,
|
|
resource.fragment ? new StartingScrollFragment(resource.fragment) : undefined
|
|
);
|
|
}
|
|
|
|
public get activePreviewResource() {
|
|
return this._activePreview?.resource;
|
|
}
|
|
|
|
public get activePreviewResourceColumn() {
|
|
return this._activePreview?.resourceColumn;
|
|
}
|
|
|
|
public toggleLock() {
|
|
const preview = this._activePreview;
|
|
if (preview instanceof DynamicMarkdownPreview) {
|
|
preview.toggleLock();
|
|
|
|
// Close any previews that are now redundant, such as having two dynamic previews in the same editor group
|
|
for (const otherPreview of this._dynamicPreviews) {
|
|
if (otherPreview !== preview && preview.matches(otherPreview)) {
|
|
otherPreview.dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public async deserializeWebviewPanel(
|
|
webview: vscode.WebviewPanel,
|
|
state: any
|
|
): Promise<void> {
|
|
const resource = vscode.Uri.parse(state.resource);
|
|
const locked = state.locked;
|
|
const line = state.line;
|
|
const resourceColumn = state.resourceColumn;
|
|
|
|
const preview = await DynamicMarkdownPreview.revive(
|
|
{ resource, locked, line, resourceColumn },
|
|
webview,
|
|
this._contentProvider,
|
|
this._previewConfigurations,
|
|
this._logger,
|
|
this._topmostLineMonitor,
|
|
this._contributions,
|
|
this._engine);
|
|
|
|
this.registerDynamicPreview(preview);
|
|
}
|
|
|
|
public async resolveCustomTextEditor(
|
|
document: vscode.TextDocument,
|
|
webview: vscode.WebviewPanel
|
|
): Promise<void> {
|
|
const preview = StaticMarkdownPreview.revive(
|
|
document.uri,
|
|
webview,
|
|
this._contentProvider,
|
|
this._previewConfigurations,
|
|
this._logger,
|
|
this._contributions,
|
|
this._engine);
|
|
this.registerStaticPreview(preview);
|
|
}
|
|
|
|
private createNewDynamicPreview(
|
|
resource: vscode.Uri,
|
|
previewSettings: DynamicPreviewSettings
|
|
): DynamicMarkdownPreview {
|
|
const preview = DynamicMarkdownPreview.create(
|
|
{
|
|
resource,
|
|
resourceColumn: previewSettings.resourceColumn,
|
|
locked: previewSettings.locked,
|
|
},
|
|
previewSettings.previewColumn,
|
|
this._contentProvider,
|
|
this._previewConfigurations,
|
|
this._logger,
|
|
this._topmostLineMonitor,
|
|
this._contributions,
|
|
this._engine);
|
|
|
|
this.setPreviewActiveContext(true);
|
|
this._activePreview = preview;
|
|
return this.registerDynamicPreview(preview);
|
|
}
|
|
|
|
private registerDynamicPreview(preview: DynamicMarkdownPreview): DynamicMarkdownPreview {
|
|
this._dynamicPreviews.add(preview);
|
|
|
|
preview.onDispose(() => {
|
|
this._dynamicPreviews.delete(preview);
|
|
});
|
|
|
|
this.trackActive(preview);
|
|
|
|
preview.onDidChangeViewState(() => {
|
|
// Remove other dynamic previews in our column
|
|
disposeAll(Array.from(this._dynamicPreviews).filter(otherPreview => preview !== otherPreview && preview.matches(otherPreview)));
|
|
});
|
|
return preview;
|
|
}
|
|
|
|
private registerStaticPreview(preview: StaticMarkdownPreview): StaticMarkdownPreview {
|
|
this._staticPreviews.add(preview);
|
|
|
|
preview.onDispose(() => {
|
|
this._staticPreviews.delete(preview);
|
|
});
|
|
|
|
this.trackActive(preview);
|
|
return preview;
|
|
}
|
|
|
|
private trackActive(preview: ManagedMarkdownPreview): void {
|
|
preview.onDidChangeViewState(({ webviewPanel }) => {
|
|
this.setPreviewActiveContext(webviewPanel.active);
|
|
this._activePreview = webviewPanel.active ? preview : undefined;
|
|
});
|
|
|
|
preview.onDispose(() => {
|
|
if (this._activePreview === preview) {
|
|
this.setPreviewActiveContext(false);
|
|
this._activePreview = undefined;
|
|
}
|
|
});
|
|
}
|
|
|
|
private setPreviewActiveContext(value: boolean) {
|
|
vscode.commands.executeCommand('setContext', MarkdownPreviewManager.markdownPreviewActiveContextKey, value);
|
|
}
|
|
}
|
|
|