extensionsRegistry.ts 11.9 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

A
Alex Dima 已提交
7
import * as nls from 'vs/nls';
J
Johannes Rieken 已提交
8 9
import { onUnexpectedError } from 'vs/base/common/errors';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
E
Erich Gamma 已提交
10
import Severity from 'vs/base/common/severity';
11
import { IMessage, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
J
Johannes Rieken 已提交
12
import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
13
import { Registry } from 'vs/platform/registry/common/platform';
S
Sandeep Somavarapu 已提交
14
import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement';
15

16
const hasOwnProperty = Object.hasOwnProperty;
17
const schemaRegistry = Registry.as<IJSONContributionRegistry>(Extensions.JSONContribution);
E
Erich Gamma 已提交
18

19
export class ExtensionMessageCollector {
E
Erich Gamma 已提交
20

21 22 23
	private readonly _messageHandler: (msg: IMessage) => void;
	private readonly _extension: IExtensionDescription;
	private readonly _extensionPointId: string;
E
Erich Gamma 已提交
24

25 26 27 28 29
	constructor(
		messageHandler: (msg: IMessage) => void,
		extension: IExtensionDescription,
		extensionPointId: string
	) {
A
Alex Dima 已提交
30
		this._messageHandler = messageHandler;
31 32
		this._extension = extension;
		this._extensionPointId = extensionPointId;
E
Erich Gamma 已提交
33 34
	}

J
Johannes Rieken 已提交
35
	private _msg(type: Severity, message: string): void {
A
Alex Dima 已提交
36
		this._messageHandler({
E
Erich Gamma 已提交
37
			type: type,
A
Alex Dima 已提交
38
			message: message,
39 40
			extensionId: this._extension.id,
			extensionPointId: this._extensionPointId
E
Erich Gamma 已提交
41 42 43
		});
	}

A
Alex Dima 已提交
44 45
	public error(message: string): void {
		this._msg(Severity.Error, message);
E
Erich Gamma 已提交
46 47
	}

A
Alex Dima 已提交
48 49
	public warn(message: string): void {
		this._msg(Severity.Warning, message);
E
Erich Gamma 已提交
50 51
	}

A
Alex Dima 已提交
52 53
	public info(message: string): void {
		this._msg(Severity.Info, message);
E
Erich Gamma 已提交
54 55 56 57
	}
}

export interface IExtensionPointUser<T> {
A
Alex Dima 已提交
58
	description: IExtensionDescription;
E
Erich Gamma 已提交
59
	value: T;
60
	collector: ExtensionMessageCollector;
E
Erich Gamma 已提交
61 62 63
}

export interface IExtensionPointHandler<T> {
B
Benjamin Pasero 已提交
64
	(extensions: IExtensionPointUser<T>[]): void;
E
Erich Gamma 已提交
65 66 67 68 69 70 71
}

export interface IExtensionPoint<T> {
	name: string;
	setHandler(handler: IExtensionPointHandler<T>): void;
}

A
Alex Dima 已提交
72
export class ExtensionPoint<T> implements IExtensionPoint<T> {
E
Erich Gamma 已提交
73

A
Alex Dima 已提交
74
	public readonly name: string;
E
Erich Gamma 已提交
75
	private _handler: IExtensionPointHandler<T>;
A
Alex Dima 已提交
76 77
	private _users: IExtensionPointUser<T>[];
	private _done: boolean;
E
Erich Gamma 已提交
78

A
Alex Dima 已提交
79
	constructor(name: string) {
E
Erich Gamma 已提交
80 81
		this.name = name;
		this._handler = null;
A
Alex Dima 已提交
82 83
		this._users = null;
		this._done = false;
E
Erich Gamma 已提交
84 85 86
	}

	setHandler(handler: IExtensionPointHandler<T>): void {
A
Alex Dima 已提交
87
		if (this._handler !== null || this._done) {
E
Erich Gamma 已提交
88 89 90 91 92 93
			throw new Error('Handler already set!');
		}
		this._handler = handler;
		this._handle();
	}

A
Alex Dima 已提交
94 95 96 97 98
	acceptUsers(users: IExtensionPointUser<T>[]): void {
		if (this._users !== null || this._done) {
			throw new Error('Users already set!');
		}
		this._users = users;
E
Erich Gamma 已提交
99 100 101 102
		this._handle();
	}

	private _handle(): void {
A
Alex Dima 已提交
103
		if (this._handler === null || this._users === null) {
E
Erich Gamma 已提交
104 105
			return;
		}
A
Alex Dima 已提交
106
		this._done = true;
E
Erich Gamma 已提交
107

A
Alex Dima 已提交
108 109 110 111 112 113 114 115 116 117 118
		let handler = this._handler;
		this._handler = null;

		let users = this._users;
		this._users = null;

		try {
			handler(users);
		} catch (err) {
			onUnexpectedError(err);
		}
E
Erich Gamma 已提交
119 120 121
	}
}

J
Joao Moreno 已提交
122 123 124
const schemaId = 'vscode://schemas/vscode-extensions';
const schema: IJSONSchema = {
	properties: {
125
		engines: {
126
			type: 'object',
J
Joao Moreno 已提交
127
			description: nls.localize('vscode.extension.engines', "Engine compatibility."),
128 129 130 131
			properties: {
				'vscode': {
					type: 'string',
					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.'),
132
					default: '^1.22.0',
133 134 135
				}
			}
		},
D
Denis Malinochkin 已提交
136 137 138 139
		publisher: {
			description: nls.localize('vscode.extension.publisher', 'The publisher of the VS Code extension.'),
			type: 'string'
		},
J
Joao Moreno 已提交
140 141 142 143 144 145 146
		displayName: {
			description: nls.localize('vscode.extension.displayName', 'The display name for the extension used in the VS Code gallery.'),
			type: 'string'
		},
		categories: {
			description: nls.localize('vscode.extension.categories', 'The categories used by the VS Code gallery to categorize the extension.'),
			type: 'array',
147
			uniqueItems: true,
J
Joao Moreno 已提交
148
			items: {
149 150 151 152 153 154 155 156 157
				oneOf: [{
					type: 'string',
					enum: ['Programming Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Other', 'Keymaps', 'Formatters', 'Extension Packs', 'SCM Providers', 'Azure', 'Language Packs'],
				},
				{
					type: 'string',
					const: 'Languages',
					deprecationMessage: nls.localize('vscode.extension.category.languages.deprecated', 'Use \'Programming  Languages\' instead'),
				}]
J
Joao Moreno 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
			}
		},
		galleryBanner: {
			type: 'object',
			description: nls.localize('vscode.extension.galleryBanner', 'Banner used in the VS Code marketplace.'),
			properties: {
				color: {
					description: nls.localize('vscode.extension.galleryBanner.color', 'The banner color on the VS Code marketplace page header.'),
					type: 'string'
				},
				theme: {
					description: nls.localize('vscode.extension.galleryBanner.theme', 'The color theme for the font used in the banner.'),
					type: 'string',
					enum: ['dark', 'light']
				}
			}
		},
D
Denis Malinochkin 已提交
175 176 177 178 179 180 181
		contributes: {
			description: nls.localize('vscode.extension.contributes', 'All contributions of the VS Code extension represented by this package.'),
			type: 'object',
			properties: {
				// extensions will fill in
			},
			default: {}
J
Joao Moreno 已提交
182
		},
D
Denis Malinochkin 已提交
183 184 185 186
		preview: {
			type: 'boolean',
			description: nls.localize('vscode.extension.preview', 'Sets the extension to be flagged as a Preview in the Marketplace.'),
		},
J
Joao Moreno 已提交
187 188 189 190
		activationEvents: {
			description: nls.localize('vscode.extension.activationEvents', 'Activation events for the VS Code extension.'),
			type: 'array',
			items: {
191
				type: 'string',
192
				defaultSnippets: [
193 194 195 196 197 198 199 200 201 202 203 204
					{
						label: 'onLanguage',
						description: nls.localize('vscode.extension.activationEvents.onLanguage', 'An activation event emitted whenever a file that resolves to the specified language gets opened.'),
						body: 'onLanguage:${1:languageId}'
					},
					{
						label: 'onCommand',
						description: nls.localize('vscode.extension.activationEvents.onCommand', 'An activation event emitted whenever the specified command gets invoked.'),
						body: 'onCommand:${2:commandId}'
					},
					{
						label: 'onDebug',
205 206
						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.'),
						body: 'onDebug'
207
					},
208 209 210 211 212 213 214 215 216 217
					{
						label: 'onDebugInitialConfigurations',
						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).'),
						body: 'onDebugInitialConfigurations'
					},
					{
						label: 'onDebugResolve',
						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).'),
						body: 'onDebugResolve:${6:type}'
					},
218 219 220 221 222 223 224
					{
						label: 'workspaceContains',
						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.'),
						body: 'workspaceContains:${4:filePattern}'
					},
					{
						label: 'onView',
S
Sandeep Somavarapu 已提交
225 226
						body: 'onView:${5:viewId}',
						description: nls.localize('vscode.extension.activationEvents.onView', 'An activation event emitted whenever the specified view is expanded.'),
227
					},
J
onUri  
Joao Moreno 已提交
228 229
					{
						label: 'onUri',
J
Joao Moreno 已提交
230
						body: 'onUri',
J
onUri  
Joao Moreno 已提交
231 232
						description: nls.localize('vscode.extension.activationEvents.onUri', 'An activation event emitted whenever a system-wide Uri directed towards this extension is open.'),
					},
233 234 235 236 237
					{
						label: '*',
						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.'),
						body: '*'
					}
238
				],
J
Joao Moreno 已提交
239 240
			}
		},
D
Denis Malinochkin 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
		badges: {
			type: 'array',
			description: nls.localize('vscode.extension.badges', 'Array of badges to display in the sidebar of the Marketplace\'s extension page.'),
			items: {
				type: 'object',
				required: ['url', 'href', 'description'],
				properties: {
					url: {
						type: 'string',
						description: nls.localize('vscode.extension.badges.url', 'Badge image URL.')
					},
					href: {
						type: 'string',
						description: nls.localize('vscode.extension.badges.href', 'Badge link.')
					},
					description: {
						type: 'string',
						description: nls.localize('vscode.extension.badges.description', 'Badge description.')
					}
				}
			}
		},
J
Joao Moreno 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
		markdown: {
			type: 'string',
			description: nls.localize('vscode.extension.markdown', "Controls the Markdown rendering engine used in the Marketplace. Either github (default) or standard."),
			enum: ['github', 'standard'],
			default: 'github'
		},
		qna: {
			default: 'marketplace',
			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."),
			anyOf: [
				{
					type: ['string', 'boolean'],
					enum: ['marketplace', false]
				},
				{
					type: 'string'
				}
			]
		},
J
Joao Moreno 已提交
282
		extensionDependencies: {
283
			description: nls.localize('vscode.extension.extensionDependencies', 'Dependencies to other extensions. The identifier of an extension is always ${publisher}.${name}. For example: vscode.csharp.'),
J
Joao Moreno 已提交
284
			type: 'array',
285
			uniqueItems: true,
J
Joao Moreno 已提交
286
			items: {
S
Sandeep Somavarapu 已提交
287 288
				type: 'string',
				pattern: EXTENSION_IDENTIFIER_PATTERN
J
Joao Moreno 已提交
289 290 291 292 293 294 295 296
			}
		},
		scripts: {
			type: 'object',
			properties: {
				'vscode:prepublish': {
					description: nls.localize('vscode.extension.scripts.prepublish', 'Script executed before the package is published as a VS Code extension.'),
					type: 'string'
S
Sandeep Somavarapu 已提交
297 298
				},
				'vscode:uninstall': {
299
					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.'),
S
Sandeep Somavarapu 已提交
300
					type: 'string'
J
Joao Moreno 已提交
301 302
				}
			}
D
Denis Malinochkin 已提交
303 304 305 306
		},
		icon: {
			type: 'string',
			description: nls.localize('vscode.extension.icon', 'The path to a 128x128 pixel icon.')
J
Joao Moreno 已提交
307 308 309 310
		}
	}
};

311
export class ExtensionsRegistryImpl {
E
Erich Gamma 已提交
312 313 314 315 316 317 318

	private _extensionPoints: { [extPoint: string]: ExtensionPoint<any>; };

	constructor() {
		this._extensionPoints = {};
	}

A
Alex Dima 已提交
319
	public registerExtensionPoint<T>(extensionPoint: string, deps: IExtensionPoint<any>[], jsonSchema: IJSONSchema): IExtensionPoint<T> {
E
Erich Gamma 已提交
320 321 322
		if (hasOwnProperty.call(this._extensionPoints, extensionPoint)) {
			throw new Error('Duplicate extension point: ' + extensionPoint);
		}
A
Alex Dima 已提交
323
		let result = new ExtensionPoint<T>(extensionPoint);
E
Erich Gamma 已提交
324 325
		this._extensionPoints[extensionPoint] = result;

326
		schema.properties['contributes'].properties[extensionPoint] = jsonSchema;
E
Erich Gamma 已提交
327 328 329 330 331
		schemaRegistry.registerSchema(schemaId, schema);

		return result;
	}

A
Alex Dima 已提交
332 333
	public getExtensionPoints(): ExtensionPoint<any>[] {
		return Object.keys(this._extensionPoints).map(point => this._extensionPoints[point]);
E
Erich Gamma 已提交
334 335 336
	}
}

A
Alex Dima 已提交
337
const PRExtensions = {
338
	ExtensionsRegistry: 'ExtensionsRegistry'
E
Erich Gamma 已提交
339
};
A
Alex Dima 已提交
340
Registry.add(PRExtensions.ExtensionsRegistry, new ExtensionsRegistryImpl());
341
export const ExtensionsRegistry: ExtensionsRegistryImpl = Registry.as(PRExtensions.ExtensionsRegistry);
E
Erich Gamma 已提交
342

W
Wade Anderson 已提交
343
schemaRegistry.registerSchema(schemaId, schema);