extensionsRegistry.ts 12.0 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 41
			source: this._extension.extensionFolderPath,
			extensionId: this._extension.id,
			extensionPointId: this._extensionPointId
E
Erich Gamma 已提交
42 43 44
		});
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

J
Joao Moreno 已提交
123 124 125
const schemaId = 'vscode://schemas/vscode-extensions';
const schema: IJSONSchema = {
	properties: {
126
		engines: {
127
			type: 'object',
J
Joao Moreno 已提交
128
			description: nls.localize('vscode.extension.engines', "Engine compatibility."),
129 130 131 132
			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.'),
133
					default: '^1.22.0',
134 135 136
				}
			}
		},
D
Denis Malinochkin 已提交
137 138 139 140
		publisher: {
			description: nls.localize('vscode.extension.publisher', 'The publisher of the VS Code extension.'),
			type: 'string'
		},
J
Joao Moreno 已提交
141 142 143 144 145 146 147
		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',
148
			uniqueItems: true,
J
Joao Moreno 已提交
149
			items: {
150 151 152 153 154 155 156 157 158
				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 已提交
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
			}
		},
		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 已提交
176 177 178 179 180 181 182
		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 已提交
183
		},
D
Denis Malinochkin 已提交
184 185 186 187
		preview: {
			type: 'boolean',
			description: nls.localize('vscode.extension.preview', 'Sets the extension to be flagged as a Preview in the Marketplace.'),
		},
J
Joao Moreno 已提交
188 189 190 191
		activationEvents: {
			description: nls.localize('vscode.extension.activationEvents', 'Activation events for the VS Code extension.'),
			type: 'array',
			items: {
192
				type: 'string',
193
				defaultSnippets: [
194 195 196 197 198 199 200 201 202 203 204 205
					{
						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',
206 207
						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'
208
					},
209 210 211 212 213 214 215 216 217 218
					{
						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}'
					},
219 220 221 222 223 224 225
					{
						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 已提交
226 227
						body: 'onView:${5:viewId}',
						description: nls.localize('vscode.extension.activationEvents.onView', 'An activation event emitted whenever the specified view is expanded.'),
228
					},
J
onUri  
Joao Moreno 已提交
229 230 231 232 233
					{
						label: 'onUri',
						body: 'onView',
						description: nls.localize('vscode.extension.activationEvents.onUri', 'An activation event emitted whenever a system-wide Uri directed towards this extension is open.'),
					},
234 235 236 237 238
					{
						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: '*'
					}
239
				],
J
Joao Moreno 已提交
240 241
			}
		},
D
Denis Malinochkin 已提交
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
		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 已提交
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
		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 已提交
283
		extensionDependencies: {
284
			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 已提交
285
			type: 'array',
286
			uniqueItems: true,
J
Joao Moreno 已提交
287
			items: {
S
Sandeep Somavarapu 已提交
288 289
				type: 'string',
				pattern: EXTENSION_IDENTIFIER_PATTERN
J
Joao Moreno 已提交
290 291 292 293 294 295 296 297
			}
		},
		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 已提交
298 299
				},
				'vscode:uninstall': {
300
					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 已提交
301
					type: 'string'
J
Joao Moreno 已提交
302 303
				}
			}
D
Denis Malinochkin 已提交
304 305 306 307
		},
		icon: {
			type: 'string',
			description: nls.localize('vscode.extension.icon', 'The path to a 128x128 pixel icon.')
J
Joao Moreno 已提交
308 309 310 311
		}
	}
};

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

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

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

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

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

		return result;
	}

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

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

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