Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/common/extensionsRegistry.ts
5252 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 nls from '../../../../nls.js';
7
import { onUnexpectedError } from '../../../../base/common/errors.js';
8
import { IJSONSchema } from '../../../../base/common/jsonSchema.js';
9
import Severity from '../../../../base/common/severity.js';
10
import { EXTENSION_IDENTIFIER_PATTERN } from '../../../../platform/extensionManagement/common/extensionManagement.js';
11
import { Extensions, IJSONContributionRegistry } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js';
12
import { Registry } from '../../../../platform/registry/common/platform.js';
13
import { IMessage } from './extensions.js';
14
import { IExtensionDescription, EXTENSION_CATEGORIES, ExtensionIdentifierSet } from '../../../../platform/extensions/common/extensions.js';
15
import { ExtensionKind } from '../../../../platform/environment/common/environment.js';
16
import { productSchemaId } from '../../../../platform/product/common/productService.js';
17
import { ImplicitActivationEvents, IActivationEventsGenerator } from '../../../../platform/extensionManagement/common/implicitActivationEvents.js';
18
import { IDisposable } from '../../../../base/common/lifecycle.js';
19
import { allApiProposals } from '../../../../platform/extensions/common/extensionsApiProposals.js';
20
21
const schemaRegistry = Registry.as<IJSONContributionRegistry>(Extensions.JSONContribution);
22
23
export class ExtensionMessageCollector {
24
25
private readonly _messageHandler: (msg: IMessage) => void;
26
private readonly _extension: IExtensionDescription;
27
private readonly _extensionPointId: string;
28
29
constructor(
30
messageHandler: (msg: IMessage) => void,
31
extension: IExtensionDescription,
32
extensionPointId: string
33
) {
34
this._messageHandler = messageHandler;
35
this._extension = extension;
36
this._extensionPointId = extensionPointId;
37
}
38
39
private _msg(type: Severity, message: string): void {
40
this._messageHandler({
41
type: type,
42
message: message,
43
extensionId: this._extension.identifier,
44
extensionPointId: this._extensionPointId
45
});
46
}
47
48
public error(message: string): void {
49
this._msg(Severity.Error, message);
50
}
51
52
public warn(message: string): void {
53
this._msg(Severity.Warning, message);
54
}
55
56
public info(message: string): void {
57
this._msg(Severity.Info, message);
58
}
59
}
60
61
export interface IExtensionPointUser<T> {
62
description: IExtensionDescription;
63
value: T;
64
collector: ExtensionMessageCollector;
65
}
66
67
export type IExtensionPointHandler<T> = (extensions: readonly IExtensionPointUser<T>[], delta: ExtensionPointUserDelta<T>) => void;
68
69
export interface IExtensionPoint<T> {
70
readonly name: string;
71
setHandler(handler: IExtensionPointHandler<T>): IDisposable;
72
readonly defaultExtensionKind: ExtensionKind[] | undefined;
73
readonly canHandleResolver?: boolean;
74
}
75
76
export class ExtensionPointUserDelta<T> {
77
78
private static _toSet<T>(arr: readonly IExtensionPointUser<T>[]): ExtensionIdentifierSet {
79
const result = new ExtensionIdentifierSet();
80
for (let i = 0, len = arr.length; i < len; i++) {
81
result.add(arr[i].description.identifier);
82
}
83
return result;
84
}
85
86
public static compute<T>(previous: readonly IExtensionPointUser<T>[] | null, current: readonly IExtensionPointUser<T>[]): ExtensionPointUserDelta<T> {
87
if (!previous || !previous.length) {
88
return new ExtensionPointUserDelta<T>(current, []);
89
}
90
if (!current || !current.length) {
91
return new ExtensionPointUserDelta<T>([], previous);
92
}
93
94
const previousSet = this._toSet(previous);
95
const currentSet = this._toSet(current);
96
97
const added = current.filter(user => !previousSet.has(user.description.identifier));
98
const removed = previous.filter(user => !currentSet.has(user.description.identifier));
99
100
return new ExtensionPointUserDelta<T>(added, removed);
101
}
102
103
constructor(
104
public readonly added: readonly IExtensionPointUser<T>[],
105
public readonly removed: readonly IExtensionPointUser<T>[],
106
) { }
107
}
108
109
export class ExtensionPoint<T> implements IExtensionPoint<T> {
110
111
public readonly name: string;
112
public readonly defaultExtensionKind: ExtensionKind[] | undefined;
113
public readonly canHandleResolver?: boolean;
114
115
private _handler: IExtensionPointHandler<T> | null;
116
private _users: IExtensionPointUser<T>[] | null;
117
private _delta: ExtensionPointUserDelta<T> | null;
118
119
constructor(name: string, defaultExtensionKind: ExtensionKind[] | undefined, canHandleResolver?: boolean) {
120
this.name = name;
121
this.defaultExtensionKind = defaultExtensionKind;
122
this.canHandleResolver = canHandleResolver;
123
this._handler = null;
124
this._users = null;
125
this._delta = null;
126
}
127
128
setHandler(handler: IExtensionPointHandler<T>): IDisposable {
129
if (this._handler !== null) {
130
throw new Error('Handler already set!');
131
}
132
this._handler = handler;
133
this._handle();
134
135
return {
136
dispose: () => {
137
this._handler = null;
138
}
139
};
140
}
141
142
acceptUsers(users: IExtensionPointUser<T>[]): void {
143
this._delta = ExtensionPointUserDelta.compute(this._users, users);
144
this._users = users;
145
this._handle();
146
}
147
148
private _handle(): void {
149
if (this._handler === null || this._users === null || this._delta === null) {
150
return;
151
}
152
153
try {
154
this._handler(this._users, this._delta);
155
} catch (err) {
156
onUnexpectedError(err);
157
}
158
}
159
}
160
161
const extensionKindSchema: IJSONSchema = {
162
type: 'string',
163
enum: [
164
'ui',
165
'workspace'
166
],
167
enumDescriptions: [
168
nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."),
169
nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote."),
170
],
171
};
172
173
const schemaId = 'vscode://schemas/vscode-extensions';
174
export const schema: IJSONSchema = {
175
properties: {
176
engines: {
177
type: 'object',
178
description: nls.localize('vscode.extension.engines', "Engine compatibility."),
179
properties: {
180
'vscode': {
181
type: 'string',
182
description: nls.localize('vscode.extension.engines.vscode', 'For VS Code extensions, specifies the VS Code version that the extension is compatible with. Cannot be *. For example: ^0.10.5 indicates compatibility with a minimum VS Code version of 0.10.5.'),
183
default: '^1.22.0',
184
}
185
}
186
},
187
publisher: {
188
description: nls.localize('vscode.extension.publisher', 'The publisher of the VS Code extension.'),
189
type: 'string'
190
},
191
displayName: {
192
description: nls.localize('vscode.extension.displayName', 'The display name for the extension used in the VS Code gallery.'),
193
type: 'string'
194
},
195
categories: {
196
description: nls.localize('vscode.extension.categories', 'The categories used by the VS Code gallery to categorize the extension.'),
197
type: 'array',
198
uniqueItems: true,
199
items: {
200
oneOf: [{
201
type: 'string',
202
enum: EXTENSION_CATEGORIES,
203
},
204
{
205
type: 'string',
206
const: 'Languages',
207
deprecationMessage: nls.localize('vscode.extension.category.languages.deprecated', 'Use \'Programming Languages\' instead'),
208
}]
209
}
210
},
211
galleryBanner: {
212
type: 'object',
213
description: nls.localize('vscode.extension.galleryBanner', 'Banner used in the VS Code marketplace.'),
214
properties: {
215
color: {
216
description: nls.localize('vscode.extension.galleryBanner.color', 'The banner color on the VS Code marketplace page header.'),
217
type: 'string'
218
},
219
theme: {
220
description: nls.localize('vscode.extension.galleryBanner.theme', 'The color theme for the font used in the banner.'),
221
type: 'string',
222
enum: ['dark', 'light']
223
}
224
}
225
},
226
contributes: {
227
description: nls.localize('vscode.extension.contributes', 'All contributions of the VS Code extension represented by this package.'),
228
type: 'object',
229
// eslint-disable-next-line local/code-no-any-casts
230
properties: {
231
// extensions will fill in
232
} as any as { [key: string]: any },
233
default: {}
234
},
235
preview: {
236
type: 'boolean',
237
description: nls.localize('vscode.extension.preview', 'Sets the extension to be flagged as a Preview in the Marketplace.'),
238
},
239
enableProposedApi: {
240
type: 'boolean',
241
deprecationMessage: nls.localize('vscode.extension.enableProposedApi.deprecated', 'Use `enabledApiProposals` instead.'),
242
},
243
enabledApiProposals: {
244
markdownDescription: nls.localize('vscode.extension.enabledApiProposals', 'Enable API proposals to try them out. Only valid **during development**. Extensions **cannot be published** with this property. For more details visit: https://code.visualstudio.com/api/advanced-topics/using-proposed-api'),
245
type: 'array',
246
uniqueItems: true,
247
items: {
248
type: 'string',
249
enum: Object.keys(allApiProposals).map(proposalName => proposalName),
250
markdownEnumDescriptions: Object.values(allApiProposals).map(value => value.proposal)
251
}
252
},
253
api: {
254
markdownDescription: nls.localize('vscode.extension.api', 'Describe the API provided by this extension. For more details visit: https://code.visualstudio.com/api/advanced-topics/remote-extensions#handling-dependencies-with-remote-extensions'),
255
type: 'string',
256
enum: ['none'],
257
enumDescriptions: [
258
nls.localize('vscode.extension.api.none', "Give up entirely the ability to export any APIs. This allows other extensions that depend on this extension to run in a separate extension host process or in a remote machine.")
259
]
260
},
261
activationEvents: {
262
description: nls.localize('vscode.extension.activationEvents', 'Activation events for the VS Code extension.'),
263
type: 'array',
264
items: {
265
type: 'string',
266
defaultSnippets: [
267
{
268
label: 'onWebviewPanel',
269
description: nls.localize('vscode.extension.activationEvents.onWebviewPanel', 'An activation event emmited when a webview is loaded of a certain viewType'),
270
body: 'onWebviewPanel:viewType'
271
},
272
{
273
label: 'onLanguage',
274
description: nls.localize('vscode.extension.activationEvents.onLanguage', 'An activation event emitted whenever a file that resolves to the specified language gets opened.'),
275
body: 'onLanguage:${1:languageId}'
276
},
277
{
278
label: 'onCommand',
279
description: nls.localize('vscode.extension.activationEvents.onCommand', 'An activation event emitted whenever the specified command gets invoked.'),
280
body: 'onCommand:${2:commandId}'
281
},
282
{
283
label: 'onDebug',
284
description: nls.localize('vscode.extension.activationEvents.onDebug', 'An activation event emitted whenever a user is about to start debugging or about to setup debug configurations.'),
285
body: 'onDebug'
286
},
287
{
288
label: 'onDebugInitialConfigurations',
289
description: nls.localize('vscode.extension.activationEvents.onDebugInitialConfigurations', 'An activation event emitted whenever a "launch.json" needs to be created (and all provideDebugConfigurations methods need to be called).'),
290
body: 'onDebugInitialConfigurations'
291
},
292
{
293
label: 'onDebugDynamicConfigurations',
294
description: nls.localize('vscode.extension.activationEvents.onDebugDynamicConfigurations', 'An activation event emitted whenever a list of all debug configurations needs to be created (and all provideDebugConfigurations methods for the "dynamic" scope need to be called).'),
295
body: 'onDebugDynamicConfigurations'
296
},
297
{
298
label: 'onDebugResolve',
299
description: nls.localize('vscode.extension.activationEvents.onDebugResolve', 'An activation event emitted whenever a debug session with the specific type is about to be launched (and a corresponding resolveDebugConfiguration method needs to be called).'),
300
body: 'onDebugResolve:${6:type}'
301
},
302
{
303
label: 'onDebugAdapterProtocolTracker',
304
description: nls.localize('vscode.extension.activationEvents.onDebugAdapterProtocolTracker', 'An activation event emitted whenever a debug session with the specific type is about to be launched and a debug protocol tracker might be needed.'),
305
body: 'onDebugAdapterProtocolTracker:${6:type}'
306
},
307
{
308
label: 'workspaceContains',
309
description: nls.localize('vscode.extension.activationEvents.workspaceContains', 'An activation event emitted whenever a folder is opened that contains at least a file matching the specified glob pattern.'),
310
body: 'workspaceContains:${4:filePattern}'
311
},
312
{
313
label: 'onStartupFinished',
314
description: nls.localize('vscode.extension.activationEvents.onStartupFinished', 'An activation event emitted after the start-up finished (after all `*` activated extensions have finished activating).'),
315
body: 'onStartupFinished'
316
},
317
{
318
label: 'onTaskType',
319
description: nls.localize('vscode.extension.activationEvents.onTaskType', 'An activation event emitted whenever tasks of a certain type need to be listed or resolved.'),
320
body: 'onTaskType:${1:taskType}'
321
},
322
{
323
label: 'onFileSystem',
324
description: nls.localize('vscode.extension.activationEvents.onFileSystem', 'An activation event emitted whenever a file or folder is accessed with the given scheme.'),
325
body: 'onFileSystem:${1:scheme}'
326
},
327
{
328
label: 'onEditSession',
329
description: nls.localize('vscode.extension.activationEvents.onEditSession', 'An activation event emitted whenever an edit session is accessed with the given scheme.'),
330
body: 'onEditSession:${1:scheme}'
331
},
332
{
333
label: 'onSearch',
334
description: nls.localize('vscode.extension.activationEvents.onSearch', 'An activation event emitted whenever a search is started in the folder with the given scheme.'),
335
body: 'onSearch:${7:scheme}'
336
},
337
{
338
label: 'onView',
339
body: 'onView:${5:viewId}',
340
description: nls.localize('vscode.extension.activationEvents.onView', 'An activation event emitted whenever the specified view is expanded.'),
341
},
342
{
343
label: 'onUri',
344
body: 'onUri',
345
description: nls.localize('vscode.extension.activationEvents.onUri', 'An activation event emitted whenever a system-wide Uri directed towards this extension is open.'),
346
},
347
{
348
label: 'onOpenExternalUri',
349
body: 'onOpenExternalUri',
350
description: nls.localize('vscode.extension.activationEvents.onOpenExternalUri', 'An activation event emitted whenever a external uri (such as an http or https link) is being opened.'),
351
},
352
{
353
label: 'onCustomEditor',
354
body: 'onCustomEditor:${9:viewType}',
355
description: nls.localize('vscode.extension.activationEvents.onCustomEditor', 'An activation event emitted whenever the specified custom editor becomes visible.'),
356
},
357
{
358
label: 'onNotebook',
359
body: 'onNotebook:${1:type}',
360
description: nls.localize('vscode.extension.activationEvents.onNotebook', 'An activation event emitted whenever the specified notebook document is opened.'),
361
},
362
{
363
label: 'onAuthenticationRequest',
364
body: 'onAuthenticationRequest:${11:authenticationProviderId}',
365
description: nls.localize('vscode.extension.activationEvents.onAuthenticationRequest', 'An activation event emitted whenever sessions are requested from the specified authentication provider.')
366
},
367
{
368
label: 'onRenderer',
369
description: nls.localize('vscode.extension.activationEvents.onRenderer', 'An activation event emitted whenever a notebook output renderer is used.'),
370
body: 'onRenderer:${11:rendererId}'
371
},
372
{
373
label: 'onTerminalProfile',
374
body: 'onTerminalProfile:${1:terminalId}',
375
description: nls.localize('vscode.extension.activationEvents.onTerminalProfile', 'An activation event emitted when a specific terminal profile is launched.'),
376
},
377
{
378
label: 'onTerminalQuickFixRequest',
379
body: 'onTerminalQuickFixRequest:${1:quickFixId}',
380
description: nls.localize('vscode.extension.activationEvents.onTerminalQuickFixRequest', 'An activation event emitted when a command matches the selector associated with this ID'),
381
},
382
{
383
label: 'onWalkthrough',
384
body: 'onWalkthrough:${1:walkthroughID}',
385
description: nls.localize('vscode.extension.activationEvents.onWalkthrough', 'An activation event emitted when a specified walkthrough is opened.'),
386
},
387
{
388
label: 'onIssueReporterOpened',
389
body: 'onIssueReporterOpened',
390
description: nls.localize('vscode.extension.activationEvents.onIssueReporterOpened', 'An activation event emitted when the issue reporter is opened.'),
391
},
392
{
393
label: 'onChatParticipant',
394
body: 'onChatParticipant:${1:participantId}',
395
description: nls.localize('vscode.extension.activationEvents.onChatParticipant', 'An activation event emitted when the specified chat participant is invoked.'),
396
},
397
{
398
label: 'onChatContextProvider',
399
body: 'onChatContextProvider:${1:contextProviderId}',
400
description: nls.localize('vscode.extension.activationEvents.onChatContextProvider', 'An activation event emitted when the specified chat context provider is invoked.'),
401
},
402
{
403
label: 'onLanguageModelChatProvider',
404
body: 'onLanguageModelChatProvider:${1:vendor}',
405
description: nls.localize('vscode.extension.activationEvents.onLanguageModelChatProvider', 'An activation event emitted when a chat model provider for the given vendor is requested.'),
406
},
407
{
408
label: 'onLanguageModelTool',
409
body: 'onLanguageModelTool:${1:toolId}',
410
description: nls.localize('vscode.extension.activationEvents.onLanguageModelTool', 'An activation event emitted when the specified language model tool is invoked.'),
411
},
412
{
413
label: 'onTerminal',
414
body: 'onTerminal:{1:shellType}',
415
description: nls.localize('vscode.extension.activationEvents.onTerminal', 'An activation event emitted when a terminal of the given shell type is opened.'),
416
},
417
{
418
label: 'onTerminalShellIntegration',
419
body: 'onTerminalShellIntegration:${1:shellType}',
420
description: nls.localize('vscode.extension.activationEvents.onTerminalShellIntegration', 'An activation event emitted when terminal shell integration is activated for the given shell type.'),
421
},
422
{
423
label: 'onMcpCollection',
424
description: nls.localize('vscode.extension.activationEvents.onMcpCollection', 'An activation event emitted whenver a tool from the MCP server is requested.'),
425
body: 'onMcpCollection:${2:collectionId}',
426
},
427
{
428
label: '*',
429
description: nls.localize('vscode.extension.activationEvents.star', 'An activation event emitted on VS Code startup. To ensure a great end user experience, please use this activation event in your extension only when no other activation events combination works in your use-case.'),
430
body: '*'
431
}
432
],
433
}
434
},
435
badges: {
436
type: 'array',
437
description: nls.localize('vscode.extension.badges', 'Array of badges to display in the sidebar of the Marketplace\'s extension page.'),
438
items: {
439
type: 'object',
440
required: ['url', 'href', 'description'],
441
properties: {
442
url: {
443
type: 'string',
444
description: nls.localize('vscode.extension.badges.url', 'Badge image URL.')
445
},
446
href: {
447
type: 'string',
448
description: nls.localize('vscode.extension.badges.href', 'Badge link.')
449
},
450
description: {
451
type: 'string',
452
description: nls.localize('vscode.extension.badges.description', 'Badge description.')
453
}
454
}
455
}
456
},
457
markdown: {
458
type: 'string',
459
description: nls.localize('vscode.extension.markdown', "Controls the Markdown rendering engine used in the Marketplace. Either github (default) or standard."),
460
enum: ['github', 'standard'],
461
default: 'github'
462
},
463
qna: {
464
default: 'marketplace',
465
description: nls.localize('vscode.extension.qna', "Controls the Q&A link in the Marketplace. Set to marketplace to enable the default Marketplace Q & A site. Set to a string to provide the URL of a custom Q & A site. Set to false to disable Q & A altogether."),
466
anyOf: [
467
{
468
type: ['string', 'boolean'],
469
enum: ['marketplace', false]
470
},
471
{
472
type: 'string'
473
}
474
]
475
},
476
extensionDependencies: {
477
description: nls.localize('vscode.extension.extensionDependencies', 'Dependencies to other extensions. The identifier of an extension is always ${publisher}.${name}. For example: vscode.csharp.'),
478
type: 'array',
479
uniqueItems: true,
480
items: {
481
type: 'string',
482
pattern: EXTENSION_IDENTIFIER_PATTERN
483
}
484
},
485
extensionAffinity: {
486
description: nls.localize('vscode.extension.extensionAffinity', 'Extensions that this extension should be colocated with in the same extension host process if possible. The identifier of an extension is always ${publisher}.${name}. For example: vscode.git.'),
487
type: 'array',
488
uniqueItems: true,
489
items: {
490
type: 'string',
491
pattern: EXTENSION_IDENTIFIER_PATTERN
492
}
493
},
494
extensionPack: {
495
description: nls.localize('vscode.extension.contributes.extensionPack', "A set of extensions that can be installed together. The identifier of an extension is always ${publisher}.${name}. For example: vscode.csharp."),
496
type: 'array',
497
uniqueItems: true,
498
items: {
499
type: 'string',
500
pattern: EXTENSION_IDENTIFIER_PATTERN
501
}
502
},
503
extensionKind: {
504
description: nls.localize('extensionKind', "Define the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions run on the remote."),
505
type: 'array',
506
items: extensionKindSchema,
507
default: ['workspace'],
508
defaultSnippets: [
509
{
510
body: ['ui'],
511
description: nls.localize('extensionKind.ui', "Define an extension which can run only on the local machine when connected to remote window.")
512
},
513
{
514
body: ['workspace'],
515
description: nls.localize('extensionKind.workspace', "Define an extension which can run only on the remote machine when connected remote window.")
516
},
517
{
518
body: ['ui', 'workspace'],
519
description: nls.localize('extensionKind.ui-workspace', "Define an extension which can run on either side, with a preference towards running on the local machine.")
520
},
521
{
522
body: ['workspace', 'ui'],
523
description: nls.localize('extensionKind.workspace-ui', "Define an extension which can run on either side, with a preference towards running on the remote machine.")
524
},
525
{
526
body: [],
527
description: nls.localize('extensionKind.empty', "Define an extension which cannot run in a remote context, neither on the local, nor on the remote machine.")
528
}
529
]
530
},
531
capabilities: {
532
description: nls.localize('vscode.extension.capabilities', "Declare the set of supported capabilities by the extension."),
533
type: 'object',
534
properties: {
535
virtualWorkspaces: {
536
description: nls.localize('vscode.extension.capabilities.virtualWorkspaces', "Declares whether the extension should be enabled in virtual workspaces. A virtual workspace is a workspace which is not backed by any on-disk resources. When false, this extension will be automatically disabled in virtual workspaces. Default is true."),
537
type: ['boolean', 'object'],
538
defaultSnippets: [
539
{ label: 'limited', body: { supported: '${1:limited}', description: '${2}' } },
540
{ label: 'false', body: { supported: false, description: '${2}' } },
541
],
542
default: true.valueOf,
543
properties: {
544
supported: {
545
markdownDescription: nls.localize('vscode.extension.capabilities.virtualWorkspaces.supported', "Declares the level of support for virtual workspaces by the extension."),
546
type: ['string', 'boolean'],
547
enum: ['limited', true, false],
548
enumDescriptions: [
549
nls.localize('vscode.extension.capabilities.virtualWorkspaces.supported.limited', "The extension will be enabled in virtual workspaces with some functionality disabled."),
550
nls.localize('vscode.extension.capabilities.virtualWorkspaces.supported.true', "The extension will be enabled in virtual workspaces with all functionality enabled."),
551
nls.localize('vscode.extension.capabilities.virtualWorkspaces.supported.false', "The extension will not be enabled in virtual workspaces."),
552
]
553
},
554
description: {
555
type: 'string',
556
markdownDescription: nls.localize('vscode.extension.capabilities.virtualWorkspaces.description', "A description of how virtual workspaces affects the extensions behavior and why it is needed. This only applies when `supported` is not `true`."),
557
}
558
}
559
},
560
untrustedWorkspaces: {
561
description: nls.localize('vscode.extension.capabilities.untrustedWorkspaces', 'Declares how the extension should be handled in untrusted workspaces.'),
562
type: 'object',
563
required: ['supported'],
564
defaultSnippets: [
565
{ body: { supported: '${1:limited}', description: '${2}' } },
566
],
567
properties: {
568
supported: {
569
markdownDescription: nls.localize('vscode.extension.capabilities.untrustedWorkspaces.supported', "Declares the level of support for untrusted workspaces by the extension."),
570
type: ['string', 'boolean'],
571
enum: ['limited', true, false],
572
enumDescriptions: [
573
nls.localize('vscode.extension.capabilities.untrustedWorkspaces.supported.limited', "The extension will be enabled in untrusted workspaces with some functionality disabled."),
574
nls.localize('vscode.extension.capabilities.untrustedWorkspaces.supported.true', "The extension will be enabled in untrusted workspaces with all functionality enabled."),
575
nls.localize('vscode.extension.capabilities.untrustedWorkspaces.supported.false', "The extension will not be enabled in untrusted workspaces."),
576
]
577
},
578
restrictedConfigurations: {
579
description: nls.localize('vscode.extension.capabilities.untrustedWorkspaces.restrictedConfigurations', "A list of configuration keys contributed by the extension that should not use workspace values in untrusted workspaces."),
580
type: 'array',
581
items: {
582
type: 'string'
583
}
584
},
585
description: {
586
type: 'string',
587
markdownDescription: nls.localize('vscode.extension.capabilities.untrustedWorkspaces.description', "A description of how workspace trust affects the extensions behavior and why it is needed. This only applies when `supported` is not `true`."),
588
}
589
}
590
}
591
}
592
},
593
sponsor: {
594
description: nls.localize('vscode.extension.contributes.sponsor', "Specify the location from where users can sponsor your extension."),
595
type: 'object',
596
defaultSnippets: [
597
{ body: { url: '${1:https:}' } },
598
],
599
properties: {
600
'url': {
601
description: nls.localize('vscode.extension.contributes.sponsor.url', "URL from where users can sponsor your extension. It must be a valid URL with a HTTP or HTTPS protocol. Example value: https://github.com/sponsors/nvaccess"),
602
type: 'string',
603
}
604
}
605
},
606
scripts: {
607
type: 'object',
608
properties: {
609
'vscode:prepublish': {
610
description: nls.localize('vscode.extension.scripts.prepublish', 'Script executed before the package is published as a VS Code extension.'),
611
type: 'string'
612
},
613
'vscode:uninstall': {
614
description: nls.localize('vscode.extension.scripts.uninstall', 'Uninstall hook for VS Code extension. Script that gets executed when the extension is completely uninstalled from VS Code which is when VS Code is restarted (shutdown and start) after the extension is uninstalled. Only Node scripts are supported.'),
615
type: 'string'
616
}
617
}
618
},
619
icon: {
620
type: 'string',
621
description: nls.localize('vscode.extension.icon', 'The path to a 128x128 pixel icon.')
622
},
623
l10n: {
624
type: 'string',
625
description: nls.localize({
626
key: 'vscode.extension.l10n',
627
comment: [
628
'{Locked="bundle.l10n._locale_.json"}',
629
'{Locked="vscode.l10n API"}'
630
]
631
}, 'The relative path to a folder containing localization (bundle.l10n.*.json) files. Must be specified if you are using the vscode.l10n API.')
632
},
633
pricing: {
634
type: 'string',
635
markdownDescription: nls.localize('vscode.extension.pricing', 'The pricing information for the extension. Can be Free (default) or Trial. For more details visit: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#extension-pricing-label'),
636
enum: ['Free', 'Trial'],
637
default: 'Free'
638
}
639
}
640
};
641
642
export type removeArray<T> = T extends Array<infer X> ? X : T;
643
644
export interface IExtensionPointDescriptor<T> {
645
extensionPoint: string;
646
deps?: IExtensionPoint<any>[];
647
jsonSchema: IJSONSchema;
648
defaultExtensionKind?: ExtensionKind[];
649
canHandleResolver?: boolean;
650
/**
651
* A function which runs before the extension point has been validated and which
652
* should collect automatic activation events from the contribution.
653
*/
654
activationEventsGenerator?: IActivationEventsGenerator<removeArray<T>>;
655
}
656
657
export class ExtensionsRegistryImpl {
658
659
private readonly _extensionPoints = new Map<string, ExtensionPoint<any>>();
660
661
public registerExtensionPoint<T>(desc: IExtensionPointDescriptor<T>): IExtensionPoint<T> {
662
if (this._extensionPoints.has(desc.extensionPoint)) {
663
throw new Error('Duplicate extension point: ' + desc.extensionPoint);
664
}
665
const result = new ExtensionPoint<T>(desc.extensionPoint, desc.defaultExtensionKind, desc.canHandleResolver);
666
this._extensionPoints.set(desc.extensionPoint, result);
667
if (desc.activationEventsGenerator) {
668
ImplicitActivationEvents.register(desc.extensionPoint, desc.activationEventsGenerator);
669
}
670
671
schema.properties!['contributes'].properties![desc.extensionPoint] = desc.jsonSchema;
672
schemaRegistry.registerSchema(schemaId, schema);
673
674
return result;
675
}
676
677
public getExtensionPoints(): ExtensionPoint<any>[] {
678
return Array.from(this._extensionPoints.values());
679
}
680
}
681
682
const PRExtensions = {
683
ExtensionsRegistry: 'ExtensionsRegistry'
684
};
685
Registry.add(PRExtensions.ExtensionsRegistry, new ExtensionsRegistryImpl());
686
export const ExtensionsRegistry: ExtensionsRegistryImpl = Registry.as(PRExtensions.ExtensionsRegistry);
687
688
schemaRegistry.registerSchema(schemaId, schema);
689
690
691
schemaRegistry.registerSchema(productSchemaId, {
692
properties: {
693
extensionEnabledApiProposals: {
694
description: nls.localize('product.extensionEnabledApiProposals', "API proposals that the respective extensions can freely use."),
695
type: 'object',
696
properties: {},
697
additionalProperties: {
698
anyOf: [{
699
type: 'array',
700
uniqueItems: true,
701
items: {
702
type: 'string',
703
enum: Object.keys(allApiProposals),
704
markdownEnumDescriptions: Object.values(allApiProposals).map(value => value.proposal)
705
}
706
}]
707
}
708
}
709
}
710
});
711
712