Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/markdown-language-features/src/markdownExtensions.ts
3292 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as vscode from 'vscode';
7
import * as arrays from './util/arrays';
8
import { Disposable } from './util/dispose';
9
10
function resolveExtensionResource(extension: vscode.Extension<any>, resourcePath: string): vscode.Uri {
11
return vscode.Uri.joinPath(extension.extensionUri, resourcePath);
12
}
13
14
function* resolveExtensionResources(extension: vscode.Extension<any>, resourcePaths: unknown): Iterable<vscode.Uri> {
15
if (Array.isArray(resourcePaths)) {
16
for (const resource of resourcePaths) {
17
try {
18
yield resolveExtensionResource(extension, resource);
19
} catch {
20
// noop
21
}
22
}
23
}
24
}
25
26
export interface MarkdownContributions {
27
readonly previewScripts: readonly vscode.Uri[];
28
readonly previewStyles: readonly vscode.Uri[];
29
readonly previewResourceRoots: readonly vscode.Uri[];
30
readonly markdownItPlugins: ReadonlyMap<string, Thenable<(md: any) => any>>;
31
}
32
33
export namespace MarkdownContributions {
34
export const Empty: MarkdownContributions = {
35
previewScripts: [],
36
previewStyles: [],
37
previewResourceRoots: [],
38
markdownItPlugins: new Map()
39
};
40
41
export function merge(a: MarkdownContributions, b: MarkdownContributions): MarkdownContributions {
42
return {
43
previewScripts: [...a.previewScripts, ...b.previewScripts],
44
previewStyles: [...a.previewStyles, ...b.previewStyles],
45
previewResourceRoots: [...a.previewResourceRoots, ...b.previewResourceRoots],
46
markdownItPlugins: new Map([...a.markdownItPlugins.entries(), ...b.markdownItPlugins.entries()]),
47
};
48
}
49
50
function uriEqual(a: vscode.Uri, b: vscode.Uri): boolean {
51
return a.toString() === b.toString();
52
}
53
54
export function equal(a: MarkdownContributions, b: MarkdownContributions): boolean {
55
return arrays.equals(a.previewScripts, b.previewScripts, uriEqual)
56
&& arrays.equals(a.previewStyles, b.previewStyles, uriEqual)
57
&& arrays.equals(a.previewResourceRoots, b.previewResourceRoots, uriEqual)
58
&& arrays.equals(Array.from(a.markdownItPlugins.keys()), Array.from(b.markdownItPlugins.keys()));
59
}
60
61
export function fromExtension(extension: vscode.Extension<any>): MarkdownContributions {
62
const contributions = extension.packageJSON?.contributes;
63
if (!contributions) {
64
return MarkdownContributions.Empty;
65
}
66
67
const previewStyles = Array.from(getContributedStyles(contributions, extension));
68
const previewScripts = Array.from(getContributedScripts(contributions, extension));
69
const previewResourceRoots = previewStyles.length || previewScripts.length ? [extension.extensionUri] : [];
70
const markdownItPlugins = getContributedMarkdownItPlugins(contributions, extension);
71
72
return {
73
previewScripts,
74
previewStyles,
75
previewResourceRoots,
76
markdownItPlugins
77
};
78
}
79
80
function getContributedMarkdownItPlugins(
81
contributes: any,
82
extension: vscode.Extension<any>
83
): Map<string, Thenable<(md: any) => any>> {
84
const map = new Map<string, Thenable<(md: any) => any>>();
85
if (contributes['markdown.markdownItPlugins']) {
86
map.set(extension.id, extension.activate().then(() => {
87
if (extension.exports?.extendMarkdownIt) {
88
return (md: any) => extension.exports.extendMarkdownIt(md);
89
}
90
return (md: any) => md;
91
}));
92
}
93
return map;
94
}
95
96
function getContributedScripts(
97
contributes: any,
98
extension: vscode.Extension<any>
99
) {
100
return resolveExtensionResources(extension, contributes['markdown.previewScripts']);
101
}
102
103
function getContributedStyles(
104
contributes: any,
105
extension: vscode.Extension<any>
106
) {
107
return resolveExtensionResources(extension, contributes['markdown.previewStyles']);
108
}
109
}
110
111
export interface MarkdownContributionProvider {
112
readonly extensionUri: vscode.Uri;
113
114
readonly contributions: MarkdownContributions;
115
readonly onContributionsChanged: vscode.Event<this>;
116
117
dispose(): void;
118
}
119
120
class VSCodeExtensionMarkdownContributionProvider extends Disposable implements MarkdownContributionProvider {
121
122
private _contributions?: MarkdownContributions;
123
124
public constructor(
125
private readonly _extensionContext: vscode.ExtensionContext,
126
) {
127
super();
128
129
this._register(vscode.extensions.onDidChange(() => {
130
const currentContributions = this._getCurrentContributions();
131
const existingContributions = this._contributions || MarkdownContributions.Empty;
132
if (!MarkdownContributions.equal(existingContributions, currentContributions)) {
133
this._contributions = currentContributions;
134
this._onContributionsChanged.fire(this);
135
}
136
}));
137
}
138
139
public get extensionUri() {
140
return this._extensionContext.extensionUri;
141
}
142
143
private readonly _onContributionsChanged = this._register(new vscode.EventEmitter<this>());
144
public readonly onContributionsChanged = this._onContributionsChanged.event;
145
146
public get contributions(): MarkdownContributions {
147
this._contributions ??= this._getCurrentContributions();
148
return this._contributions;
149
}
150
151
private _getCurrentContributions(): MarkdownContributions {
152
return vscode.extensions.all
153
.map(MarkdownContributions.fromExtension)
154
.reduce(MarkdownContributions.merge, MarkdownContributions.Empty);
155
}
156
}
157
158
export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributionProvider {
159
return new VSCodeExtensionMarkdownContributionProvider(context);
160
}
161
162