Adopt prefix private with _ in markdown extension (#165088)

This commit is contained in:
Matt Bierner 2022-10-31 14:22:39 -07:00 committed by GitHub
parent b97827dacf
commit 33867c55f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 374 additions and 355 deletions

View file

@ -0,0 +1,19 @@
{
"rules": {
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "default",
"modifiers": ["private"],
"format": null,
"leadingUnderscore": "require"
},
{
"selector": "default",
"modifiers": ["public"],
"format": null,
"leadingUnderscore": "forbid"
}
]
}
}

View file

@ -18,12 +18,12 @@ type DirWatcherEntry = {
export class FileWatcherManager { export class FileWatcherManager {
private readonly fileWatchers = new Map<number, { private readonly _fileWatchers = new Map<number, {
readonly watcher: vscode.FileSystemWatcher; readonly watcher: vscode.FileSystemWatcher;
readonly dirWatchers: DirWatcherEntry[]; readonly dirWatchers: DirWatcherEntry[];
}>(); }>();
private readonly dirWatchers = new ResourceMap<{ private readonly _dirWatchers = new ResourceMap<{
readonly watcher: vscode.FileSystemWatcher; readonly watcher: vscode.FileSystemWatcher;
refCount: number; refCount: number;
}>(); }>();
@ -31,7 +31,7 @@ export class FileWatcherManager {
create(id: number, uri: vscode.Uri, watchParentDirs: boolean, listeners: { create?: () => void; change?: () => void; delete?: () => void }): void { create(id: number, uri: vscode.Uri, watchParentDirs: boolean, listeners: { create?: () => void; change?: () => void; delete?: () => void }): void {
const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*'), !listeners.create, !listeners.change, !listeners.delete); const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*'), !listeners.create, !listeners.change, !listeners.delete);
const parentDirWatchers: DirWatcherEntry[] = []; const parentDirWatchers: DirWatcherEntry[] = [];
this.fileWatchers.set(id, { watcher, dirWatchers: parentDirWatchers }); this._fileWatchers.set(id, { watcher, dirWatchers: parentDirWatchers });
if (listeners.create) { watcher.onDidCreate(listeners.create); } if (listeners.create) { watcher.onDidCreate(listeners.create); }
if (listeners.change) { watcher.onDidChange(listeners.change); } if (listeners.change) { watcher.onDidChange(listeners.change); }
@ -42,12 +42,12 @@ export class FileWatcherManager {
for (let dirUri = Utils.dirname(uri); dirUri.path.length > 1; dirUri = Utils.dirname(dirUri)) { for (let dirUri = Utils.dirname(uri); dirUri.path.length > 1; dirUri = Utils.dirname(dirUri)) {
const dirWatcher: DirWatcherEntry = { uri: dirUri, listeners: [] }; const dirWatcher: DirWatcherEntry = { uri: dirUri, listeners: [] };
let parentDirWatcher = this.dirWatchers.get(dirUri); let parentDirWatcher = this._dirWatchers.get(dirUri);
if (!parentDirWatcher) { if (!parentDirWatcher) {
const glob = new vscode.RelativePattern(Utils.dirname(dirUri), Utils.basename(dirUri)); const glob = new vscode.RelativePattern(Utils.dirname(dirUri), Utils.basename(dirUri));
const parentWatcher = vscode.workspace.createFileSystemWatcher(glob, !listeners.create, true, !listeners.delete); const parentWatcher = vscode.workspace.createFileSystemWatcher(glob, !listeners.create, true, !listeners.delete);
parentDirWatcher = { refCount: 0, watcher: parentWatcher }; parentDirWatcher = { refCount: 0, watcher: parentWatcher };
this.dirWatchers.set(dirUri, parentDirWatcher); this._dirWatchers.set(dirUri, parentDirWatcher);
} }
parentDirWatcher.refCount++; parentDirWatcher.refCount++;
@ -77,16 +77,16 @@ export class FileWatcherManager {
} }
delete(id: number): void { delete(id: number): void {
const entry = this.fileWatchers.get(id); const entry = this._fileWatchers.get(id);
if (entry) { if (entry) {
for (const dirWatcher of entry.dirWatchers) { for (const dirWatcher of entry.dirWatchers) {
disposeAll(dirWatcher.listeners); disposeAll(dirWatcher.listeners);
const dirWatcherEntry = this.dirWatchers.get(dirWatcher.uri); const dirWatcherEntry = this._dirWatchers.get(dirWatcher.uri);
if (dirWatcherEntry) { if (dirWatcherEntry) {
if (--dirWatcherEntry.refCount <= 0) { if (--dirWatcherEntry.refCount <= 0) {
dirWatcherEntry.watcher.dispose(); dirWatcherEntry.watcher.dispose();
this.dirWatchers.delete(dirWatcher.uri); this._dirWatchers.delete(dirWatcher.uri);
} }
} }
} }
@ -94,6 +94,6 @@ export class FileWatcherManager {
entry.watcher.dispose(); entry.watcher.dispose();
} }
this.fileWatchers.delete(id); this._fileWatchers.delete(id);
} }
} }

View file

@ -10,11 +10,11 @@ export class InMemoryDocument implements ITextDocument {
constructor( constructor(
public readonly uri: vscode.Uri, public readonly uri: vscode.Uri,
private readonly contents: string, private readonly _contents: string,
public readonly version = 0, public readonly version = 0,
) { } ) { }
getText(): string { getText(): string {
return this.contents; return this._contents;
} }
} }

View file

@ -21,7 +21,7 @@ export class VsCodeMdWorkspace extends Disposable {
private readonly _documentCache = new ResourceMap<ITextDocument>(); private readonly _documentCache = new ResourceMap<ITextDocument>();
private readonly utf8Decoder = new TextDecoder('utf-8'); private readonly _utf8Decoder = new TextDecoder('utf-8');
constructor() { constructor() {
super(); super();
@ -45,7 +45,7 @@ export class VsCodeMdWorkspace extends Disposable {
})); }));
} }
private isRelevantMarkdownDocument(doc: vscode.TextDocument) { private _isRelevantMarkdownDocument(doc: vscode.TextDocument) {
return isMarkdownFile(doc) && doc.uri.scheme !== 'vscode-bulkeditpreview'; return isMarkdownFile(doc) && doc.uri.scheme !== 'vscode-bulkeditpreview';
} }
@ -55,7 +55,7 @@ export class VsCodeMdWorkspace extends Disposable {
return existing; return existing;
} }
const matchingDocument = vscode.workspace.textDocuments.find((doc) => this.isRelevantMarkdownDocument(doc) && doc.uri.toString() === resource.toString()); const matchingDocument = vscode.workspace.textDocuments.find((doc) => this._isRelevantMarkdownDocument(doc) && doc.uri.toString() === resource.toString());
if (matchingDocument) { if (matchingDocument) {
this._documentCache.set(resource, matchingDocument); this._documentCache.set(resource, matchingDocument);
return matchingDocument; return matchingDocument;
@ -69,7 +69,7 @@ export class VsCodeMdWorkspace extends Disposable {
const bytes = await vscode.workspace.fs.readFile(resource); const bytes = await vscode.workspace.fs.readFile(resource);
// We assume that markdown is in UTF-8 // We assume that markdown is in UTF-8
const text = this.utf8Decoder.decode(bytes); const text = this._utf8Decoder.decode(bytes);
const doc = new InMemoryDocument(resource, text, 0); const doc = new InMemoryDocument(resource, text, 0);
this._documentCache.set(resource, doc); this._documentCache.set(resource, doc);
return doc; return doc;

View file

@ -12,27 +12,27 @@ export interface Command {
} }
export class CommandManager { export class CommandManager {
private readonly commands = new Map<string, vscode.Disposable>(); private readonly _commands = new Map<string, vscode.Disposable>();
public dispose() { public dispose() {
for (const registration of this.commands.values()) { for (const registration of this._commands.values()) {
registration.dispose(); registration.dispose();
} }
this.commands.clear(); this._commands.clear();
} }
public register<T extends Command>(command: T): vscode.Disposable { public register<T extends Command>(command: T): vscode.Disposable {
this.registerCommand(command.id, command.execute, command); this._registerCommand(command.id, command.execute, command);
return new vscode.Disposable(() => { return new vscode.Disposable(() => {
this.commands.delete(command.id); this._commands.delete(command.id);
}); });
} }
private registerCommand(id: string, impl: (...args: any[]) => void, thisArg?: any) { private _registerCommand(id: string, impl: (...args: any[]) => void, thisArg?: any) {
if (this.commands.has(id)) { if (this._commands.has(id)) {
return; return;
} }
this.commands.set(id, vscode.commands.registerCommand(id, impl, thisArg)); this._commands.set(id, vscode.commands.registerCommand(id, impl, thisArg));
} }
} }

View file

@ -11,12 +11,12 @@ export class RefreshPreviewCommand implements Command {
public readonly id = 'markdown.preview.refresh'; public readonly id = 'markdown.preview.refresh';
public constructor( public constructor(
private readonly webviewManager: MarkdownPreviewManager, private readonly _webviewManager: MarkdownPreviewManager,
private readonly engine: MarkdownItEngine private readonly _engine: MarkdownItEngine
) { } ) { }
public execute() { public execute() {
this.engine.cleanCache(); this._engine.cleanCache();
this.webviewManager.refresh(); this._webviewManager.refresh();
} }
} }

View file

@ -11,13 +11,13 @@ export class ReloadPlugins implements Command {
public readonly id = 'markdown.api.reloadPlugins'; public readonly id = 'markdown.api.reloadPlugins';
public constructor( public constructor(
private readonly webviewManager: MarkdownPreviewManager, private readonly _webviewManager: MarkdownPreviewManager,
private readonly engine: MarkdownItEngine, private readonly _engine: MarkdownItEngine,
) { } ) { }
public execute(): void { public execute(): void {
this.engine.reloadPlugins(); this._engine.reloadPlugins();
this.engine.cleanCache(); this._engine.cleanCache();
this.webviewManager.refresh(); this._webviewManager.refresh();
} }
} }

View file

@ -11,10 +11,10 @@ export class RenderDocument implements Command {
public readonly id = 'markdown.api.render'; public readonly id = 'markdown.api.render';
public constructor( public constructor(
private readonly engine: MarkdownItEngine private readonly _engine: MarkdownItEngine
) { } ) { }
public async execute(document: ITextDocument | string): Promise<string> { public async execute(document: ITextDocument | string): Promise<string> {
return (await (this.engine.render(document))).html; return (await (this._engine.render(document))).html;
} }
} }

View file

@ -54,13 +54,13 @@ export class ShowPreviewCommand implements Command {
public readonly id = 'markdown.showPreview'; public readonly id = 'markdown.showPreview';
public constructor( public constructor(
private readonly webviewManager: MarkdownPreviewManager, private readonly _webviewManager: MarkdownPreviewManager,
private readonly telemetryReporter: TelemetryReporter private readonly _telemetryReporter: TelemetryReporter
) { } ) { }
public execute(mainUri?: vscode.Uri, allUris?: vscode.Uri[], previewSettings?: DynamicPreviewSettings) { public execute(mainUri?: vscode.Uri, allUris?: vscode.Uri[], previewSettings?: DynamicPreviewSettings) {
for (const uri of Array.isArray(allUris) ? allUris : [mainUri]) { for (const uri of Array.isArray(allUris) ? allUris : [mainUri]) {
showPreview(this.webviewManager, this.telemetryReporter, uri, { showPreview(this._webviewManager, this._telemetryReporter, uri, {
sideBySide: false, sideBySide: false,
locked: previewSettings && previewSettings.locked locked: previewSettings && previewSettings.locked
}); });
@ -72,12 +72,12 @@ export class ShowPreviewToSideCommand implements Command {
public readonly id = 'markdown.showPreviewToSide'; public readonly id = 'markdown.showPreviewToSide';
public constructor( public constructor(
private readonly webviewManager: MarkdownPreviewManager, private readonly _webviewManager: MarkdownPreviewManager,
private readonly telemetryReporter: TelemetryReporter private readonly _telemetryReporter: TelemetryReporter
) { } ) { }
public execute(uri?: vscode.Uri, previewSettings?: DynamicPreviewSettings) { public execute(uri?: vscode.Uri, previewSettings?: DynamicPreviewSettings) {
showPreview(this.webviewManager, this.telemetryReporter, uri, { showPreview(this._webviewManager, this._telemetryReporter, uri, {
sideBySide: true, sideBySide: true,
locked: previewSettings && previewSettings.locked locked: previewSettings && previewSettings.locked
}); });
@ -89,12 +89,12 @@ export class ShowLockedPreviewToSideCommand implements Command {
public readonly id = 'markdown.showLockedPreviewToSide'; public readonly id = 'markdown.showLockedPreviewToSide';
public constructor( public constructor(
private readonly webviewManager: MarkdownPreviewManager, private readonly _webviewManager: MarkdownPreviewManager,
private readonly telemetryReporter: TelemetryReporter private readonly _telemetryReporter: TelemetryReporter
) { } ) { }
public execute(uri?: vscode.Uri) { public execute(uri?: vscode.Uri) {
showPreview(this.webviewManager, this.telemetryReporter, uri, { showPreview(this._webviewManager, this._telemetryReporter, uri, {
sideBySide: true, sideBySide: true,
locked: true locked: true
}); });

View file

@ -13,18 +13,18 @@ export class ShowPreviewSecuritySelectorCommand implements Command {
public readonly id = 'markdown.showPreviewSecuritySelector'; public readonly id = 'markdown.showPreviewSecuritySelector';
public constructor( public constructor(
private readonly previewSecuritySelector: PreviewSecuritySelector, private readonly _previewSecuritySelector: PreviewSecuritySelector,
private readonly previewManager: MarkdownPreviewManager private readonly _previewManager: MarkdownPreviewManager
) { } ) { }
public execute(resource: string | undefined) { public execute(resource: string | undefined) {
if (this.previewManager.activePreviewResource) { if (this._previewManager.activePreviewResource) {
this.previewSecuritySelector.showSecuritySelectorForResource(this.previewManager.activePreviewResource); this._previewSecuritySelector.showSecuritySelectorForResource(this._previewManager.activePreviewResource);
} else if (resource) { } else if (resource) {
const source = vscode.Uri.parse(resource); const source = vscode.Uri.parse(resource);
this.previewSecuritySelector.showSecuritySelectorForResource(source.query ? vscode.Uri.parse(source.query) : source); this._previewSecuritySelector.showSecuritySelectorForResource(source.query ? vscode.Uri.parse(source.query) : source);
} else if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { } else if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) {
this.previewSecuritySelector.showSecuritySelectorForResource(vscode.window.activeTextEditor.document.uri); this._previewSecuritySelector.showSecuritySelectorForResource(vscode.window.activeTextEditor.document.uri);
} }
} }
} }

View file

@ -11,11 +11,11 @@ export class ShowSourceCommand implements Command {
public readonly id = 'markdown.showSource'; public readonly id = 'markdown.showSource';
public constructor( public constructor(
private readonly previewManager: MarkdownPreviewManager private readonly _previewManager: MarkdownPreviewManager
) { } ) { }
public execute() { public execute() {
const { activePreviewResource, activePreviewResourceColumn } = this.previewManager; const { activePreviewResource, activePreviewResourceColumn } = this._previewManager;
if (activePreviewResource && activePreviewResourceColumn) { if (activePreviewResource && activePreviewResourceColumn) {
return vscode.workspace.openTextDocument(activePreviewResource).then(document => { return vscode.workspace.openTextDocument(activePreviewResource).then(document => {
return vscode.window.showTextDocument(document, activePreviewResourceColumn); return vscode.window.showTextDocument(document, activePreviewResourceColumn);

View file

@ -10,10 +10,10 @@ export class ToggleLockCommand implements Command {
public readonly id = 'markdown.preview.toggleLock'; public readonly id = 'markdown.preview.toggleLock';
public constructor( public constructor(
private readonly previewManager: MarkdownPreviewManager private readonly _previewManager: MarkdownPreviewManager
) { } ) { }
public execute() { public execute() {
this.previewManager.toggleLock(); this._previewManager.toggleLock();
} }
} }

View file

@ -34,7 +34,7 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
for (const imageMime of supportedImageMimes) { for (const imageMime of supportedImageMimes) {
const file = dataTransfer.get(imageMime)?.asFile(); const file = dataTransfer.get(imageMime)?.asFile();
if (file) { if (file) {
const edit = await this.makeCreateImagePasteEdit(document, file, token); const edit = await this._makeCreateImagePasteEdit(document, file, token);
if (token.isCancellationRequested) { if (token.isCancellationRequested) {
return; return;
} }
@ -49,7 +49,7 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
return snippet ? new vscode.DocumentPasteEdit(snippet) : undefined; return snippet ? new vscode.DocumentPasteEdit(snippet) : undefined;
} }
private async makeCreateImagePasteEdit(document: vscode.TextDocument, file: vscode.DataTransferFile, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> { private async _makeCreateImagePasteEdit(document: vscode.TextDocument, file: vscode.DataTransferFile, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> {
if (file.uri) { if (file.uri) {
// If file is already in workspace, we don't want to create a copy of it // If file is already in workspace, we don't want to create a copy of it
const workspaceFolder = vscode.workspace.getWorkspaceFolder(file.uri); const workspaceFolder = vscode.workspace.getWorkspaceFolder(file.uri);
@ -59,7 +59,7 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
} }
} }
const uri = await this.getNewFileName(document, file); const uri = await this._getNewFileName(document, file);
if (token.isCancellationRequested) { if (token.isCancellationRequested) {
return; return;
} }
@ -78,7 +78,7 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
return pasteEdit; return pasteEdit;
} }
private async getNewFileName(document: vscode.TextDocument, file: vscode.DataTransferFile): Promise<vscode.Uri> { private async _getNewFileName(document: vscode.TextDocument, file: vscode.DataTransferFile): Promise<vscode.Uri> {
const root = Utils.dirname(document.uri); const root = Utils.dirname(document.uri);
const ext = path.extname(file.name); const ext = path.extname(file.name);

View file

@ -22,14 +22,14 @@ class AddToIgnoreLinksQuickFixProvider implements vscode.CodeActionProvider {
private static readonly _addToIgnoreLinksCommandId = '_markdown.addToIgnoreLinks'; private static readonly _addToIgnoreLinksCommandId = '_markdown.addToIgnoreLinks';
private static readonly metadata: vscode.CodeActionProviderMetadata = { private static readonly _metadata: vscode.CodeActionProviderMetadata = {
providedCodeActionKinds: [ providedCodeActionKinds: [
vscode.CodeActionKind.QuickFix vscode.CodeActionKind.QuickFix
], ],
}; };
public static register(selector: vscode.DocumentSelector, commandManager: CommandManager): vscode.Disposable { public static register(selector: vscode.DocumentSelector, commandManager: CommandManager): vscode.Disposable {
const reg = vscode.languages.registerCodeActionsProvider(selector, new AddToIgnoreLinksQuickFixProvider(), AddToIgnoreLinksQuickFixProvider.metadata); const reg = vscode.languages.registerCodeActionsProvider(selector, new AddToIgnoreLinksQuickFixProvider(), AddToIgnoreLinksQuickFixProvider._metadata);
const commandReg = commandManager.register({ const commandReg = commandManager.register({
id: AddToIgnoreLinksQuickFixProvider._addToIgnoreLinksCommandId, id: AddToIgnoreLinksQuickFixProvider._addToIgnoreLinksCommandId,
execute(resource: vscode.Uri, path: string) { execute(resource: vscode.Uri, path: string) {

View file

@ -17,7 +17,7 @@ export class FindFileReferencesCommand implements Command {
public readonly id = 'markdown.findAllFileReferences'; public readonly id = 'markdown.findAllFileReferences';
constructor( constructor(
private readonly client: MdLanguageClient, private readonly _client: MdLanguageClient,
) { } ) { }
public async execute(resource?: vscode.Uri) { public async execute(resource?: vscode.Uri) {
@ -31,7 +31,7 @@ export class FindFileReferencesCommand implements Command {
location: vscode.ProgressLocation.Window, location: vscode.ProgressLocation.Window,
title: localize('progress.title', "Finding file references") title: localize('progress.title', "Finding file references")
}, async (_progress, token) => { }, async (_progress, token) => {
const locations = (await this.client.getReferencesToFileInWorkspace(resource!, token)).map(loc => { const locations = (await this._client.getReferencesToFileInWorkspace(resource!, token)).map(loc => {
return new vscode.Location(vscode.Uri.parse(loc.uri), convertRange(loc.range)); return new vscode.Location(vscode.Uri.parse(loc.uri), convertRange(loc.range));
}); });

View file

@ -39,13 +39,13 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
private readonly _pendingRenames = new Set<RenameAction>(); private readonly _pendingRenames = new Set<RenameAction>();
public constructor( public constructor(
private readonly client: MdLanguageClient, private readonly _client: MdLanguageClient,
) { ) {
super(); super();
this._register(vscode.workspace.onDidRenameFiles(async (e) => { this._register(vscode.workspace.onDidRenameFiles(async (e) => {
await Promise.all(e.files.map(async (rename) => { await Promise.all(e.files.map(async (rename) => {
if (await this.shouldParticipateInLinkUpdate(rename.newUri)) { if (await this._shouldParticipateInLinkUpdate(rename.newUri)) {
this._pendingRenames.add(rename); this._pendingRenames.add(rename);
} }
})); }));
@ -55,26 +55,26 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
vscode.window.withProgress({ vscode.window.withProgress({
location: vscode.ProgressLocation.Window, location: vscode.ProgressLocation.Window,
title: localize('renameProgress.title', "Checking for Markdown links to update") title: localize('renameProgress.title', "Checking for Markdown links to update")
}, () => this.flushRenames()); }, () => this._flushRenames());
}); });
} }
})); }));
} }
private async flushRenames(): Promise<void> { private async _flushRenames(): Promise<void> {
const renames = Array.from(this._pendingRenames); const renames = Array.from(this._pendingRenames);
this._pendingRenames.clear(); this._pendingRenames.clear();
const result = await this.getEditsForFileRename(renames, noopToken); const result = await this._getEditsForFileRename(renames, noopToken);
if (result && result.edit.size) { if (result && result.edit.size) {
if (await this.confirmActionWithUser(result.resourcesBeingRenamed)) { if (await this._confirmActionWithUser(result.resourcesBeingRenamed)) {
await vscode.workspace.applyEdit(result.edit); await vscode.workspace.applyEdit(result.edit);
} }
} }
} }
private async confirmActionWithUser(newResources: readonly vscode.Uri[]): Promise<boolean> { private async _confirmActionWithUser(newResources: readonly vscode.Uri[]): Promise<boolean> {
if (!newResources.length) { if (!newResources.length) {
return false; return false;
} }
@ -83,7 +83,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
const setting = config.get<UpdateLinksOnFileMoveSetting>(settingNames.enabled); const setting = config.get<UpdateLinksOnFileMoveSetting>(settingNames.enabled);
switch (setting) { switch (setting) {
case UpdateLinksOnFileMoveSetting.Prompt: case UpdateLinksOnFileMoveSetting.Prompt:
return this.promptUser(newResources); return this._promptUser(newResources);
case UpdateLinksOnFileMoveSetting.Always: case UpdateLinksOnFileMoveSetting.Always:
return true; return true;
case UpdateLinksOnFileMoveSetting.Never: case UpdateLinksOnFileMoveSetting.Never:
@ -91,7 +91,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
return false; return false;
} }
} }
private async shouldParticipateInLinkUpdate(newUri: vscode.Uri): Promise<boolean> { private async _shouldParticipateInLinkUpdate(newUri: vscode.Uri): Promise<boolean> {
const config = vscode.workspace.getConfiguration('markdown', newUri); const config = vscode.workspace.getConfiguration('markdown', newUri);
const setting = config.get<UpdateLinksOnFileMoveSetting>(settingNames.enabled); const setting = config.get<UpdateLinksOnFileMoveSetting>(settingNames.enabled);
if (setting === UpdateLinksOnFileMoveSetting.Never) { if (setting === UpdateLinksOnFileMoveSetting.Never) {
@ -115,7 +115,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
return false; return false;
} }
private async promptUser(newResources: readonly vscode.Uri[]): Promise<boolean> { private async _promptUser(newResources: readonly vscode.Uri[]): Promise<boolean> {
if (!newResources.length) { if (!newResources.length) {
return false; return false;
} }
@ -140,7 +140,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
const choice = await vscode.window.showInformationMessage( const choice = await vscode.window.showInformationMessage(
newResources.length === 1 newResources.length === 1
? localize('prompt', "Update Markdown links for '{0}'?", path.basename(newResources[0].fsPath)) ? localize('prompt', "Update Markdown links for '{0}'?", path.basename(newResources[0].fsPath))
: this.getConfirmMessage(localize('promptMoreThanOne', "Update Markdown links for the following {0} files?", newResources.length), newResources), { : this._getConfirmMessage(localize('promptMoreThanOne', "Update Markdown links for the following {0} files?", newResources.length), newResources), {
modal: true, modal: true,
}, rejectItem, acceptItem, alwaysItem, neverItem); }, rejectItem, acceptItem, alwaysItem, neverItem);
@ -156,7 +156,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
config.update( config.update(
settingNames.enabled, settingNames.enabled,
UpdateLinksOnFileMoveSetting.Always, UpdateLinksOnFileMoveSetting.Always,
this.getConfigTargetScope(config, settingNames.enabled)); this._getConfigTargetScope(config, settingNames.enabled));
return true; return true;
} }
case neverItem: { case neverItem: {
@ -164,7 +164,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
config.update( config.update(
settingNames.enabled, settingNames.enabled,
UpdateLinksOnFileMoveSetting.Never, UpdateLinksOnFileMoveSetting.Never,
this.getConfigTargetScope(config, settingNames.enabled)); this._getConfigTargetScope(config, settingNames.enabled));
return false; return false;
} }
default: { default: {
@ -173,8 +173,8 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
} }
} }
private async getEditsForFileRename(renames: readonly RenameAction[], token: vscode.CancellationToken): Promise<{ edit: vscode.WorkspaceEdit; resourcesBeingRenamed: vscode.Uri[] } | undefined> { private async _getEditsForFileRename(renames: readonly RenameAction[], token: vscode.CancellationToken): Promise<{ edit: vscode.WorkspaceEdit; resourcesBeingRenamed: vscode.Uri[] } | undefined> {
const result = await this.client.getEditForFileRenames(renames.map(rename => ({ oldUri: rename.oldUri.toString(), newUri: rename.newUri.toString() })), token); const result = await this._client.getEditForFileRenames(renames.map(rename => ({ oldUri: rename.oldUri.toString(), newUri: rename.newUri.toString() })), token);
if (!result?.edit.documentChanges?.length) { if (!result?.edit.documentChanges?.length) {
return undefined; return undefined;
} }
@ -194,7 +194,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
}; };
} }
private getConfirmMessage(start: string, resourcesToConfirm: readonly vscode.Uri[]): string { private _getConfirmMessage(start: string, resourcesToConfirm: readonly vscode.Uri[]): string {
const MAX_CONFIRM_FILES = 10; const MAX_CONFIRM_FILES = 10;
const paths = [start]; const paths = [start];
@ -213,7 +213,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
return paths.join('\n'); return paths.join('\n');
} }
private getConfigTargetScope(config: vscode.WorkspaceConfiguration, settingsName: string): vscode.ConfigurationTarget { private _getConfigTargetScope(config: vscode.WorkspaceConfiguration, settingsName: string): vscode.ConfigurationTarget {
const inspected = config.inspect(settingsName); const inspected = config.inspect(settingsName);
if (inspected?.workspaceFolderValue) { if (inspected?.workspaceFolderValue) {
return vscode.ConfigurationTarget.WorkspaceFolder; return vscode.ConfigurationTarget.WorkspaceFolder;

View file

@ -30,54 +30,54 @@ export interface ILogger {
} }
export class VsCodeOutputLogger extends Disposable implements ILogger { export class VsCodeOutputLogger extends Disposable implements ILogger {
private trace?: Trace; private _trace?: Trace;
private _outputChannel?: vscode.OutputChannel; private _outputChannelValue?: vscode.OutputChannel;
private get outputChannel() { private get _outputChannel() {
this._outputChannel ??= this._register(vscode.window.createOutputChannel('Markdown')); this._outputChannelValue ??= this._register(vscode.window.createOutputChannel('Markdown'));
return this._outputChannel; return this._outputChannelValue;
} }
constructor() { constructor() {
super(); super();
this._register(vscode.workspace.onDidChangeConfiguration(() => { this._register(vscode.workspace.onDidChangeConfiguration(() => {
this.updateConfiguration(); this._updateConfiguration();
})); }));
this.updateConfiguration(); this._updateConfiguration();
} }
public verbose(title: string, message: string, data?: any): void { public verbose(title: string, message: string, data?: any): void {
if (this.trace === Trace.Verbose) { if (this._trace === Trace.Verbose) {
this.appendLine(`[Verbose ${this.now()}] ${title}: ${message}`); this._appendLine(`[Verbose ${this._now()}] ${title}: ${message}`);
if (data) { if (data) {
this.appendLine(VsCodeOutputLogger.data2String(data)); this._appendLine(VsCodeOutputLogger._data2String(data));
} }
} }
} }
private now(): string { private _now(): string {
const now = new Date(); const now = new Date();
return String(now.getUTCHours()).padStart(2, '0') return String(now.getUTCHours()).padStart(2, '0')
+ ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0')
+ ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0'); + ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0');
} }
private updateConfiguration(): void { private _updateConfiguration(): void {
this.trace = this.readTrace(); this._trace = this._readTrace();
} }
private appendLine(value: string): void { private _appendLine(value: string): void {
this.outputChannel.appendLine(value); this._outputChannel.appendLine(value);
} }
private readTrace(): Trace { private _readTrace(): Trace {
return Trace.fromString(vscode.workspace.getConfiguration().get<string>('markdown.trace.extension', 'off')); return Trace.fromString(vscode.workspace.getConfiguration().get<string>('markdown.trace.extension', 'off'));
} }
private static data2String(data: any): string { private static _data2String(data: any): string {
if (data instanceof Error) { if (data instanceof Error) {
if (typeof data.stack === 'string') { if (typeof data.stack === 'string') {
return data.stack; return data.stack;

View file

@ -46,37 +46,37 @@ const pluginSourceMap: MarkdownIt.PluginSimple = (md): void => {
type MarkdownItConfig = Readonly<Required<Pick<MarkdownIt.Options, 'breaks' | 'linkify' | 'typographer'>>>; type MarkdownItConfig = Readonly<Required<Pick<MarkdownIt.Options, 'breaks' | 'linkify' | 'typographer'>>>;
class TokenCache { class TokenCache {
private cachedDocument?: { private _cachedDocument?: {
readonly uri: vscode.Uri; readonly uri: vscode.Uri;
readonly version: number; readonly version: number;
readonly config: MarkdownItConfig; readonly config: MarkdownItConfig;
}; };
private tokens?: Token[]; private _tokens?: Token[];
public tryGetCached(document: ITextDocument, config: MarkdownItConfig): Token[] | undefined { public tryGetCached(document: ITextDocument, config: MarkdownItConfig): Token[] | undefined {
if (this.cachedDocument if (this._cachedDocument
&& this.cachedDocument.uri.toString() === document.uri.toString() && this._cachedDocument.uri.toString() === document.uri.toString()
&& this.cachedDocument.version === document.version && this._cachedDocument.version === document.version
&& this.cachedDocument.config.breaks === config.breaks && this._cachedDocument.config.breaks === config.breaks
&& this.cachedDocument.config.linkify === config.linkify && this._cachedDocument.config.linkify === config.linkify
) { ) {
return this.tokens; return this._tokens;
} }
return undefined; return undefined;
} }
public update(document: ITextDocument, config: MarkdownItConfig, tokens: Token[]) { public update(document: ITextDocument, config: MarkdownItConfig, tokens: Token[]) {
this.cachedDocument = { this._cachedDocument = {
uri: document.uri, uri: document.uri,
version: document.version, version: document.version,
config, config,
}; };
this.tokens = tokens; this._tokens = tokens;
} }
public clean(): void { public clean(): void {
this.cachedDocument = undefined; this._cachedDocument = undefined;
this.tokens = undefined; this._tokens = undefined;
} }
} }
@ -99,7 +99,7 @@ export interface IMdParser {
export class MarkdownItEngine implements IMdParser { export class MarkdownItEngine implements IMdParser {
private md?: Promise<MarkdownIt>; private _md?: Promise<MarkdownIt>;
private _slugCount = new Map<string, number>(); private _slugCount = new Map<string, number>();
private _tokenCache = new TokenCache(); private _tokenCache = new TokenCache();
@ -107,26 +107,26 @@ export class MarkdownItEngine implements IMdParser {
public readonly slugifier: Slugifier; public readonly slugifier: Slugifier;
public constructor( public constructor(
private readonly contributionProvider: MarkdownContributionProvider, private readonly _contributionProvider: MarkdownContributionProvider,
slugifier: Slugifier, slugifier: Slugifier,
private readonly logger: ILogger, private readonly _logger: ILogger,
) { ) {
this.slugifier = slugifier; this.slugifier = slugifier;
contributionProvider.onContributionsChanged(() => { _contributionProvider.onContributionsChanged(() => {
// Markdown plugin contributions may have changed // Markdown plugin contributions may have changed
this.md = undefined; this._md = undefined;
}); });
} }
private async getEngine(config: MarkdownItConfig): Promise<MarkdownIt> { private async _getEngine(config: MarkdownItConfig): Promise<MarkdownIt> {
if (!this.md) { if (!this._md) {
this.md = (async () => { this._md = (async () => {
const markdownIt = await import('markdown-it'); const markdownIt = await import('markdown-it');
let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md)); let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md));
md.linkify.set({ fuzzyLink: false }); md.linkify.set({ fuzzyLink: false });
for (const plugin of this.contributionProvider.contributions.markdownItPlugins.values()) { for (const plugin of this._contributionProvider.contributions.markdownItPlugins.values()) {
try { try {
md = (await plugin)(md); md = (await plugin)(md);
} catch (e) { } catch (e) {
@ -149,60 +149,60 @@ export class MarkdownItEngine implements IMdParser {
alt: ['paragraph', 'reference', 'blockquote', 'list'] alt: ['paragraph', 'reference', 'blockquote', 'list']
}); });
this.addImageRenderer(md); this._addImageRenderer(md);
this.addFencedRenderer(md); this._addFencedRenderer(md);
this.addLinkNormalizer(md); this._addLinkNormalizer(md);
this.addLinkValidator(md); this._addLinkValidator(md);
this.addNamedHeaders(md); this._addNamedHeaders(md);
this.addLinkRenderer(md); this._addLinkRenderer(md);
md.use(pluginSourceMap); md.use(pluginSourceMap);
return md; return md;
})(); })();
} }
const md = await this.md!; const md = await this._md!;
md.set(config); md.set(config);
return md; return md;
} }
public reloadPlugins() { public reloadPlugins() {
this.md = undefined; this._md = undefined;
} }
private tokenizeDocument( private _tokenizeDocument(
document: ITextDocument, document: ITextDocument,
config: MarkdownItConfig, config: MarkdownItConfig,
engine: MarkdownIt engine: MarkdownIt
): Token[] { ): Token[] {
const cached = this._tokenCache.tryGetCached(document, config); const cached = this._tokenCache.tryGetCached(document, config);
if (cached) { if (cached) {
this.resetSlugCount(); this._resetSlugCount();
return cached; return cached;
} }
this.logger.verbose('MarkdownItEngine', `tokenizeDocument - ${document.uri}`); this._logger.verbose('MarkdownItEngine', `tokenizeDocument - ${document.uri}`);
const tokens = this.tokenizeString(document.getText(), engine); const tokens = this._tokenizeString(document.getText(), engine);
this._tokenCache.update(document, config, tokens); this._tokenCache.update(document, config, tokens);
return tokens; return tokens;
} }
private tokenizeString(text: string, engine: MarkdownIt) { private _tokenizeString(text: string, engine: MarkdownIt) {
this.resetSlugCount(); this._resetSlugCount();
return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {});
} }
private resetSlugCount(): void { private _resetSlugCount(): void {
this._slugCount = new Map<string, number>(); this._slugCount = new Map<string, number>();
} }
public async render(input: ITextDocument | string, resourceProvider?: WebviewResourceProvider): Promise<RenderOutput> { public async render(input: ITextDocument | string, resourceProvider?: WebviewResourceProvider): Promise<RenderOutput> {
const config = this.getConfig(typeof input === 'string' ? undefined : input.uri); const config = this._getConfig(typeof input === 'string' ? undefined : input.uri);
const engine = await this.getEngine(config); const engine = await this._getEngine(config);
const tokens = typeof input === 'string' const tokens = typeof input === 'string'
? this.tokenizeString(input, engine) ? this._tokenizeString(input, engine)
: this.tokenizeDocument(input, config, engine); : this._tokenizeDocument(input, config, engine);
const env: RenderEnv = { const env: RenderEnv = {
containingImages: new Set<string>(), containingImages: new Set<string>(),
@ -222,16 +222,16 @@ export class MarkdownItEngine implements IMdParser {
} }
public async tokenize(document: ITextDocument): Promise<Token[]> { public async tokenize(document: ITextDocument): Promise<Token[]> {
const config = this.getConfig(document.uri); const config = this._getConfig(document.uri);
const engine = await this.getEngine(config); const engine = await this._getEngine(config);
return this.tokenizeDocument(document, config, engine); return this._tokenizeDocument(document, config, engine);
} }
public cleanCache(): void { public cleanCache(): void {
this._tokenCache.clean(); this._tokenCache.clean();
} }
private getConfig(resource?: vscode.Uri): MarkdownItConfig { private _getConfig(resource?: vscode.Uri): MarkdownItConfig {
const config = vscode.workspace.getConfiguration('markdown', resource ?? null); const config = vscode.workspace.getConfiguration('markdown', resource ?? null);
return { return {
breaks: config.get<boolean>('preview.breaks', false), breaks: config.get<boolean>('preview.breaks', false),
@ -240,7 +240,7 @@ export class MarkdownItEngine implements IMdParser {
}; };
} }
private addImageRenderer(md: MarkdownIt): void { private _addImageRenderer(md: MarkdownIt): void {
const original = md.renderer.rules.image; const original = md.renderer.rules.image;
md.renderer.rules.image = (tokens: Token[], idx: number, options, env: RenderEnv, self) => { md.renderer.rules.image = (tokens: Token[], idx: number, options, env: RenderEnv, self) => {
const token = tokens[idx]; const token = tokens[idx];
@ -249,7 +249,7 @@ export class MarkdownItEngine implements IMdParser {
env.containingImages?.add(src); env.containingImages?.add(src);
if (!token.attrGet('data-src')) { if (!token.attrGet('data-src')) {
token.attrSet('src', this.toResourceUri(src, env.currentDocument, env.resourceProvider)); token.attrSet('src', this._toResourceUri(src, env.currentDocument, env.resourceProvider));
token.attrSet('data-src', src); token.attrSet('data-src', src);
} }
} }
@ -262,11 +262,11 @@ export class MarkdownItEngine implements IMdParser {
}; };
} }
private addFencedRenderer(md: MarkdownIt): void { private _addFencedRenderer(md: MarkdownIt): void {
const original = md.renderer.rules['fenced']; const original = md.renderer.rules['fenced'];
md.renderer.rules['fenced'] = (tokens: Token[], idx: number, options, env, self) => { md.renderer.rules['fenced'] = (tokens: Token[], idx: number, options, env, self) => {
const token = tokens[idx]; const token = tokens[idx];
if (token.map && token.map.length) { if (token.map?.length) {
token.attrJoin('class', 'hljs'); token.attrJoin('class', 'hljs');
} }
@ -278,7 +278,7 @@ export class MarkdownItEngine implements IMdParser {
}; };
} }
private addLinkNormalizer(md: MarkdownIt): void { private _addLinkNormalizer(md: MarkdownIt): void {
const normalizeLink = md.normalizeLink; const normalizeLink = md.normalizeLink;
md.normalizeLink = (link: string) => { md.normalizeLink = (link: string) => {
try { try {
@ -294,7 +294,7 @@ export class MarkdownItEngine implements IMdParser {
}; };
} }
private addLinkValidator(md: MarkdownIt): void { private _addLinkValidator(md: MarkdownIt): void {
const validateLink = md.validateLink; const validateLink = md.validateLink;
md.validateLink = (link: string) => { md.validateLink = (link: string) => {
return validateLink(link) return validateLink(link)
@ -304,7 +304,7 @@ export class MarkdownItEngine implements IMdParser {
}; };
} }
private addNamedHeaders(md: MarkdownIt): void { private _addNamedHeaders(md: MarkdownIt): void {
const original = md.renderer.rules.heading_open; const original = md.renderer.rules.heading_open;
md.renderer.rules.heading_open = (tokens: Token[], idx: number, options, env, self) => { md.renderer.rules.heading_open = (tokens: Token[], idx: number, options, env, self) => {
const title = tokens[idx + 1].children!.reduce<string>((acc, t) => acc + t.content, ''); const title = tokens[idx + 1].children!.reduce<string>((acc, t) => acc + t.content, '');
@ -328,7 +328,7 @@ export class MarkdownItEngine implements IMdParser {
}; };
} }
private addLinkRenderer(md: MarkdownIt): void { private _addLinkRenderer(md: MarkdownIt): void {
const original = md.renderer.rules.link_open; const original = md.renderer.rules.link_open;
md.renderer.rules.link_open = (tokens: Token[], idx: number, options, env, self) => { md.renderer.rules.link_open = (tokens: Token[], idx: number, options, env, self) => {
@ -346,7 +346,7 @@ export class MarkdownItEngine implements IMdParser {
}; };
} }
private toResourceUri(href: string, currentDocument: vscode.Uri | undefined, resourceProvider: WebviewResourceProvider | undefined): string { private _toResourceUri(href: string, currentDocument: vscode.Uri | undefined, resourceProvider: WebviewResourceProvider | undefined): string {
try { try {
// Support file:// links // Support file:// links
if (isOfScheme(Schemes.file, href)) { if (isOfScheme(Schemes.file, href)) {

View file

@ -127,7 +127,7 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements
super(); super();
this._register(vscode.extensions.onDidChange(() => { this._register(vscode.extensions.onDidChange(() => {
const currentContributions = this.getCurrentContributions(); const currentContributions = this._getCurrentContributions();
const existingContributions = this._contributions || MarkdownContributions.Empty; const existingContributions = this._contributions || MarkdownContributions.Empty;
if (!MarkdownContributions.equal(existingContributions, currentContributions)) { if (!MarkdownContributions.equal(existingContributions, currentContributions)) {
this._contributions = currentContributions; this._contributions = currentContributions;
@ -144,11 +144,11 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements
public readonly onContributionsChanged = this._onContributionsChanged.event; public readonly onContributionsChanged = this._onContributionsChanged.event;
public get contributions(): MarkdownContributions { public get contributions(): MarkdownContributions {
this._contributions ??= this.getCurrentContributions(); this._contributions ??= this._getCurrentContributions();
return this._contributions; return this._contributions;
} }
private getCurrentContributions(): MarkdownContributions { private _getCurrentContributions(): MarkdownContributions {
return vscode.extensions.all return vscode.extensions.all
.map(MarkdownContributions.fromExtension) .map(MarkdownContributions.fromExtension)
.reduce(MarkdownContributions.merge, MarkdownContributions.Empty); .reduce(MarkdownContributions.merge, MarkdownContributions.Empty);

View file

@ -44,15 +44,15 @@ export interface MarkdownContentProviderOutput {
export class MdDocumentRenderer { export class MdDocumentRenderer {
constructor( constructor(
private readonly engine: MarkdownItEngine, private readonly _engine: MarkdownItEngine,
private readonly context: vscode.ExtensionContext, private readonly _context: vscode.ExtensionContext,
private readonly cspArbiter: ContentSecurityPolicyArbiter, private readonly _cspArbiter: ContentSecurityPolicyArbiter,
private readonly contributionProvider: MarkdownContributionProvider, private readonly _contributionProvider: MarkdownContributionProvider,
private readonly logger: ILogger private readonly _logger: ILogger
) { ) {
this.iconPath = { this.iconPath = {
dark: vscode.Uri.joinPath(this.context.extensionUri, 'media', 'preview-dark.svg'), dark: vscode.Uri.joinPath(this._context.extensionUri, 'media', 'preview-dark.svg'),
light: vscode.Uri.joinPath(this.context.extensionUri, 'media', 'preview-light.svg'), light: vscode.Uri.joinPath(this._context.extensionUri, 'media', 'preview-light.svg'),
}; };
} }
@ -77,15 +77,15 @@ export class MdDocumentRenderer {
scrollPreviewWithEditor: config.scrollPreviewWithEditor, scrollPreviewWithEditor: config.scrollPreviewWithEditor,
scrollEditorWithPreview: config.scrollEditorWithPreview, scrollEditorWithPreview: config.scrollEditorWithPreview,
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor, doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor,
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings(), disableSecurityWarnings: this._cspArbiter.shouldDisableSecurityWarnings(),
webviewResourceRoot: resourceProvider.asWebviewUri(markdownDocument.uri).toString(), webviewResourceRoot: resourceProvider.asWebviewUri(markdownDocument.uri).toString(),
}; };
this.logger.verbose('DocumentRenderer', `provideTextDocumentContent - ${markdownDocument.uri}`, initialData); this._logger.verbose('DocumentRenderer', `provideTextDocumentContent - ${markdownDocument.uri}`, initialData);
// Content Security Policy // Content Security Policy
const nonce = getNonce(); const nonce = getNonce();
const csp = this.getCsp(resourceProvider, sourceUri, nonce); const csp = this._getCsp(resourceProvider, sourceUri, nonce);
const body = await this.renderBody(markdownDocument, resourceProvider); const body = await this.renderBody(markdownDocument, resourceProvider);
if (token.isCancellationRequested) { if (token.isCancellationRequested) {
@ -93,7 +93,7 @@ export class MdDocumentRenderer {
} }
const html = `<!DOCTYPE html> const html = `<!DOCTYPE html>
<html style="${escapeAttribute(this.getSettingsOverrideStyles(config))}"> <html style="${escapeAttribute(this._getSettingsOverrideStyles(config))}">
<head> <head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
${csp} ${csp}
@ -101,13 +101,13 @@ export class MdDocumentRenderer {
data-settings="${escapeAttribute(JSON.stringify(initialData))}" data-settings="${escapeAttribute(JSON.stringify(initialData))}"
data-strings="${escapeAttribute(JSON.stringify(previewStrings))}" data-strings="${escapeAttribute(JSON.stringify(previewStrings))}"
data-state="${escapeAttribute(JSON.stringify(state || {}))}"> data-state="${escapeAttribute(JSON.stringify(state || {}))}">
<script src="${this.extensionResourcePath(resourceProvider, 'pre.js')}" nonce="${nonce}"></script> <script src="${this._extensionResourcePath(resourceProvider, 'pre.js')}" nonce="${nonce}"></script>
${this.getStyles(resourceProvider, sourceUri, config, state)} ${this._getStyles(resourceProvider, sourceUri, config, state)}
<base href="${resourceProvider.asWebviewUri(markdownDocument.uri)}"> <base href="${resourceProvider.asWebviewUri(markdownDocument.uri)}">
</head> </head>
<body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}"> <body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}">
${body.html} ${body.html}
${this.getScripts(resourceProvider, nonce)} ${this._getScripts(resourceProvider, nonce)}
</body> </body>
</html>`; </html>`;
return { return {
@ -120,7 +120,7 @@ export class MdDocumentRenderer {
markdownDocument: vscode.TextDocument, markdownDocument: vscode.TextDocument,
resourceProvider: WebviewResourceProvider, resourceProvider: WebviewResourceProvider,
): Promise<MarkdownContentProviderOutput> { ): Promise<MarkdownContentProviderOutput> {
const rendered = await this.engine.render(markdownDocument, resourceProvider); const rendered = await this._engine.render(markdownDocument, resourceProvider);
const html = `<div class="markdown-body" dir="auto">${rendered.html}<div class="code-line" data-line="${markdownDocument.lineCount}"></div></div>`; const html = `<div class="markdown-body" dir="auto">${rendered.html}<div class="code-line" data-line="${markdownDocument.lineCount}"></div></div>`;
return { return {
html, html,
@ -139,13 +139,13 @@ export class MdDocumentRenderer {
</html>`; </html>`;
} }
private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string { private _extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string {
const webviewResource = resourceProvider.asWebviewUri( const webviewResource = resourceProvider.asWebviewUri(
vscode.Uri.joinPath(this.context.extensionUri, 'media', mediaFile)); vscode.Uri.joinPath(this._context.extensionUri, 'media', mediaFile));
return webviewResource.toString(); return webviewResource.toString();
} }
private fixHref(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, href: string): string { private _fixHref(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, href: string): string {
if (!href) { if (!href) {
return href; return href;
} }
@ -169,18 +169,18 @@ export class MdDocumentRenderer {
return resourceProvider.asWebviewUri(vscode.Uri.joinPath(uri.Utils.dirname(resource), href)).toString(); return resourceProvider.asWebviewUri(vscode.Uri.joinPath(uri.Utils.dirname(resource), href)).toString();
} }
private computeCustomStyleSheetIncludes(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string { private _computeCustomStyleSheetIncludes(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
if (!Array.isArray(config.styles)) { if (!Array.isArray(config.styles)) {
return ''; return '';
} }
const out: string[] = []; const out: string[] = [];
for (const style of config.styles) { for (const style of config.styles) {
out.push(`<link rel="stylesheet" class="code-user-style" data-source="${escapeAttribute(style)}" href="${escapeAttribute(this.fixHref(resourceProvider, resource, style))}" type="text/css" media="screen">`); out.push(`<link rel="stylesheet" class="code-user-style" data-source="${escapeAttribute(style)}" href="${escapeAttribute(this._fixHref(resourceProvider, resource, style))}" type="text/css" media="screen">`);
} }
return out.join('\n'); return out.join('\n');
} }
private getSettingsOverrideStyles(config: MarkdownPreviewConfiguration): string { private _getSettingsOverrideStyles(config: MarkdownPreviewConfiguration): string {
return [ return [
config.fontFamily ? `--markdown-font-family: ${config.fontFamily};` : '', config.fontFamily ? `--markdown-font-family: ${config.fontFamily};` : '',
isNaN(config.fontSize) ? '' : `--markdown-font-size: ${config.fontSize}px;`, isNaN(config.fontSize) ? '' : `--markdown-font-size: ${config.fontSize}px;`,
@ -188,7 +188,7 @@ export class MdDocumentRenderer {
].join(' '); ].join(' ');
} }
private getImageStabilizerStyles(state?: any) { private _getImageStabilizerStyles(state?: any) {
let ret = '<style>\n'; let ret = '<style>\n';
if (state && state.imageInfo) { if (state && state.imageInfo) {
state.imageInfo.forEach((imgInfo: any) => { state.imageInfo.forEach((imgInfo: any) => {
@ -203,20 +203,20 @@ export class MdDocumentRenderer {
return ret; return ret;
} }
private getStyles(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration, state?: any): string { private _getStyles(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration, state?: any): string {
const baseStyles: string[] = []; const baseStyles: string[] = [];
for (const resource of this.contributionProvider.contributions.previewStyles) { for (const resource of this._contributionProvider.contributions.previewStyles) {
baseStyles.push(`<link rel="stylesheet" type="text/css" href="${escapeAttribute(resourceProvider.asWebviewUri(resource))}">`); baseStyles.push(`<link rel="stylesheet" type="text/css" href="${escapeAttribute(resourceProvider.asWebviewUri(resource))}">`);
} }
return `${baseStyles.join('\n')} return `${baseStyles.join('\n')}
${this.computeCustomStyleSheetIncludes(resourceProvider, resource, config)} ${this._computeCustomStyleSheetIncludes(resourceProvider, resource, config)}
${this.getImageStabilizerStyles(state)}`; ${this._getImageStabilizerStyles(state)}`;
} }
private getScripts(resourceProvider: WebviewResourceProvider, nonce: string): string { private _getScripts(resourceProvider: WebviewResourceProvider, nonce: string): string {
const out: string[] = []; const out: string[] = [];
for (const resource of this.contributionProvider.contributions.previewScripts) { for (const resource of this._contributionProvider.contributions.previewScripts) {
out.push(`<script async out.push(`<script async
src="${escapeAttribute(resourceProvider.asWebviewUri(resource))}" src="${escapeAttribute(resourceProvider.asWebviewUri(resource))}"
nonce="${nonce}" nonce="${nonce}"
@ -225,13 +225,13 @@ export class MdDocumentRenderer {
return out.join('\n'); return out.join('\n');
} }
private getCsp( private _getCsp(
provider: WebviewResourceProvider, provider: WebviewResourceProvider,
resource: vscode.Uri, resource: vscode.Uri,
nonce: string nonce: string
): string { ): string {
const rule = provider.cspSource; const rule = provider.cspSource;
switch (this.cspArbiter.getSecurityLevelForResource(resource)) { switch (this._cspArbiter.getSecurityLevelForResource(resource)) {
case MarkdownPreviewSecurityLevel.AllowInsecureContent: case MarkdownPreviewSecurityLevel.AllowInsecureContent:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${rule} http: https: data:; media-src 'self' ${rule} http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${rule} 'unsafe-inline' http: https: data:; font-src 'self' ${rule} http: https: data:;">`; return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${rule} http: https: data:; media-src 'self' ${rule} http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${rule} 'unsafe-inline' http: https: data:; font-src 'self' ${rule} http: https: data:;">`;

View file

@ -64,16 +64,16 @@ interface PreviewStyleLoadErrorMessage extends WebviewMessage {
export class PreviewDocumentVersion { export class PreviewDocumentVersion {
public readonly resource: vscode.Uri; public readonly resource: vscode.Uri;
private readonly version: number; private readonly _version: number;
public constructor(document: vscode.TextDocument) { public constructor(document: vscode.TextDocument) {
this.resource = document.uri; this.resource = document.uri;
this.version = document.version; this._version = document.version;
} }
public equals(other: PreviewDocumentVersion): boolean { public equals(other: PreviewDocumentVersion): boolean {
return this.resource.fsPath === other.resource.fsPath return this.resource.fsPath === other.resource.fsPath
&& this.version === other.version; && this._version === other._version;
} }
} }
@ -86,23 +86,23 @@ interface MarkdownPreviewDelegate {
class MarkdownPreview extends Disposable implements WebviewResourceProvider { class MarkdownPreview extends Disposable implements WebviewResourceProvider {
private static readonly unwatchedImageSchemes = new Set(['https', 'http', 'data']); private static readonly _unwatchedImageSchemes = new Set(['https', 'http', 'data']);
private _disposed: boolean = false; private _disposed: boolean = false;
private readonly delay = 300; private readonly _delay = 300;
private throttleTimer: any; private _throttleTimer: any;
private readonly _resource: vscode.Uri; private readonly _resource: vscode.Uri;
private readonly _webviewPanel: vscode.WebviewPanel; private readonly _webviewPanel: vscode.WebviewPanel;
private line: number | undefined; private _line: number | undefined;
private scrollToFragment: string | undefined; private _scrollToFragment: string | undefined;
private firstUpdate = true; private _firstUpdate = true;
private currentVersion?: PreviewDocumentVersion; private _currentVersion?: PreviewDocumentVersion;
private isScrolling = false; private _isScrolling = false;
private imageInfo: { readonly id: string; readonly width: number; readonly height: number }[] = []; private _imageInfo: { readonly id: string; readonly width: number; readonly height: number }[] = [];
private readonly _fileWatchersBySrc = new Map</* src: */ string, vscode.FileSystemWatcher>(); private readonly _fileWatchersBySrc = new Map</* src: */ string, vscode.FileSystemWatcher>();
private readonly _onScrollEmitter = this._register(new vscode.EventEmitter<LastScrollLocation>()); private readonly _onScrollEmitter = this._register(new vscode.EventEmitter<LastScrollLocation>());
@ -114,7 +114,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
webview: vscode.WebviewPanel, webview: vscode.WebviewPanel,
resource: vscode.Uri, resource: vscode.Uri,
startingScroll: StartingScrollLocation | undefined, startingScroll: StartingScrollLocation | undefined,
private readonly delegate: MarkdownPreviewDelegate, private readonly _delegate: MarkdownPreviewDelegate,
private readonly _contentProvider: MdDocumentRenderer, private readonly _contentProvider: MdDocumentRenderer,
private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager,
private readonly _logger: ILogger, private readonly _logger: ILogger,
@ -129,12 +129,12 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
switch (startingScroll?.type) { switch (startingScroll?.type) {
case 'line': case 'line':
if (!isNaN(startingScroll.line!)) { if (!isNaN(startingScroll.line!)) {
this.line = startingScroll.line; this._line = startingScroll.line;
} }
break; break;
case 'fragment': case 'fragment':
this.scrollToFragment = startingScroll.fragment; this._scrollToFragment = startingScroll.fragment;
break; break;
} }
@ -171,19 +171,19 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
switch (e.type) { switch (e.type) {
case 'cacheImageSizes': case 'cacheImageSizes':
this.imageInfo = e.body; this._imageInfo = e.body;
break; break;
case 'revealLine': case 'revealLine':
this.onDidScrollPreview(e.body.line); this._onDidScrollPreview(e.body.line);
break; break;
case 'didClick': case 'didClick':
this.onDidClickPreview(e.body.line); this._onDidClickPreview(e.body.line);
break; break;
case 'openLink': case 'openLink':
this.onDidClickPreviewLink(e.body.href); this._onDidClickPreviewLink(e.body.href);
break; break;
case 'showPreviewSecuritySelector': case 'showPreviewSecuritySelector':
@ -209,7 +209,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
this._disposed = true; this._disposed = true;
clearTimeout(this.throttleTimer); clearTimeout(this._throttleTimer);
for (const entry of this._fileWatchersBySrc.values()) { for (const entry of this._fileWatchersBySrc.values()) {
entry.dispose(); entry.dispose();
} }
@ -223,10 +223,10 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
public get state() { public get state() {
return { return {
resource: this._resource.toString(), resource: this._resource.toString(),
line: this.line, line: this._line,
imageInfo: this.imageInfo, imageInfo: this._imageInfo,
fragment: this.scrollToFragment, fragment: this._scrollToFragment,
...this.delegate.getAdditionalState(), ...this._delegate.getAdditionalState(),
}; };
} }
@ -236,15 +236,15 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
*/ */
public refresh(forceUpdate: boolean = false) { public refresh(forceUpdate: boolean = false) {
// Schedule update if none is pending // Schedule update if none is pending
if (!this.throttleTimer) { if (!this._throttleTimer) {
if (this.firstUpdate) { if (this._firstUpdate) {
this.updatePreview(true); this._updatePreview(true);
} else { } else {
this.throttleTimer = setTimeout(() => this.updatePreview(forceUpdate), this.delay); this._throttleTimer = setTimeout(() => this._updatePreview(forceUpdate), this._delay);
} }
} }
this.firstUpdate = false; this._firstUpdate = false;
} }
@ -263,13 +263,13 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
return; return;
} }
if (this.isScrolling) { if (this._isScrolling) {
this.isScrolling = false; this._isScrolling = false;
return; return;
} }
this._logger.verbose('MarkdownPreview', 'updateForView', { markdownFile: this._resource }); this._logger.verbose('MarkdownPreview', 'updateForView', { markdownFile: this._resource });
this.line = topLine; this._line = topLine;
this.postMessage({ this.postMessage({
type: 'updateView', type: 'updateView',
line: topLine, line: topLine,
@ -277,9 +277,9 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
}); });
} }
private async updatePreview(forceUpdate?: boolean): Promise<void> { private async _updatePreview(forceUpdate?: boolean): Promise<void> {
clearTimeout(this.throttleTimer); clearTimeout(this._throttleTimer);
this.throttleTimer = undefined; this._throttleTimer = undefined;
if (this._disposed) { if (this._disposed) {
return; return;
@ -290,7 +290,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
document = await vscode.workspace.openTextDocument(this._resource); document = await vscode.workspace.openTextDocument(this._resource);
} catch { } catch {
if (!this._disposed) { if (!this._disposed) {
await this.showFileNotFoundError(); await this._showFileNotFoundError();
} }
return; return;
} }
@ -300,15 +300,15 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
} }
const pendingVersion = new PreviewDocumentVersion(document); const pendingVersion = new PreviewDocumentVersion(document);
if (!forceUpdate && this.currentVersion?.equals(pendingVersion)) { if (!forceUpdate && this._currentVersion?.equals(pendingVersion)) {
if (this.line) { if (this._line) {
this.scrollTo(this.line); this.scrollTo(this._line);
} }
return; return;
} }
const shouldReloadPage = forceUpdate || !this.currentVersion || this.currentVersion.resource.toString() !== pendingVersion.resource.toString() || !this._webviewPanel.visible; const shouldReloadPage = forceUpdate || !this._currentVersion || this._currentVersion.resource.toString() !== pendingVersion.resource.toString() || !this._webviewPanel.visible;
this.currentVersion = pendingVersion; this._currentVersion = pendingVersion;
let selectedLine: number | undefined = undefined; let selectedLine: number | undefined = undefined;
for (const editor of vscode.window.visibleTextEditors) { for (const editor of vscode.window.visibleTextEditors) {
@ -319,20 +319,20 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
} }
const content = await (shouldReloadPage const content = await (shouldReloadPage
? this._contentProvider.renderDocument(document, this, this._previewConfigurations, this.line, selectedLine, this.state, this._disposeCts.token) ? this._contentProvider.renderDocument(document, this, this._previewConfigurations, this._line, selectedLine, this.state, this._disposeCts.token)
: this._contentProvider.renderBody(document, this)); : this._contentProvider.renderBody(document, this));
// Another call to `doUpdate` may have happened. // Another call to `doUpdate` may have happened.
// Make sure we are still updating for the correct document // Make sure we are still updating for the correct document
if (this.currentVersion?.equals(pendingVersion)) { if (this._currentVersion?.equals(pendingVersion)) {
this.updateWebviewContent(content.html, shouldReloadPage); this._updateWebviewContent(content.html, shouldReloadPage);
this.updateImageWatchers(content.containingImages); this._updateImageWatchers(content.containingImages);
} }
} }
private onDidScrollPreview(line: number) { private _onDidScrollPreview(line: number) {
this.line = line; this._line = line;
this._onScrollEmitter.fire({ line: this.line, uri: this._resource }); this._onScrollEmitter.fire({ line: this._line, uri: this._resource });
const config = this._previewConfigurations.loadAndCacheConfiguration(this._resource); const config = this._previewConfigurations.loadAndCacheConfiguration(this._resource);
if (!config.scrollEditorWithPreview) { if (!config.scrollEditorWithPreview) {
return; return;
@ -343,12 +343,12 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
continue; continue;
} }
this.isScrolling = true; this._isScrolling = true;
scrollEditorToLine(line, editor); scrollEditorToLine(line, editor);
} }
} }
private async onDidClickPreview(line: number): Promise<void> { private async _onDidClickPreview(line: number): Promise<void> {
// fix #82457, find currently opened but unfocused source tab // fix #82457, find currently opened but unfocused source tab
await vscode.commands.executeCommand('markdown.showSource'); await vscode.commands.executeCommand('markdown.showSource');
@ -376,19 +376,19 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
}); });
} }
private async showFileNotFoundError() { private async _showFileNotFoundError() {
this._webviewPanel.webview.html = this._contentProvider.renderFileNotFoundDocument(this._resource); this._webviewPanel.webview.html = this._contentProvider.renderFileNotFoundDocument(this._resource);
} }
private updateWebviewContent(html: string, reloadPage: boolean): void { private _updateWebviewContent(html: string, reloadPage: boolean): void {
if (this._disposed) { if (this._disposed) {
return; return;
} }
if (this.delegate.getTitle) { if (this._delegate.getTitle) {
this._webviewPanel.title = this.delegate.getTitle(this._resource); this._webviewPanel.title = this._delegate.getTitle(this._resource);
} }
this._webviewPanel.webview.options = this.getWebviewOptions(); this._webviewPanel.webview.options = this._getWebviewOptions();
if (reloadPage) { if (reloadPage) {
this._webviewPanel.webview.html = html; this._webviewPanel.webview.html = html;
@ -401,7 +401,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
} }
} }
private updateImageWatchers(srcs: Set<string>) { private _updateImageWatchers(srcs: Set<string>) {
// Delete stale file watchers. // Delete stale file watchers.
for (const [src, watcher] of this._fileWatchersBySrc) { for (const [src, watcher] of this._fileWatchersBySrc) {
if (!srcs.has(src)) { if (!srcs.has(src)) {
@ -414,7 +414,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
const root = vscode.Uri.joinPath(this._resource, '../'); const root = vscode.Uri.joinPath(this._resource, '../');
for (const src of srcs) { for (const src of srcs) {
const uri = urlToUri(src, root); const uri = urlToUri(src, root);
if (uri && !MarkdownPreview.unwatchedImageSchemes.has(uri.scheme) && !this._fileWatchersBySrc.has(src)) { if (uri && !MarkdownPreview._unwatchedImageSchemes.has(uri.scheme) && !this._fileWatchersBySrc.has(src)) {
const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*')); const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*'));
watcher.onDidChange(() => { watcher.onDidChange(() => {
this.refresh(true); this.refresh(true);
@ -424,15 +424,15 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
} }
} }
private getWebviewOptions(): vscode.WebviewOptions { private _getWebviewOptions(): vscode.WebviewOptions {
return { return {
enableScripts: true, enableScripts: true,
enableForms: false, enableForms: false,
localResourceRoots: this.getLocalResourceRoots() localResourceRoots: this._getLocalResourceRoots()
}; };
} }
private getLocalResourceRoots(): ReadonlyArray<vscode.Uri> { private _getLocalResourceRoots(): ReadonlyArray<vscode.Uri> {
const baseRoots = Array.from(this._contributionProvider.contributions.previewResourceRoots); const baseRoots = Array.from(this._contributionProvider.contributions.previewResourceRoots);
const folder = vscode.workspace.getWorkspaceFolder(this._resource); const folder = vscode.workspace.getWorkspaceFolder(this._resource);
@ -448,7 +448,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
return baseRoots; return baseRoots;
} }
private async onDidClickPreviewLink(href: string) { private async _onDidClickPreviewLink(href: string) {
const config = vscode.workspace.getConfiguration('markdown', this.resource); const config = vscode.workspace.getConfiguration('markdown', this.resource);
const openLinks = config.get<string>('preview.openMarkdownLinks', 'inPreview'); const openLinks = config.get<string>('preview.openMarkdownLinks', 'inPreview');
if (openLinks === 'inPreview') { if (openLinks === 'inPreview') {
@ -457,7 +457,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
try { try {
const doc = await vscode.workspace.openTextDocument(vscode.Uri.from(resolved.uri)); const doc = await vscode.workspace.openTextDocument(vscode.Uri.from(resolved.uri));
if (isMarkdownFile(doc)) { if (isMarkdownFile(doc)) {
return this.delegate.openPreviewLinkToMarkdownFile(doc.uri, resolved.fragment ?? ''); return this._delegate.openPreviewLinkToMarkdownFile(doc.uri, resolved.fragment ?? '');
} }
} catch { } catch {
// Noop // Noop
@ -519,7 +519,7 @@ export class StaticMarkdownPreview extends Disposable implements IManagedMarkdow
return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, topmostLineMonitor, logger, contributionProvider, opener, scrollLine); return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, topmostLineMonitor, logger, contributionProvider, opener, scrollLine);
} }
private readonly preview: MarkdownPreview; private readonly _preview: MarkdownPreview;
private constructor( private constructor(
private readonly _webviewPanel: vscode.WebviewPanel, private readonly _webviewPanel: vscode.WebviewPanel,
@ -534,7 +534,7 @@ export class StaticMarkdownPreview extends Disposable implements IManagedMarkdow
) { ) {
super(); super();
const topScrollLocation = scrollLine ? new StartingScrollLine(scrollLine) : undefined; const topScrollLocation = scrollLine ? new StartingScrollLine(scrollLine) : undefined;
this.preview = this._register(new MarkdownPreview(this._webviewPanel, resource, topScrollLocation, { this._preview = this._register(new MarkdownPreview(this._webviewPanel, resource, topScrollLocation, {
getAdditionalState: () => { return {}; }, getAdditionalState: () => { return {}; },
openPreviewLinkToMarkdownFile: (markdownLink, fragment) => { openPreviewLinkToMarkdownFile: (markdownLink, fragment) => {
return vscode.commands.executeCommand('vscode.openWith', markdownLink.with({ return vscode.commands.executeCommand('vscode.openWith', markdownLink.with({
@ -551,13 +551,13 @@ export class StaticMarkdownPreview extends Disposable implements IManagedMarkdow
this._onDidChangeViewState.fire(e); this._onDidChangeViewState.fire(e);
})); }));
this._register(this.preview.onScroll((scrollInfo) => { this._register(this._preview.onScroll((scrollInfo) => {
topmostLineMonitor.setPreviousStaticEditorLine(scrollInfo); topmostLineMonitor.setPreviousStaticEditorLine(scrollInfo);
})); }));
this._register(topmostLineMonitor.onDidChanged(event => { this._register(topmostLineMonitor.onDidChanged(event => {
if (this.preview.isPreviewOf(event.resource)) { if (this._preview.isPreviewOf(event.resource)) {
this.preview.scrollTo(event.line); this._preview.scrollTo(event.line);
} }
})); }));
} }
@ -582,17 +582,17 @@ export class StaticMarkdownPreview extends Disposable implements IManagedMarkdow
} }
public refresh() { public refresh() {
this.preview.refresh(true); this._preview.refresh(true);
} }
public updateConfiguration() { public updateConfiguration() {
if (this._previewConfigurations.hasConfigurationChanged(this.preview.resource)) { if (this._previewConfigurations.hasConfigurationChanged(this._preview.resource)) {
this.refresh(); this.refresh();
} }
} }
public get resource() { public get resource() {
return this.preview.resource; return this._preview.resource;
} }
public get resourceColumn() { public get resourceColumn() {
@ -645,7 +645,7 @@ export class DynamicMarkdownPreview extends Disposable implements IManagedMarkdo
): DynamicMarkdownPreview { ): DynamicMarkdownPreview {
const webview = vscode.window.createWebviewPanel( const webview = vscode.window.createWebviewPanel(
DynamicMarkdownPreview.viewType, DynamicMarkdownPreview.viewType,
DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked), DynamicMarkdownPreview._getPreviewTitle(input.resource, input.locked),
previewColumn, { enableFindWidget: true, }); previewColumn, { enableFindWidget: true, });
webview.iconPath = contentProvider.iconPath; webview.iconPath = contentProvider.iconPath;
@ -671,7 +671,7 @@ export class DynamicMarkdownPreview extends Disposable implements IManagedMarkdo
this._resourceColumn = input.resourceColumn; this._resourceColumn = input.resourceColumn;
this._locked = input.locked; this._locked = input.locked;
this._preview = this.createPreview(input.resource, typeof input.line === 'number' ? new StartingScrollLine(input.line) : undefined); this._preview = this._createPreview(input.resource, typeof input.line === 'number' ? new StartingScrollLine(input.line) : undefined);
this._register(webview.onDidDispose(() => { this.dispose(); })); this._register(webview.onDidDispose(() => { this.dispose(); }));
@ -762,15 +762,15 @@ export class DynamicMarkdownPreview extends Disposable implements IManagedMarkdo
} }
this._preview.dispose(); this._preview.dispose();
this._preview = this.createPreview(newResource, scrollLocation); this._preview = this._createPreview(newResource, scrollLocation);
} }
public toggleLock() { public toggleLock() {
this._locked = !this._locked; this._locked = !this._locked;
this._webviewPanel.title = DynamicMarkdownPreview.getPreviewTitle(this._preview.resource, this._locked); this._webviewPanel.title = DynamicMarkdownPreview._getPreviewTitle(this._preview.resource, this._locked);
} }
private static getPreviewTitle(resource: vscode.Uri, locked: boolean): string { private static _getPreviewTitle(resource: vscode.Uri, locked: boolean): string {
const resourceLabel = uri.Utils.basename(resource); const resourceLabel = uri.Utils.basename(resource);
return locked return locked
? localize('lockedPreviewTitle', '[Preview] {0}', resourceLabel) ? localize('lockedPreviewTitle', '[Preview] {0}', resourceLabel)
@ -801,9 +801,9 @@ export class DynamicMarkdownPreview extends Disposable implements IManagedMarkdo
return this.matchesResource(otherPreview._preview.resource, otherPreview.position, otherPreview._locked); return this.matchesResource(otherPreview._preview.resource, otherPreview.position, otherPreview._locked);
} }
private createPreview(resource: vscode.Uri, startingScroll?: StartingScrollLocation): MarkdownPreview { private _createPreview(resource: vscode.Uri, startingScroll?: StartingScrollLocation): MarkdownPreview {
return new MarkdownPreview(this._webviewPanel, resource, startingScroll, { return new MarkdownPreview(this._webviewPanel, resource, startingScroll, {
getTitle: (resource) => DynamicMarkdownPreview.getPreviewTitle(resource, this._locked), getTitle: (resource) => DynamicMarkdownPreview._getPreviewTitle(resource, this._locked),
getAdditionalState: () => { getAdditionalState: () => {
return { return {
resourceColumn: this.resourceColumn, resourceColumn: this.resourceColumn,

View file

@ -65,26 +65,26 @@ export class MarkdownPreviewConfiguration {
} }
export class MarkdownPreviewConfigurationManager { export class MarkdownPreviewConfigurationManager {
private readonly previewConfigurationsForWorkspaces = new Map<string, MarkdownPreviewConfiguration>(); private readonly _previewConfigurationsForWorkspaces = new Map<string, MarkdownPreviewConfiguration>();
public loadAndCacheConfiguration( public loadAndCacheConfiguration(
resource: vscode.Uri resource: vscode.Uri
): MarkdownPreviewConfiguration { ): MarkdownPreviewConfiguration {
const config = MarkdownPreviewConfiguration.getForResource(resource); const config = MarkdownPreviewConfiguration.getForResource(resource);
this.previewConfigurationsForWorkspaces.set(this.getKey(resource), config); this._previewConfigurationsForWorkspaces.set(this._getKey(resource), config);
return config; return config;
} }
public hasConfigurationChanged( public hasConfigurationChanged(
resource: vscode.Uri resource: vscode.Uri
): boolean { ): boolean {
const key = this.getKey(resource); const key = this._getKey(resource);
const currentConfig = this.previewConfigurationsForWorkspaces.get(key); const currentConfig = this._previewConfigurationsForWorkspaces.get(key);
const newConfig = MarkdownPreviewConfiguration.getForResource(resource); const newConfig = MarkdownPreviewConfiguration.getForResource(resource);
return (!currentConfig || !currentConfig.isEqualTo(newConfig)); return (!currentConfig || !currentConfig.isEqualTo(newConfig));
} }
private getKey( private _getKey(
resource: vscode.Uri resource: vscode.Uri
): string { ): string {
const folder = vscode.workspace.getWorkspaceFolder(resource); const folder = vscode.workspace.getWorkspaceFolder(resource);

View file

@ -42,7 +42,7 @@ class PreviewStore<T extends IManagedMarkdownPreview> extends Disposable {
} }
public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): T | undefined { public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): T | undefined {
const previewColumn = this.resolvePreviewColumn(previewSettings); const previewColumn = this._resolvePreviewColumn(previewSettings);
for (const preview of this._previews) { for (const preview of this._previews) {
if (preview.matchesResource(resource, previewColumn, previewSettings.locked)) { if (preview.matchesResource(resource, previewColumn, previewSettings.locked)) {
return preview; return preview;
@ -59,7 +59,7 @@ class PreviewStore<T extends IManagedMarkdownPreview> extends Disposable {
this._previews.delete(preview); this._previews.delete(preview);
} }
private resolvePreviewColumn(previewSettings: DynamicPreviewSettings): vscode.ViewColumn | undefined { private _resolvePreviewColumn(previewSettings: DynamicPreviewSettings): vscode.ViewColumn | undefined {
if (previewSettings.previewColumn === vscode.ViewColumn.Active) { if (previewSettings.previewColumn === vscode.ViewColumn.Active) {
return vscode.window.tabGroups.activeTabGroup.viewColumn; return vscode.window.tabGroups.activeTabGroup.viewColumn;
} }
@ -133,7 +133,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
if (preview) { if (preview) {
preview.reveal(settings.previewColumn); preview.reveal(settings.previewColumn);
} else { } else {
preview = this.createNewDynamicPreview(resource, settings); preview = this._createNewDynamicPreview(resource, settings);
} }
preview.update( preview.update(
@ -184,7 +184,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
this._contributions, this._contributions,
this._opener); this._opener);
this.registerDynamicPreview(preview); this._registerDynamicPreview(preview);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -238,10 +238,10 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
this._opener, this._opener,
lineNumber lineNumber
); );
this.registerStaticPreview(preview); this._registerStaticPreview(preview);
} }
private createNewDynamicPreview( private _createNewDynamicPreview(
resource: vscode.Uri, resource: vscode.Uri,
previewSettings: DynamicPreviewSettings previewSettings: DynamicPreviewSettings
): DynamicMarkdownPreview { ): DynamicMarkdownPreview {
@ -263,17 +263,17 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
this._opener); this._opener);
this._activePreview = preview; this._activePreview = preview;
return this.registerDynamicPreview(preview); return this._registerDynamicPreview(preview);
} }
private registerDynamicPreview(preview: DynamicMarkdownPreview): DynamicMarkdownPreview { private _registerDynamicPreview(preview: DynamicMarkdownPreview): DynamicMarkdownPreview {
this._dynamicPreviews.add(preview); this._dynamicPreviews.add(preview);
preview.onDispose(() => { preview.onDispose(() => {
this._dynamicPreviews.delete(preview); this._dynamicPreviews.delete(preview);
}); });
this.trackActive(preview); this._trackActive(preview);
preview.onDidChangeViewState(() => { preview.onDidChangeViewState(() => {
// Remove other dynamic previews in our column // Remove other dynamic previews in our column
@ -282,18 +282,18 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
return preview; return preview;
} }
private registerStaticPreview(preview: StaticMarkdownPreview): StaticMarkdownPreview { private _registerStaticPreview(preview: StaticMarkdownPreview): StaticMarkdownPreview {
this._staticPreviews.add(preview); this._staticPreviews.add(preview);
preview.onDispose(() => { preview.onDispose(() => {
this._staticPreviews.delete(preview); this._staticPreviews.delete(preview);
}); });
this.trackActive(preview); this._trackActive(preview);
return preview; return preview;
} }
private trackActive(preview: IManagedMarkdownPreview): void { private _trackActive(preview: IManagedMarkdownPreview): void {
preview.onDidChangeViewState(({ webviewPanel }) => { preview.onDidChangeViewState(({ webviewPanel }) => {
this._activePreview = webviewPanel.active ? preview : undefined; this._activePreview = webviewPanel.active ? preview : undefined;
}); });

View file

@ -31,31 +31,31 @@ export interface ContentSecurityPolicyArbiter {
} }
export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPolicyArbiter { export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPolicyArbiter {
private readonly old_trusted_workspace_key = 'trusted_preview_workspace:'; private readonly _old_trusted_workspace_key = 'trusted_preview_workspace:';
private readonly security_level_key = 'preview_security_level:'; private readonly _security_level_key = 'preview_security_level:';
private readonly should_disable_security_warning_key = 'preview_should_show_security_warning:'; private readonly _should_disable_security_warning_key = 'preview_should_show_security_warning:';
constructor( constructor(
private readonly globalState: vscode.Memento, private readonly _globalState: vscode.Memento,
private readonly workspaceState: vscode.Memento private readonly _workspaceState: vscode.Memento
) { } ) { }
public getSecurityLevelForResource(resource: vscode.Uri): MarkdownPreviewSecurityLevel { public getSecurityLevelForResource(resource: vscode.Uri): MarkdownPreviewSecurityLevel {
// Use new security level setting first // Use new security level setting first
const level = this.globalState.get<MarkdownPreviewSecurityLevel | undefined>(this.security_level_key + this.getRoot(resource), undefined); const level = this._globalState.get<MarkdownPreviewSecurityLevel | undefined>(this._security_level_key + this._getRoot(resource), undefined);
if (typeof level !== 'undefined') { if (typeof level !== 'undefined') {
return level; return level;
} }
// Fallback to old trusted workspace setting // Fallback to old trusted workspace setting
if (this.globalState.get<boolean>(this.old_trusted_workspace_key + this.getRoot(resource), false)) { if (this._globalState.get<boolean>(this._old_trusted_workspace_key + this._getRoot(resource), false)) {
return MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent; return MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent;
} }
return MarkdownPreviewSecurityLevel.Strict; return MarkdownPreviewSecurityLevel.Strict;
} }
public setSecurityLevelForResource(resource: vscode.Uri, level: MarkdownPreviewSecurityLevel): Thenable<void> { public setSecurityLevelForResource(resource: vscode.Uri, level: MarkdownPreviewSecurityLevel): Thenable<void> {
return this.globalState.update(this.security_level_key + this.getRoot(resource), level); return this._globalState.update(this._security_level_key + this._getRoot(resource), level);
} }
public shouldAllowSvgsForResource(resource: vscode.Uri) { public shouldAllowSvgsForResource(resource: vscode.Uri) {
@ -64,14 +64,14 @@ export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPol
} }
public shouldDisableSecurityWarnings(): boolean { public shouldDisableSecurityWarnings(): boolean {
return this.workspaceState.get<boolean>(this.should_disable_security_warning_key, false); return this._workspaceState.get<boolean>(this._should_disable_security_warning_key, false);
} }
public setShouldDisableSecurityWarning(disabled: boolean): Thenable<void> { public setShouldDisableSecurityWarning(disabled: boolean): Thenable<void> {
return this.workspaceState.update(this.should_disable_security_warning_key, disabled); return this._workspaceState.update(this._should_disable_security_warning_key, disabled);
} }
private getRoot(resource: vscode.Uri): vscode.Uri { private _getRoot(resource: vscode.Uri): vscode.Uri {
if (vscode.workspace.workspaceFolders) { if (vscode.workspace.workspaceFolders) {
const folderForResource = vscode.workspace.getWorkspaceFolder(resource); const folderForResource = vscode.workspace.getWorkspaceFolder(resource);
if (folderForResource) { if (folderForResource) {
@ -90,8 +90,8 @@ export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPol
export class PreviewSecuritySelector { export class PreviewSecuritySelector {
public constructor( public constructor(
private readonly cspArbiter: ContentSecurityPolicyArbiter, private readonly _cspArbiter: ContentSecurityPolicyArbiter,
private readonly webviewManager: MarkdownPreviewManager private readonly _webviewManager: MarkdownPreviewManager
) { } ) { }
public async showSecuritySelectorForResource(resource: vscode.Uri): Promise<void> { public async showSecuritySelectorForResource(resource: vscode.Uri): Promise<void> {
@ -103,7 +103,7 @@ export class PreviewSecuritySelector {
return when ? '• ' : ''; return when ? '• ' : '';
} }
const currentSecurityLevel = this.cspArbiter.getSecurityLevelForResource(resource); const currentSecurityLevel = this._cspArbiter.getSecurityLevelForResource(resource);
const selection = await vscode.window.showQuickPick<PreviewSecurityPickItem>( const selection = await vscode.window.showQuickPick<PreviewSecurityPickItem>(
[ [
{ {
@ -128,7 +128,7 @@ export class PreviewSecuritySelector {
description: '' description: ''
}, { }, {
type: 'toggle', type: 'toggle',
label: this.cspArbiter.shouldDisableSecurityWarnings() label: this._cspArbiter.shouldDisableSecurityWarnings()
? localize('enableSecurityWarning.title', "Enable preview security warnings in this workspace") ? localize('enableSecurityWarning.title', "Enable preview security warnings in this workspace")
: localize('disableSecurityWarning.title', "Disable preview security warning in this workspace"), : localize('disableSecurityWarning.title', "Disable preview security warning in this workspace"),
description: localize('toggleSecurityWarning.description', 'Does not affect the content security level') description: localize('toggleSecurityWarning.description', 'Does not affect the content security level')
@ -148,12 +148,12 @@ export class PreviewSecuritySelector {
} }
if (selection.type === 'toggle') { if (selection.type === 'toggle') {
this.cspArbiter.setShouldDisableSecurityWarning(!this.cspArbiter.shouldDisableSecurityWarnings()); this._cspArbiter.setShouldDisableSecurityWarning(!this._cspArbiter.shouldDisableSecurityWarnings());
this.webviewManager.refresh(); this._webviewManager.refresh();
return; return;
} else { } else {
await this.cspArbiter.setSecurityLevelForResource(resource, selection.type); await this._cspArbiter.setSecurityLevelForResource(resource, selection.type);
} }
this.webviewManager.refresh(); this._webviewManager.refresh();
} }
} }

View file

@ -15,10 +15,10 @@ export interface LastScrollLocation {
export class TopmostLineMonitor extends Disposable { export class TopmostLineMonitor extends Disposable {
private readonly pendingUpdates = new ResourceMap<number>(); private readonly _pendingUpdates = new ResourceMap<number>();
private readonly throttle = 50; private readonly _throttle = 50;
private previousTextEditorInfo = new ResourceMap<LastScrollLocation>(); private _previousTextEditorInfo = new ResourceMap<LastScrollLocation>();
private previousStaticEditorInfo = new ResourceMap<LastScrollLocation>(); private _previousStaticEditorInfo = new ResourceMap<LastScrollLocation>();
constructor() { constructor() {
super(); super();
@ -43,28 +43,28 @@ export class TopmostLineMonitor extends Disposable {
public readonly onDidChanged = this._onChanged.event; public readonly onDidChanged = this._onChanged.event;
public setPreviousStaticEditorLine(scrollLocation: LastScrollLocation): void { public setPreviousStaticEditorLine(scrollLocation: LastScrollLocation): void {
this.previousStaticEditorInfo.set(scrollLocation.uri, scrollLocation); this._previousStaticEditorInfo.set(scrollLocation.uri, scrollLocation);
} }
public getPreviousStaticEditorLineByUri(resource: vscode.Uri): number | undefined { public getPreviousStaticEditorLineByUri(resource: vscode.Uri): number | undefined {
const scrollLoc = this.previousStaticEditorInfo.get(resource); const scrollLoc = this._previousStaticEditorInfo.get(resource);
this.previousStaticEditorInfo.delete(resource); this._previousStaticEditorInfo.delete(resource);
return scrollLoc?.line; return scrollLoc?.line;
} }
public setPreviousTextEditorLine(scrollLocation: LastScrollLocation): void { public setPreviousTextEditorLine(scrollLocation: LastScrollLocation): void {
this.previousTextEditorInfo.set(scrollLocation.uri, scrollLocation); this._previousTextEditorInfo.set(scrollLocation.uri, scrollLocation);
} }
public getPreviousTextEditorLineByUri(resource: vscode.Uri): number | undefined { public getPreviousTextEditorLineByUri(resource: vscode.Uri): number | undefined {
const scrollLoc = this.previousTextEditorInfo.get(resource); const scrollLoc = this._previousTextEditorInfo.get(resource);
this.previousTextEditorInfo.delete(resource); this._previousTextEditorInfo.delete(resource);
return scrollLoc?.line; return scrollLoc?.line;
} }
public getPreviousStaticTextEditorLineByUri(resource: vscode.Uri): number | undefined { public getPreviousStaticTextEditorLineByUri(resource: vscode.Uri): number | undefined {
const state = this.previousStaticEditorInfo.get(resource); const state = this._previousStaticEditorInfo.get(resource);
return state?.line; return state?.line;
} }
@ -72,20 +72,20 @@ export class TopmostLineMonitor extends Disposable {
resource: vscode.Uri, resource: vscode.Uri,
line: number line: number
) { ) {
if (!this.pendingUpdates.has(resource)) { if (!this._pendingUpdates.has(resource)) {
// schedule update // schedule update
setTimeout(() => { setTimeout(() => {
if (this.pendingUpdates.has(resource)) { if (this._pendingUpdates.has(resource)) {
this._onChanged.fire({ this._onChanged.fire({
resource, resource,
line: this.pendingUpdates.get(resource) as number line: this._pendingUpdates.get(resource) as number
}); });
this.pendingUpdates.delete(resource); this._pendingUpdates.delete(resource);
} }
}, this.throttle); }, this._throttle);
} }
this.pendingUpdates.set(resource, line); this._pendingUpdates.set(resource, line);
} }
} }

View file

@ -10,55 +10,55 @@ export interface ITask<T> {
export class Delayer<T> { export class Delayer<T> {
public defaultDelay: number; public defaultDelay: number;
private timeout: any; // Timer private _timeout: any; // Timer
private completionPromise: Promise<T | null> | null; private _cancelTimeout: Promise<T | null> | null;
private onSuccess: ((value: T | PromiseLike<T> | undefined) => void) | null; private _onSuccess: ((value: T | PromiseLike<T> | undefined) => void) | null;
private task: ITask<T> | null; private _task: ITask<T> | null;
constructor(defaultDelay: number) { constructor(defaultDelay: number) {
this.defaultDelay = defaultDelay; this.defaultDelay = defaultDelay;
this.timeout = null; this._timeout = null;
this.completionPromise = null; this._cancelTimeout = null;
this.onSuccess = null; this._onSuccess = null;
this.task = null; this._task = null;
} }
dispose() { dispose() {
this.cancelTimeout(); this._doCancelTimeout();
} }
public trigger(task: ITask<T>, delay: number = this.defaultDelay): Promise<T | null> { public trigger(task: ITask<T>, delay: number = this.defaultDelay): Promise<T | null> {
this.task = task; this._task = task;
if (delay >= 0) { if (delay >= 0) {
this.cancelTimeout(); this._doCancelTimeout();
} }
if (!this.completionPromise) { if (!this._cancelTimeout) {
this.completionPromise = new Promise<T | undefined>((resolve) => { this._cancelTimeout = new Promise<T | undefined>((resolve) => {
this.onSuccess = resolve; this._onSuccess = resolve;
}).then(() => { }).then(() => {
this.completionPromise = null; this._cancelTimeout = null;
this.onSuccess = null; this._onSuccess = null;
const result = this.task && this.task(); const result = this._task && this._task?.();
this.task = null; this._task = null;
return result; return result;
}); });
} }
if (delay >= 0 || this.timeout === null) { if (delay >= 0 || this._timeout === null) {
this.timeout = setTimeout(() => { this._timeout = setTimeout(() => {
this.timeout = null; this._timeout = null;
this.onSuccess?.(undefined); this._onSuccess?.(undefined);
}, delay >= 0 ? delay : this.defaultDelay); }, delay >= 0 ? delay : this.defaultDelay);
} }
return this.completionPromise; return this._cancelTimeout;
} }
private cancelTimeout(): void { private _doCancelTimeout(): void {
if (this.timeout !== null) { if (this._timeout !== null) {
clearTimeout(this.timeout); clearTimeout(this._timeout);
this.timeout = null; this._timeout = null;
} }
} }
} }

View file

@ -5,8 +5,8 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
export const noopToken = new class implements vscode.CancellationToken { export const noopToken: vscode.CancellationToken = new class implements vscode.CancellationToken {
_onCancellationRequestedEmitter = new vscode.EventEmitter<void>(); private readonly _onCancellationRequestedEmitter = new vscode.EventEmitter<void>();
onCancellationRequested = this._onCancellationRequestedEmitter.event; onCancellationRequested = this._onCancellationRequestedEmitter.event;
get isCancellationRequested() { return false; } get isCancellationRequested() { return false; }

View file

@ -15,15 +15,15 @@ enum OpenMarkdownLinks {
export class MdLinkOpener { export class MdLinkOpener {
constructor( constructor(
private readonly client: MdLanguageClient, private readonly _client: MdLanguageClient,
) { } ) { }
public async resolveDocumentLink(linkText: string, fromResource: vscode.Uri): Promise<proto.ResolvedDocumentLinkTarget> { public async resolveDocumentLink(linkText: string, fromResource: vscode.Uri): Promise<proto.ResolvedDocumentLinkTarget> {
return this.client.resolveLinkTarget(linkText, fromResource); return this._client.resolveLinkTarget(linkText, fromResource);
} }
public async openDocumentLink(linkText: string, fromResource: vscode.Uri, viewColumn?: vscode.ViewColumn): Promise<void> { public async openDocumentLink(linkText: string, fromResource: vscode.Uri, viewColumn?: vscode.ViewColumn): Promise<void> {
const resolved = await this.client.resolveLinkTarget(linkText, fromResource); const resolved = await this._client.resolveLinkTarget(linkText, fromResource);
if (!resolved) { if (!resolved) {
return; return;
} }

View file

@ -11,53 +11,53 @@ const defaultResourceToKey = (resource: vscode.Uri): string => resource.toString
export class ResourceMap<T> { export class ResourceMap<T> {
private readonly map = new Map<string, { readonly uri: vscode.Uri; readonly value: T }>(); private readonly _map = new Map<string, { readonly uri: vscode.Uri; readonly value: T }>();
private readonly toKey: ResourceToKey; private readonly _toKey: ResourceToKey;
constructor(toKey: ResourceToKey = defaultResourceToKey) { constructor(toKey: ResourceToKey = defaultResourceToKey) {
this.toKey = toKey; this._toKey = toKey;
} }
public set(uri: vscode.Uri, value: T): this { public set(uri: vscode.Uri, value: T): this {
this.map.set(this.toKey(uri), { uri, value }); this._map.set(this._toKey(uri), { uri, value });
return this; return this;
} }
public get(resource: vscode.Uri): T | undefined { public get(resource: vscode.Uri): T | undefined {
return this.map.get(this.toKey(resource))?.value; return this._map.get(this._toKey(resource))?.value;
} }
public has(resource: vscode.Uri): boolean { public has(resource: vscode.Uri): boolean {
return this.map.has(this.toKey(resource)); return this._map.has(this._toKey(resource));
} }
public get size(): number { public get size(): number {
return this.map.size; return this._map.size;
} }
public clear(): void { public clear(): void {
this.map.clear(); this._map.clear();
} }
public delete(resource: vscode.Uri): boolean { public delete(resource: vscode.Uri): boolean {
return this.map.delete(this.toKey(resource)); return this._map.delete(this._toKey(resource));
} }
public *values(): IterableIterator<T> { public *values(): IterableIterator<T> {
for (const entry of this.map.values()) { for (const entry of this._map.values()) {
yield entry.value; yield entry.value;
} }
} }
public *keys(): IterableIterator<vscode.Uri> { public *keys(): IterableIterator<vscode.Uri> {
for (const entry of this.map.values()) { for (const entry of this._map.values()) {
yield entry.uri; yield entry.uri;
} }
} }
public *entries(): IterableIterator<[vscode.Uri, T]> { public *entries(): IterableIterator<[vscode.Uri, T]> {
for (const entry of this.map.values()) { for (const entry of this._map.values()) {
yield [entry.uri, entry.value]; yield [entry.uri, entry.value];
} }
} }