Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/extensions/common/extensions.ts
5240 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
readonly when?: string;
200
}
201
202
export interface IChatFileContribution {
203
readonly path: string;
204
readonly name?: string;
205
readonly description?: string;
206
}
207
208
export interface IExtensionContributions {
209
commands?: ICommand[];
210
configuration?: any;
211
configurationDefaults?: any;
212
debuggers?: IDebugger[];
213
grammars?: IGrammar[];
214
jsonValidation?: IJSONValidation[];
215
keybindings?: IKeyBinding[];
216
languages?: ILanguage[];
217
menus?: { [context: string]: IMenu[] };
218
snippets?: ISnippet[];
219
themes?: ITheme[];
220
iconThemes?: ITheme[];
221
productIconThemes?: ITheme[];
222
viewsContainers?: { [location: string]: IViewContainer[] };
223
views?: { [location: string]: IView[] };
224
colors?: IColor[];
225
localizations?: ILocalizationContribution[];
226
readonly customEditors?: readonly IWebviewEditor[];
227
readonly codeActions?: readonly ICodeActionContribution[];
228
authentication?: IAuthenticationContribution[];
229
walkthroughs?: IWalkthrough[];
230
startEntries?: IStartEntry[];
231
readonly notebooks?: INotebookEntry[];
232
readonly notebookRenderer?: INotebookRendererContribution[];
233
readonly debugVisualizers?: IDebugVisualizationContribution[];
234
readonly chatParticipants?: ReadonlyArray<IChatParticipantContribution>;
235
readonly chatPromptFiles?: ReadonlyArray<IChatFileContribution>;
236
readonly chatInstructions?: ReadonlyArray<IChatFileContribution>;
237
readonly chatAgents?: ReadonlyArray<IChatFileContribution>;
238
readonly chatSkills?: ReadonlyArray<IChatFileContribution>;
239
readonly languageModelTools?: ReadonlyArray<IToolContribution>;
240
readonly languageModelToolSets?: ReadonlyArray<IToolSetContribution>;
241
readonly mcpServerDefinitionProviders?: ReadonlyArray<IMcpCollectionContribution>;
242
}
243
244
export interface IExtensionCapabilities {
245
readonly virtualWorkspaces?: ExtensionVirtualWorkspaceSupport;
246
readonly untrustedWorkspaces?: ExtensionUntrustedWorkspaceSupport;
247
}
248
249
250
export const ALL_EXTENSION_KINDS: readonly ExtensionKind[] = ['ui', 'workspace', 'web'];
251
252
export type LimitedWorkspaceSupportType = 'limited';
253
export type ExtensionUntrustedWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;
254
export type ExtensionUntrustedWorkspaceSupport = { supported: true } | { supported: false; description: string } | { supported: LimitedWorkspaceSupportType; description: string; restrictedConfigurations?: string[] };
255
256
export type ExtensionVirtualWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;
257
export type ExtensionVirtualWorkspaceSupport = boolean | { supported: true } | { supported: false | LimitedWorkspaceSupportType; description: string };
258
259
export function getWorkspaceSupportTypeMessage(supportType: ExtensionUntrustedWorkspaceSupport | ExtensionVirtualWorkspaceSupport | undefined): string | undefined {
260
if (typeof supportType === 'object' && supportType !== null) {
261
if (supportType.supported !== true) {
262
return supportType.description;
263
}
264
}
265
return undefined;
266
}
267
268
269
export interface IExtensionIdentifier {
270
id: string;
271
uuid?: string;
272
}
273
274
export const EXTENSION_CATEGORIES = [
275
'AI',
276
'Azure',
277
'Chat',
278
'Data Science',
279
'Debuggers',
280
'Extension Packs',
281
'Education',
282
'Formatters',
283
'Keymaps',
284
'Language Packs',
285
'Linters',
286
'Machine Learning',
287
'Notebooks',
288
'Programming Languages',
289
'SCM Providers',
290
'Snippets',
291
'Testing',
292
'Themes',
293
'Visualization',
294
'Other',
295
];
296
297
export interface IRelaxedExtensionManifest {
298
name: string;
299
displayName?: string;
300
publisher: string;
301
version: string;
302
engines: { readonly vscode: string };
303
description?: string;
304
main?: string;
305
type?: string;
306
browser?: string;
307
preview?: boolean;
308
// For now this only supports pointing to l10n bundle files
309
// but it will be used for package.l10n.json files in the future
310
l10n?: string;
311
icon?: string;
312
categories?: string[];
313
keywords?: string[];
314
activationEvents?: readonly string[];
315
extensionDependencies?: string[];
316
extensionAffinity?: string[];
317
extensionPack?: string[];
318
extensionKind?: ExtensionKind | ExtensionKind[];
319
contributes?: IExtensionContributions;
320
repository?: { url: string };
321
bugs?: { url: string };
322
originalEnabledApiProposals?: readonly string[];
323
enabledApiProposals?: readonly string[];
324
api?: string;
325
scripts?: { [key: string]: string };
326
capabilities?: IExtensionCapabilities;
327
}
328
329
export type IExtensionManifest = Readonly<IRelaxedExtensionManifest>;
330
331
export const enum ExtensionType {
332
System,
333
User
334
}
335
336
export const enum TargetPlatform {
337
WIN32_X64 = 'win32-x64',
338
WIN32_ARM64 = 'win32-arm64',
339
340
LINUX_X64 = 'linux-x64',
341
LINUX_ARM64 = 'linux-arm64',
342
LINUX_ARMHF = 'linux-armhf',
343
344
ALPINE_X64 = 'alpine-x64',
345
ALPINE_ARM64 = 'alpine-arm64',
346
347
DARWIN_X64 = 'darwin-x64',
348
DARWIN_ARM64 = 'darwin-arm64',
349
350
WEB = 'web',
351
352
UNIVERSAL = 'universal',
353
UNKNOWN = 'unknown',
354
UNDEFINED = 'undefined',
355
}
356
357
export interface IExtension {
358
readonly type: ExtensionType;
359
readonly isBuiltin: boolean;
360
readonly identifier: IExtensionIdentifier;
361
readonly manifest: IExtensionManifest;
362
readonly location: URI;
363
readonly targetPlatform: TargetPlatform;
364
readonly publisherDisplayName?: string;
365
readonly readmeUrl?: URI;
366
readonly changelogUrl?: URI;
367
readonly isValid: boolean;
368
readonly validations: readonly [Severity, string][];
369
readonly preRelease: boolean;
370
}
371
372
/**
373
* **!Do not construct directly!**
374
*
375
* **!Only static methods because it gets serialized!**
376
*
377
* This represents the "canonical" version for an extension identifier. Extension ids
378
* have to be case-insensitive (due to the marketplace), but we must ensure case
379
* preservation because the extension API is already public at this time.
380
*
381
* For example, given an extension with the publisher `"Hello"` and the name `"World"`,
382
* its canonical extension identifier is `"Hello.World"`. This extension could be
383
* referenced in some other extension's dependencies using the string `"hello.world"`.
384
*
385
* To make matters more complicated, an extension can optionally have an UUID. When two
386
* extensions have the same UUID, they are considered equal even if their identifier is different.
387
*/
388
export class ExtensionIdentifier {
389
public readonly value: string;
390
391
/**
392
* Do not use directly. This is public to avoid mangling and thus
393
* allow compatibility between running from source and a built version.
394
*/
395
readonly _lower: string;
396
397
constructor(value: string) {
398
this.value = value;
399
this._lower = value.toLowerCase();
400
}
401
402
public static equals(a: ExtensionIdentifier | string | null | undefined, b: ExtensionIdentifier | string | null | undefined) {
403
if (typeof a === 'undefined' || a === null) {
404
return (typeof b === 'undefined' || b === null);
405
}
406
if (typeof b === 'undefined' || b === null) {
407
return false;
408
}
409
if (typeof a === 'string' || typeof b === 'string') {
410
// At least one of the arguments is an extension id in string form,
411
// so we have to use the string comparison which ignores case.
412
const aValue = (typeof a === 'string' ? a : a.value);
413
const bValue = (typeof b === 'string' ? b : b.value);
414
return strings.equalsIgnoreCase(aValue, bValue);
415
}
416
417
// Now we know both arguments are ExtensionIdentifier
418
return (a._lower === b._lower);
419
}
420
421
/**
422
* Gives the value by which to index (for equality).
423
*/
424
public static toKey(id: ExtensionIdentifier | string): string {
425
if (typeof id === 'string') {
426
return id.toLowerCase();
427
}
428
return id._lower;
429
}
430
}
431
432
export class ExtensionIdentifierSet {
433
434
private readonly _set = new Set<string>();
435
436
public get size(): number {
437
return this._set.size;
438
}
439
440
constructor(iterable?: Iterable<ExtensionIdentifier | string>) {
441
if (iterable) {
442
for (const value of iterable) {
443
this.add(value);
444
}
445
}
446
}
447
448
public add(id: ExtensionIdentifier | string): void {
449
this._set.add(ExtensionIdentifier.toKey(id));
450
}
451
452
public delete(extensionId: ExtensionIdentifier): boolean {
453
return this._set.delete(ExtensionIdentifier.toKey(extensionId));
454
}
455
456
public has(id: ExtensionIdentifier | string): boolean {
457
return this._set.has(ExtensionIdentifier.toKey(id));
458
}
459
}
460
461
export class ExtensionIdentifierMap<T> {
462
463
private readonly _map = new Map<string, T>();
464
465
public clear(): void {
466
this._map.clear();
467
}
468
469
public delete(id: ExtensionIdentifier | string): void {
470
this._map.delete(ExtensionIdentifier.toKey(id));
471
}
472
473
public get(id: ExtensionIdentifier | string): T | undefined {
474
return this._map.get(ExtensionIdentifier.toKey(id));
475
}
476
477
public has(id: ExtensionIdentifier | string): boolean {
478
return this._map.has(ExtensionIdentifier.toKey(id));
479
}
480
481
public set(id: ExtensionIdentifier | string, value: T): void {
482
this._map.set(ExtensionIdentifier.toKey(id), value);
483
}
484
485
public values(): IterableIterator<T> {
486
return this._map.values();
487
}
488
489
forEach(callbackfn: (value: T, key: string, map: Map<string, T>) => void): void {
490
this._map.forEach(callbackfn);
491
}
492
493
[Symbol.iterator](): IterableIterator<[string, T]> {
494
return this._map[Symbol.iterator]();
495
}
496
}
497
498
/**
499
* An error that is clearly from an extension, identified by the `ExtensionIdentifier`
500
*/
501
export class ExtensionError extends Error {
502
503
readonly extension: ExtensionIdentifier;
504
505
constructor(extensionIdentifier: ExtensionIdentifier, cause: Error, message?: string) {
506
super(`Error in extension ${ExtensionIdentifier.toKey(extensionIdentifier)}: ${message ?? cause.message}`, { cause });
507
this.name = 'ExtensionError';
508
this.extension = extensionIdentifier;
509
}
510
}
511
512
export interface IRelaxedExtensionDescription extends IRelaxedExtensionManifest {
513
id?: string;
514
identifier: ExtensionIdentifier;
515
uuid?: string;
516
publisherDisplayName?: string;
517
targetPlatform: TargetPlatform;
518
isBuiltin: boolean;
519
isUserBuiltin: boolean;
520
isUnderDevelopment: boolean;
521
extensionLocation: URI;
522
preRelease: boolean;
523
}
524
525
export type IExtensionDescription = Readonly<IRelaxedExtensionDescription>;
526
527
export function isApplicationScopedExtension(manifest: IExtensionManifest): boolean {
528
return isLanguagePackExtension(manifest);
529
}
530
531
export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {
532
return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false;
533
}
534
535
export function isAuthenticationProviderExtension(manifest: IExtensionManifest): boolean {
536
return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false;
537
}
538
539
export function isResolverExtension(manifest: IExtensionManifest, remoteAuthority: string | undefined): boolean {
540
if (remoteAuthority) {
541
const activationEvent = `onResolveRemoteAuthority:${getRemoteName(remoteAuthority)}`;
542
return !!manifest.activationEvents?.includes(activationEvent);
543
}
544
return false;
545
}
546
547
export function parseApiProposals(enabledApiProposals: string[]): { proposalName: string; version?: number }[] {
548
return enabledApiProposals.map(proposal => {
549
const [proposalName, version] = proposal.split('@');
550
return { proposalName, version: version ? parseInt(version) : undefined };
551
});
552
}
553
554
export function parseEnabledApiProposalNames(enabledApiProposals: string[]): string[] {
555
return enabledApiProposals.map(proposal => proposal.split('@')[0]);
556
}
557
558
export const IBuiltinExtensionsScannerService = createDecorator<IBuiltinExtensionsScannerService>('IBuiltinExtensionsScannerService');
559
export interface IBuiltinExtensionsScannerService {
560
readonly _serviceBrand: undefined;
561
scanBuiltinExtensions(): Promise<IExtension[]>;
562
}
563
564