Skip to content

Commit

Permalink
Make markdown extension points dynamic
Browse files Browse the repository at this point in the history
  • Loading branch information
mjbvz committed Feb 6, 2019
1 parent 8f10101 commit 038e8d3
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 11 deletions.
4 changes: 4 additions & 0 deletions extensions/markdown-language-features/src/features/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ export class MarkdownPreview extends Disposable {
this._onDidChangeViewStateEmitter.fire(e);
}, null, this._disposables);

_contributionProvider.onContributionsChanged(() => {
setImmediate(() => this.refresh());
}, null, this._disposables);

this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => {
if (e.source !== this._resource.toString()) {
return;
Expand Down
9 changes: 7 additions & 2 deletions extensions/markdown-language-features/src/markdownEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,19 @@ export class MarkdownEngine {
public constructor(
private readonly contributionProvider: MarkdownContributionProvider,
private readonly slugifier: Slugifier,
) { }
) {
contributionProvider.onContributionsChanged(() => {
// Markdown plugin contributions may have changed
this.md = undefined;
});
}

private async getEngine(config: MarkdownItConfig): Promise<MarkdownIt> {
if (!this.md) {
this.md = import('markdown-it').then(async markdownIt => {
let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md));

for (const plugin of this.contributionProvider.contributions.markdownItPlugins) {
for (const plugin of this.contributionProvider.contributions.markdownItPlugins.values()) {
try {
md = (await plugin)(md);
} catch {
Expand Down
50 changes: 41 additions & 9 deletions extensions/markdown-language-features/src/markdownExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import * as vscode from 'vscode';
import * as path from 'path';
import { Disposable } from './util/dispose';
import * as arrays from './util/arrays';

const resolveExtensionResource = (extension: vscode.Extension<any>, resourcePath: string): vscode.Uri => {
return vscode.Uri.file(path.join(extension.extensionPath, resourcePath))
Expand All @@ -29,26 +31,37 @@ export interface MarkdownContributions {
readonly previewScripts: ReadonlyArray<vscode.Uri>;
readonly previewStyles: ReadonlyArray<vscode.Uri>;
readonly previewResourceRoots: ReadonlyArray<vscode.Uri>;
readonly markdownItPlugins: ReadonlyArray<Thenable<(md: any) => any>>;
readonly markdownItPlugins: Map<string, Thenable<(md: any) => any>>;
}

export namespace MarkdownContributions {
export const Empty: MarkdownContributions = {
previewScripts: [],
previewStyles: [],
previewResourceRoots: [],
markdownItPlugins: []
markdownItPlugins: new Map()
};

export function merge(a: MarkdownContributions, b: MarkdownContributions): MarkdownContributions {
return {
previewScripts: [...a.previewScripts, ...b.previewScripts],
previewStyles: [...a.previewStyles, ...b.previewStyles],
previewResourceRoots: [...a.previewResourceRoots, ...b.previewResourceRoots],
markdownItPlugins: [...a.markdownItPlugins, ...b.markdownItPlugins],
markdownItPlugins: new Map([...a.markdownItPlugins.entries(), ...b.markdownItPlugins.entries()]),
};
}

function uriEqual(a: vscode.Uri, b: vscode.Uri): boolean {
return a.toString() === b.toString();
}

export function equal(a: MarkdownContributions, b: MarkdownContributions): boolean {
return arrays.equals(a.previewScripts, b.previewScripts, uriEqual)
&& arrays.equals(a.previewStyles, b.previewStyles, uriEqual)
&& arrays.equals(a.previewResourceRoots, b.previewResourceRoots, uriEqual)
&& arrays.equals(Array.from(a.markdownItPlugins.keys()), Array.from(b.markdownItPlugins.keys()));
}

export function fromExtension(
extension: vscode.Extension<any>
): MarkdownContributions {
Expand All @@ -69,7 +82,7 @@ export namespace MarkdownContributions {
previewScripts: scripts,
previewStyles: styles,
previewResourceRoots,
markdownItPlugins: plugins ? [plugins] : []
markdownItPlugins: plugins ? new Map([[extension.id, plugins]]) : new Map()
};
}

Expand Down Expand Up @@ -106,23 +119,42 @@ export namespace MarkdownContributions {
export interface MarkdownContributionProvider {
readonly extensionPath: string;
readonly contributions: MarkdownContributions;
readonly onContributionsChanged: vscode.Event<this>;
}

class VSCodeExtensionMarkdownContributionProvider implements MarkdownContributionProvider {
class VSCodeExtensionMarkdownContributionProvider extends Disposable implements MarkdownContributionProvider {
private _contributions?: MarkdownContributions;

public constructor(
public readonly extensionPath: string,
) { }
) {
super();

vscode.extensions.onDidChange(() => {
const currentContributions = this.getCurrentContributions();
const existingContributions = this._contributions || MarkdownContributions.Empty;
if (!MarkdownContributions.equal(existingContributions, currentContributions)) {
this._contributions = currentContributions;
this._onContributionsChanged.fire(this);
}
}, undefined, this._disposables);
}

private readonly _onContributionsChanged = new vscode.EventEmitter<this>();
public readonly onContributionsChanged = this._onContributionsChanged.event;

public get contributions(): MarkdownContributions {
if (!this._contributions) {
this._contributions = vscode.extensions.all.reduce(
(contributions, extension) => MarkdownContributions.merge(contributions, MarkdownContributions.fromExtension(extension)),
MarkdownContributions.Empty);
this._contributions = this.getCurrentContributions();
}
return this._contributions;
}

private getCurrentContributions(): MarkdownContributions {
return vscode.extensions.all
.map(MarkdownContributions.fromExtension)
.reduce(MarkdownContributions.merge, MarkdownContributions.Empty);
}
}

export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributionProvider {
Expand Down
2 changes: 2 additions & 0 deletions extensions/markdown-language-features/src/test/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { MarkdownEngine } from '../markdownEngine';
import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions';
import { githubSlugifier } from '../slugify';

const emptyContributions = new class implements MarkdownContributionProvider {
readonly extensionPath = '';
readonly contributions = MarkdownContributions.Empty;
readonly onContributionsChanged = new vscode.EventEmitter<this>().event;
};

export function createNewMarkdownEngine(): MarkdownEngine {
Expand Down
18 changes: 18 additions & 0 deletions extensions/markdown-language-features/src/util/arrays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

export function equals<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
if (one.length !== other.length) {
return false;
}

for (let i = 0, len = one.length; i < len; i++) {
if (!itemEquals(one[i], other[i])) {
return false;
}
}

return true;
}

0 comments on commit 038e8d3

Please sign in to comment.