Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/extensions/common/extensions.ts
3296 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 Severity from '../../../base/common/severity.js';
7
import * as strings from '../../../base/common/strings.js';
8
import { URI } from '../../../base/common/uri.js';
9
import { ILocalizedString } from '../../action/common/action.js';
10
import { ExtensionKind } from '../../environment/common/environment.js';
11
import { createDecorator } from '../../instantiation/common/instantiation.js';
12
import { getRemoteName } from '../../remote/common/remoteHosts.js';
13
14
export const USER_MANIFEST_CACHE_FILE = 'extensions.user.cache';
15
export const BUILTIN_MANIFEST_CACHE_FILE = 'extensions.builtin.cache';
16
export const UNDEFINED_PUBLISHER = 'undefined_publisher';
17
18
export interface ICommand {
19
command: string;
20
title: string | ILocalizedString;
21
category?: string | ILocalizedString;
22
}
23
24
export interface IDebugger {
25
label?: string;
26
type: string;
27
runtime?: string;
28
}
29
30
export interface IGrammar {
31
language?: string;
32
}
33
34
export interface IJSONValidation {
35
fileMatch: string | string[];
36
url: string;
37
}
38
39
export interface IKeyBinding {
40
command: string;
41
key: string;
42
when?: string;
43
mac?: string;
44
linux?: string;
45
win?: string;
46
}
47
48
export interface ILanguage {
49
id: string;
50
extensions: string[];
51
aliases: string[];
52
}
53
54
export interface IMenu {
55
command: string;
56
alt?: string;
57
when?: string;
58
group?: string;
59
}
60
61
export interface ISnippet {
62
language: string;
63
}
64
65
export interface ITheme {
66
label: string;
67
}
68
69
export interface IViewContainer {
70
id: string;
71
title: string;
72
}
73
74
export interface IView {
75
id: string;
76
name: string;
77
}
78
79
export interface IColor {
80
id: string;
81
description: string;
82
defaults: { light: string; dark: string; highContrast: string };
83
}
84
85
interface IWebviewEditor {
86
readonly viewType: string;
87
readonly priority: string;
88
readonly selector: readonly {
89
readonly filenamePattern?: string;
90
}[];
91
}
92
93
export interface ICodeActionContributionAction {
94
readonly kind: string;
95
readonly title: string;
96
readonly description?: string;
97
}
98
99
export interface ICodeActionContribution {
100
readonly languages: readonly string[];
101
readonly actions: readonly ICodeActionContributionAction[];
102
}
103
104
export interface IAuthenticationContribution {
105
readonly id: string;
106
readonly label: string;
107
readonly authorizationServerGlobs?: string[];
108
}
109
110
export interface IWalkthroughStep {
111
readonly id: string;
112
readonly title: string;
113
readonly description: string | undefined;
114
readonly media:
115
| { image: string | { dark: string; light: string; hc: string }; altText: string; markdown?: never; svg?: never; video?: never }
116
| { markdown: string; image?: never; svg?: never; video?: never }
117
| { svg: string; altText: string; markdown?: never; image?: never; video?: never }
118
| { video: string | { dark: string; light: string; hc: string }; poster: string | { dark: string; light: string; hc: string }; altText: string; markdown?: never; image?: never; svg?: never };
119
readonly completionEvents?: string[];
120
/** @deprecated use `completionEvents: 'onCommand:...'` */
121
readonly doneOn?: { command: string };
122
readonly when?: string;
123
}
124
125
export interface IWalkthrough {
126
readonly id: string;
127
readonly title: string;
128
readonly icon?: string;
129
readonly description: string;
130
readonly steps: IWalkthroughStep[];
131
readonly featuredFor: string[] | undefined;
132
readonly when?: string;
133
}
134
135
export interface IStartEntry {
136
readonly title: string;
137
readonly description: string;
138
readonly command: string;
139
readonly when?: string;
140
readonly category: 'file' | 'folder' | 'notebook';
141
}
142
143
export interface INotebookEntry {
144
readonly type: string;
145
readonly displayName: string;
146
}
147
148
export interface INotebookRendererContribution {
149
readonly id: string;
150
readonly displayName: string;
151
readonly mimeTypes: string[];
152
}
153
154
export interface IDebugVisualizationContribution {
155
readonly id: string;
156
readonly when: string;
157
}
158
159
export interface ITranslation {
160
id: string;
161
path: string;
162
}
163
164
export interface ILocalizationContribution {
165
languageId: string;
166
languageName?: string;
167
localizedLanguageName?: string;
168
translations: ITranslation[];
169
minimalTranslations?: { [key: string]: string };
170
}
171
172
export interface IChatParticipantContribution {
173
id: string;
174
name: string;
175
fullName: string;
176
description?: string;
177
isDefault?: boolean;
178
commands?: { name: string }[];
179
}
180
181
export interface IToolContribution {
182
name: string;
183
displayName: string;
184
modelDescription: string;
185
userDescription?: string;
186
}
187
188
export interface IToolSetContribution {
189
name: string;
190
referenceName: string;
191
description: string;
192
icon?: string;
193
tools: string[];
194
}
195
196
export interface IMcpCollectionContribution {
197
readonly id: string;
198
readonly label: string;
199
}
200
201
export interface IExtensionContributions {
202
commands?: ICommand[];
203
configuration?: any;
204
debuggers?: IDebugger[];
205
grammars?: IGrammar[];
206
jsonValidation?: IJSONValidation[];
207
keybindings?: IKeyBinding[];
208
languages?: ILanguage[];
209
menus?: { [context: string]: IMenu[] };
210
snippets?: ISnippet[];
211
themes?: ITheme[];
212
iconThemes?: ITheme[];
213
productIconThemes?: ITheme[];
214
viewsContainers?: { [location: string]: IViewContainer[] };
215
views?: { [location: string]: IView[] };
216
colors?: IColor[];
217
localizations?: ILocalizationContribution[];
218
readonly customEditors?: readonly IWebviewEditor[];
219
readonly codeActions?: readonly ICodeActionContribution[];
220
authentication?: IAuthenticationContribution[];
221
walkthroughs?: IWalkthrough[];
222
startEntries?: IStartEntry[];
223
readonly notebooks?: INotebookEntry[];
224
readonly notebookRenderer?: INotebookRendererContribution[];
225
readonly debugVisualizers?: IDebugVisualizationContribution[];
226
readonly chatParticipants?: ReadonlyArray<IChatParticipantContribution>;
227
readonly languageModelTools?: ReadonlyArray<IToolContribution>;
228
readonly languageModelToolSets?: ReadonlyArray<IToolSetContribution>;
229
readonly mcpServerDefinitionProviders?: ReadonlyArray<IMcpCollectionContribution>;
230
}
231
232
export interface IExtensionCapabilities {
233
readonly virtualWorkspaces?: ExtensionVirtualWorkspaceSupport;
234
readonly untrustedWorkspaces?: ExtensionUntrustedWorkspaceSupport;
235
}
236
237
238
export const ALL_EXTENSION_KINDS: readonly ExtensionKind[] = ['ui', 'workspace', 'web'];
239
240
export type LimitedWorkspaceSupportType = 'limited';
241
export type ExtensionUntrustedWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;
242
export type ExtensionUntrustedWorkspaceSupport = { supported: true } | { supported: false; description: string } | { supported: LimitedWorkspaceSupportType; description: string; restrictedConfigurations?: string[] };
243
244
export type ExtensionVirtualWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;
245
export type ExtensionVirtualWorkspaceSupport = boolean | { supported: true } | { supported: false | LimitedWorkspaceSupportType; description: string };
246
247
export function getWorkspaceSupportTypeMessage(supportType: ExtensionUntrustedWorkspaceSupport | ExtensionVirtualWorkspaceSupport | undefined): string | undefined {
248
if (typeof supportType === 'object' && supportType !== null) {
249
if (supportType.supported !== true) {
250
return supportType.description;
251
}
252
}
253
return undefined;
254
}
255
256
257
export interface IExtensionIdentifier {
258
id: string;
259
uuid?: string;
260
}
261
262
export const EXTENSION_CATEGORIES = [
263
'AI',
264
'Azure',
265
'Chat',
266
'Data Science',
267
'Debuggers',
268
'Extension Packs',
269
'Education',
270
'Formatters',
271
'Keymaps',
272
'Language Packs',
273
'Linters',
274
'Machine Learning',
275
'Notebooks',
276
'Programming Languages',
277
'SCM Providers',
278
'Snippets',
279
'Testing',
280
'Themes',
281
'Visualization',
282
'Other',
283
];
284
285
export interface IRelaxedExtensionManifest {
286
name: string;
287
displayName?: string;
288
publisher: string;
289
version: string;
290
engines: { readonly vscode: string };
291
description?: string;
292
main?: string;
293
type?: string;
294
browser?: string;
295
preview?: boolean;
296
// For now this only supports pointing to l10n bundle files
297
// but it will be used for package.l10n.json files in the future
298
l10n?: string;
299
icon?: string;
300
categories?: string[];
301
keywords?: string[];
302
activationEvents?: string[];
303
extensionDependencies?: string[];
304
extensionPack?: string[];
305
extensionKind?: ExtensionKind | ExtensionKind[];
306
contributes?: IExtensionContributions;
307
repository?: { url: string };
308
bugs?: { url: string };
309
originalEnabledApiProposals?: readonly string[];
310
enabledApiProposals?: readonly string[];
311
api?: string;
312
scripts?: { [key: string]: string };
313
capabilities?: IExtensionCapabilities;
314
}
315
316
export type IExtensionManifest = Readonly<IRelaxedExtensionManifest>;
317
318
export const enum ExtensionType {
319
System,
320
User
321
}
322
323
export const enum TargetPlatform {
324
WIN32_X64 = 'win32-x64',
325
WIN32_ARM64 = 'win32-arm64',
326
327
LINUX_X64 = 'linux-x64',
328
LINUX_ARM64 = 'linux-arm64',
329
LINUX_ARMHF = 'linux-armhf',
330
331
ALPINE_X64 = 'alpine-x64',
332
ALPINE_ARM64 = 'alpine-arm64',
333
334
DARWIN_X64 = 'darwin-x64',
335
DARWIN_ARM64 = 'darwin-arm64',
336
337
WEB = 'web',
338
339
UNIVERSAL = 'universal',
340
UNKNOWN = 'unknown',
341
UNDEFINED = 'undefined',
342
}
343
344
export interface IExtension {
345
readonly type: ExtensionType;
346
readonly isBuiltin: boolean;
347
readonly identifier: IExtensionIdentifier;
348
readonly manifest: IExtensionManifest;
349
readonly location: URI;
350
readonly targetPlatform: TargetPlatform;
351
readonly publisherDisplayName?: string;
352
readonly readmeUrl?: URI;
353
readonly changelogUrl?: URI;
354
readonly isValid: boolean;
355
readonly validations: readonly [Severity, string][];
356
readonly preRelease: boolean;
357
}
358
359
/**
360
* **!Do not construct directly!**
361
*
362
* **!Only static methods because it gets serialized!**
363
*
364
* This represents the "canonical" version for an extension identifier. Extension ids
365
* have to be case-insensitive (due to the marketplace), but we must ensure case
366
* preservation because the extension API is already public at this time.
367
*
368
* For example, given an extension with the publisher `"Hello"` and the name `"World"`,
369
* its canonical extension identifier is `"Hello.World"`. This extension could be
370
* referenced in some other extension's dependencies using the string `"hello.world"`.
371
*
372
* To make matters more complicated, an extension can optionally have an UUID. When two
373
* extensions have the same UUID, they are considered equal even if their identifier is different.
374
*/
375
export class ExtensionIdentifier {
376
public readonly value: string;
377
378
/**
379
* Do not use directly. This is public to avoid mangling and thus
380
* allow compatibility between running from source and a built version.
381
*/
382
readonly _lower: string;
383
384
constructor(value: string) {
385
this.value = value;
386
this._lower = value.toLowerCase();
387
}
388
389
public static equals(a: ExtensionIdentifier | string | null | undefined, b: ExtensionIdentifier | string | null | undefined) {
390
if (typeof a === 'undefined' || a === null) {
391
return (typeof b === 'undefined' || b === null);
392
}
393
if (typeof b === 'undefined' || b === null) {
394
return false;
395
}
396
if (typeof a === 'string' || typeof b === 'string') {
397
// At least one of the arguments is an extension id in string form,
398
// so we have to use the string comparison which ignores case.
399
const aValue = (typeof a === 'string' ? a : a.value);
400
const bValue = (typeof b === 'string' ? b : b.value);
401
return strings.equalsIgnoreCase(aValue, bValue);
402
}
403
404
// Now we know both arguments are ExtensionIdentifier
405
return (a._lower === b._lower);
406
}
407
408
/**
409
* Gives the value by which to index (for equality).
410
*/
411
public static toKey(id: ExtensionIdentifier | string): string {
412
if (typeof id === 'string') {
413
return id.toLowerCase();
414
}
415
return id._lower;
416
}
417
}
418
419
export class ExtensionIdentifierSet {
420
421
private readonly _set = new Set<string>();
422
423
public get size(): number {
424
return this._set.size;
425
}
426
427
constructor(iterable?: Iterable<ExtensionIdentifier | string>) {
428
if (iterable) {
429
for (const value of iterable) {
430
this.add(value);
431
}
432
}
433
}
434
435
public add(id: ExtensionIdentifier | string): void {
436
this._set.add(ExtensionIdentifier.toKey(id));
437
}
438
439
public delete(extensionId: ExtensionIdentifier): boolean {
440
return this._set.delete(ExtensionIdentifier.toKey(extensionId));
441
}
442
443
public has(id: ExtensionIdentifier | string): boolean {
444
return this._set.has(ExtensionIdentifier.toKey(id));
445
}
446
}
447
448
export class ExtensionIdentifierMap<T> {
449
450
private readonly _map = new Map<string, T>();
451
452
public clear(): void {
453
this._map.clear();
454
}
455
456
public delete(id: ExtensionIdentifier | string): void {
457
this._map.delete(ExtensionIdentifier.toKey(id));
458
}
459
460
public get(id: ExtensionIdentifier | string): T | undefined {
461
return this._map.get(ExtensionIdentifier.toKey(id));
462
}
463
464
public has(id: ExtensionIdentifier | string): boolean {
465
return this._map.has(ExtensionIdentifier.toKey(id));
466
}
467
468
public set(id: ExtensionIdentifier | string, value: T): void {
469
this._map.set(ExtensionIdentifier.toKey(id), value);
470
}
471
472
public values(): IterableIterator<T> {
473
return this._map.values();
474
}
475
476
forEach(callbackfn: (value: T, key: string, map: Map<string, T>) => void): void {
477
this._map.forEach(callbackfn);
478
}
479
480
[Symbol.iterator](): IterableIterator<[string, T]> {
481
return this._map[Symbol.iterator]();
482
}
483
}
484
485
/**
486
* An error that is clearly from an extension, identified by the `ExtensionIdentifier`
487
*/
488
export class ExtensionError extends Error {
489
490
readonly extension: ExtensionIdentifier;
491
492
constructor(extensionIdentifier: ExtensionIdentifier, cause: Error, message?: string) {
493
super(`Error in extension ${ExtensionIdentifier.toKey(extensionIdentifier)}: ${message ?? cause.message}`, { cause });
494
this.name = 'ExtensionError';
495
this.extension = extensionIdentifier;
496
}
497
}
498
499
export interface IRelaxedExtensionDescription extends IRelaxedExtensionManifest {
500
id?: string;
501
identifier: ExtensionIdentifier;
502
uuid?: string;
503
publisherDisplayName?: string;
504
targetPlatform: TargetPlatform;
505
isBuiltin: boolean;
506
isUserBuiltin: boolean;
507
isUnderDevelopment: boolean;
508
extensionLocation: URI;
509
preRelease: boolean;
510
}
511
512
export type IExtensionDescription = Readonly<IRelaxedExtensionDescription>;
513
514
export function isApplicationScopedExtension(manifest: IExtensionManifest): boolean {
515
return isLanguagePackExtension(manifest);
516
}
517
518
export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {
519
return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false;
520
}
521
522
export function isAuthenticationProviderExtension(manifest: IExtensionManifest): boolean {
523
return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false;
524
}
525
526
export function isResolverExtension(manifest: IExtensionManifest, remoteAuthority: string | undefined): boolean {
527
if (remoteAuthority) {
528
const activationEvent = `onResolveRemoteAuthority:${getRemoteName(remoteAuthority)}`;
529
return !!manifest.activationEvents?.includes(activationEvent);
530
}
531
return false;
532
}
533
534
export function parseApiProposals(enabledApiProposals: string[]): { proposalName: string; version?: number }[] {
535
return enabledApiProposals.map(proposal => {
536
const [proposalName, version] = proposal.split('@');
537
return { proposalName, version: version ? parseInt(version) : undefined };
538
});
539
}
540
541
export function parseEnabledApiProposalNames(enabledApiProposals: string[]): string[] {
542
return enabledApiProposals.map(proposal => proposal.split('@')[0]);
543
}
544
545
export const IBuiltinExtensionsScannerService = createDecorator<IBuiltinExtensionsScannerService>('IBuiltinExtensionsScannerService');
546
export interface IBuiltinExtensionsScannerService {
547
readonly _serviceBrand: undefined;
548
scanBuiltinExtensions(): Promise<IExtension[]>;
549
}
550
551