提交 25417553 编写于 作者: A Alex Dima

Adopt case insensitive extension identifiers

上级 5adc152e
......@@ -10,6 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import * as types from 'vs/base/common/types';
import * as strings from 'vs/base/common/strings';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export const Extensions = {
Configuration: 'base.contributions.configuration'
......@@ -93,7 +94,7 @@ export interface IConfigurationNode {
}
export interface IDefaultConfigurationExtension {
id: string;
id: CanonicalExtensionIdentifier;
name: string;
defaults: { [key: string]: {} };
}
......
......@@ -6,6 +6,7 @@
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import * as strings from 'vs/base/common/strings';
export const MANIFEST_CACHE_FOLDER = 'CachedExtensions';
export const USER_MANIFEST_CACHE_FILE = 'user';
......@@ -31,3 +32,58 @@ export function isUIExtension(manifest: IExtensionManifest, configurationService
default: return uiExtensions.has(extensionId) || !manifest.main;
}
}
/**
* **!Do not construct directly!**
*
* **!Only static methods because it gets serialized!**
*
* This represents the "canonical" version for an extension identifier. Extension ids
* have to be case-insensitive (due to the marketplace), but we must ensure case
* preservation because the extension API is already public at this time.
*
* For example, given an extension with the publisher `"Hello"` and the name `"World"`,
* its canonical extension identifier is `"Hello.World"`. This extension could be
* referenced in some other extension's dependencies using the string `"hello.world"`.
*
* To make matters more complicated, an extension can optionally have an UUID. When two
* extensions have the same UUID, they are considered equal even if their identifier is different.
*/
export class CanonicalExtensionIdentifier {
public readonly value: string;
private readonly _lower: string;
constructor(value: string) {
this.value = value;
this._lower = value.toLowerCase();
}
public static equals(a: CanonicalExtensionIdentifier | string | null | undefined, b: CanonicalExtensionIdentifier | string | null | undefined) {
if (typeof a === 'undefined' || a === null) {
return (typeof b === 'undefined' || b === null);
}
if (typeof b === 'undefined' || b === null) {
return false;
}
if (typeof a === 'string' || typeof b === 'string') {
// At least one of the arguments is an extension id in string form,
// so we have to use the string comparison which ignores case.
let aValue = (typeof a === 'string' ? a : a.value);
let bValue = (typeof b === 'string' ? b : b.value);
return strings.equalsIgnoreCase(aValue, bValue);
}
// Now we know both arguments are CanonicalExtensionIdentifier
return (a._lower === b._lower);
}
/**
* Gives the value by which to index (for equality).
*/
public static toKey(id: CanonicalExtensionIdentifier | string): string {
if (typeof id === 'string') {
return id.toLowerCase();
}
return id._lower;
}
}
......@@ -6,6 +6,7 @@
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export const IStatusbarService = createDecorator<IStatusbarService>('statusbarService');
......@@ -48,7 +49,7 @@ export interface IStatusbarEntry {
/**
* An optional extension ID if this entry is provided from an extension.
*/
readonly extensionId?: string;
readonly extensionId?: CanonicalExtensionIdentifier;
/**
* Wether to show a beak above the status bar entry.
......
......@@ -30,6 +30,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export interface IUserFriendlyViewsContainerDescriptor {
id: string;
......@@ -147,12 +148,12 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution {
containers.forEach(descriptor => {
const cssClass = `extensionViewlet-${descriptor.id}`;
const icon = resources.joinPath(extension.extensionLocation, descriptor.icon);
this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, order++, cssClass, extension.id);
this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, order++, cssClass, extension.identifier);
});
return order;
}
private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string, extensionId: string): void {
private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string, extensionId: CanonicalExtensionIdentifier): void {
const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const viewletRegistry = Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets);
const id = descriptor.id;
......
......@@ -19,6 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { VIEWLET_ID as EXPLORER } from 'vs/workbench/parts/files/common/files';
import { VIEWLET_ID as SCM } from 'vs/workbench/parts/scm/common/scm';
import { VIEWLET_ID as DEBUG } from 'vs/workbench/parts/debug/common/debug';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
interface IUserFriendlyViewDescriptor {
id: string;
......@@ -136,7 +137,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution {
canToggleVisibility: true,
collapsed: this.showCollapsed(container),
treeView: this.instantiationService.createInstance(CustomTreeView, item.id, container),
order: extension.description.id === container.extensionId ? index + 1 : void 0
order: CanonicalExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : void 0
};
viewIds.push(viewDescriptor.id);
......
......@@ -19,6 +19,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { generateUuid } from 'vs/base/common/uuid';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommentsConfiguration } from 'vs/workbench/parts/comments/electron-browser/comments.contribution';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider {
private _proxy: ExtHostCommentsShape;
......@@ -125,7 +126,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
}
}
$registerWorkspaceCommentProvider(handle: number, extensionId: string): void {
$registerWorkspaceCommentProvider(handle: number, extensionId: CanonicalExtensionIdentifier): void {
this._workspaceProviders.set(handle, undefined);
const providerId = generateUuid();
......@@ -165,7 +166,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
}
*/
this._telemetryService.publicLog('comments:registerWorkspaceCommentProvider', {
extensionId: extensionId
extensionId: extensionId.value
});
}
......
......@@ -9,6 +9,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC
import { IExtHostContext, MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/node/extHost.protocol';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadExtensionService)
export class MainThreadExtensionService implements MainThreadExtensionServiceShape {
......@@ -30,13 +31,13 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
$localShowMessage(severity: Severity, msg: string): void {
this._extensionService._logOrShowMessage(severity, msg);
}
$onWillActivateExtension(extensionId: string): void {
$onWillActivateExtension(extensionId: CanonicalExtensionIdentifier): void {
this._extensionService._onWillActivateExtension(extensionId);
}
$onDidActivateExtension(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
$onDidActivateExtension(extensionId: CanonicalExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
this._extensionService._onDidActivateExtension(extensionId, startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
}
$onExtensionRuntimeError(extensionId: string, data: SerializedError): void {
$onExtensionRuntimeError(extensionId: CanonicalExtensionIdentifier, data: SerializedError): void {
const error = new Error();
error.name = data.name;
error.message = data.message;
......@@ -45,9 +46,9 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
console.error(`[${extensionId}]${error.message}`);
console.error(error.stack);
}
$onExtensionActivationFailed(extensionId: string): void {
$onExtensionActivationFailed(extensionId: CanonicalExtensionIdentifier): void {
}
$addMessage(extensionId: string, severity: Severity, message: string): void {
$addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void {
this._extensionService._addMessage(extensionId, severity, message);
}
}
......@@ -14,6 +14,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { Event } from 'vs/base/common/event';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { dispose } from 'vs/base/common/lifecycle';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadMessageService)
export class MainThreadMessageService implements MainThreadMessageServiceShape {
......@@ -55,9 +56,9 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
}
class ManageExtensionAction extends Action {
constructor(id: string, label: string, commandService: ICommandService) {
super(id, label, undefined, true, () => {
return commandService.executeCommand('_extensions.manage', id);
constructor(id: CanonicalExtensionIdentifier, label: string, commandService: ICommandService) {
super(id.value, label, undefined, true, () => {
return commandService.executeCommand('_extensions.manage', id.value);
});
}
}
......@@ -77,7 +78,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
const secondaryActions: IAction[] = [];
if (extension && !extension.isUnderDevelopment) {
secondaryActions.push(new ManageExtensionAction(extension.id, nls.localize('manageExtension', "Manage Extension"), this._commandService));
secondaryActions.push(new ManageExtensionAction(extension.identifier, nls.localize('manageExtension', "Manage Extension"), this._commandService));
}
const messageHandle = this._notificationService.notify({
......
......@@ -8,6 +8,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { MainThreadStatusBarShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadStatusBar)
export class MainThreadStatusBar implements MainThreadStatusBarShape {
......@@ -27,7 +28,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
}
}
$setEntry(id: number, extensionId: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void {
$setEntry(id: number, extensionId: CanonicalExtensionIdentifier, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void {
// Dispose any old
this.$dispose(id);
......
......@@ -9,17 +9,18 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { URI } from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IExtensionUrlHandler } from 'vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
class ExtensionUrlHandler implements IURLHandler {
constructor(
private readonly proxy: ExtHostUrlsShape,
private readonly handle: number,
readonly extensionId: string
readonly extensionId: CanonicalExtensionIdentifier
) { }
handleURL(uri: URI): Promise<boolean> {
if (uri.authority !== this.extensionId) {
if (!CanonicalExtensionIdentifier.equals(this.extensionId, uri.authority)) {
return Promise.resolve(false);
}
......@@ -31,7 +32,7 @@ class ExtensionUrlHandler implements IURLHandler {
export class MainThreadUrls implements MainThreadUrlsShape {
private readonly proxy: ExtHostUrlsShape;
private handlers = new Map<number, { extensionId: string, disposable: IDisposable }>();
private handlers = new Map<number, { extensionId: CanonicalExtensionIdentifier, disposable: IDisposable }>();
constructor(
context: IExtHostContext,
......@@ -41,7 +42,7 @@ export class MainThreadUrls implements MainThreadUrlsShape {
this.proxy = context.getProxy(ExtHostContext.ExtHostUrls);
}
$registerUriHandler(handle: number, extensionId: string): Promise<void> {
$registerUriHandler(handle: number, extensionId: CanonicalExtensionIdentifier): Promise<void> {
const handler = new ExtensionUrlHandler(this.proxy, handle, extensionId);
const disposable = this.urlService.registerHandler(handler);
......
......@@ -20,6 +20,7 @@ import * as vscode from 'vscode';
import { extHostNamedCustomer } from './extHostCustomers';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { onUnexpectedError } from 'vs/base/common/errors';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver {
......@@ -69,7 +70,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
title: string,
showOptions: { viewColumn: EditorViewColumn | null, preserveFocus: boolean },
options: WebviewInputOptions,
extensionId: string,
extensionId: CanonicalExtensionIdentifier,
extensionLocation: UriComponents
): void {
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
......@@ -92,7 +93,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
"extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId });
this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value });
}
public $disposeWebview(handle: WebviewPanelHandle): void {
......
......@@ -9,7 +9,6 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
import { IFolderQuery, IPatternInfo, ISearchConfiguration, ISearchProgressItem, ISearchService, QueryType, IFileQuery } from 'vs/platform/search/common/search';
......@@ -24,6 +23,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { TextSearchComplete } from 'vscode';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
......@@ -246,7 +246,7 @@ CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (ac
if (disableExtensions && disableExtensions.length) {
const runningExtensions = await extensionService.getExtensions();
// If requested extension to disable is running, then reload window with given workspace
if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => areSameExtensions({ id }, { id: runningExtension.id })))) {
if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => CanonicalExtensionIdentifier.equals(runningExtension.identifier, id)))) {
return windowService.openWindow([URI.file(workspace.fsPath)], { args: { _: [], 'disable-extension': disableExtensions } });
}
}
......
......@@ -63,6 +63,7 @@ import { IExtensionDescription, throwProposedApiError, checkProposedApiEnabled,
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import * as vscode from 'vscode';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry): typeof vscode;
......@@ -152,7 +153,7 @@ export function createApiFactory(
let done = (!extension.isUnderDevelopment);
function informOnce(selector: vscode.DocumentSelector) {
if (!done) {
console.info(`Extension '${extension.id}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`);
console.info(`Extension '${extension.identifier.value}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`);
done = true;
}
}
......@@ -180,7 +181,7 @@ export function createApiFactory(
const informOnce = () => {
if (!done) {
done = true;
console.warn(`Extension '${extension.id}' uses the 'vscode.previewHtml' command which is deprecated and will be removed. Please update your extension to use the Webview API: https://go.microsoft.com/fwlink/?linkid=2039309`);
console.warn(`Extension '${extension.identifier.value}' uses the 'vscode.previewHtml' command which is deprecated and will be removed. Please update your extension to use the Webview API: https://go.microsoft.com/fwlink/?linkid=2039309`);
}
};
return (commandId: string) => {
......@@ -310,7 +311,7 @@ export function createApiFactory(
return extHostLanguageFeatures.registerTypeDefinitionProvider(extension, checkSelector(selector), provider);
},
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.id);
return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.identifier);
},
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider);
......@@ -448,7 +449,7 @@ export function createApiFactory(
return extHostDialogs.showSaveDialog(options);
},
createStatusBarItem(position?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem {
return extHostStatusBar.createStatusBarEntry(extension.id, <number>position, priority);
return extHostStatusBar.createStatusBarEntry(extension.identifier, <number>position, priority);
},
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): vscode.Disposable {
return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable);
......@@ -485,16 +486,16 @@ export function createApiFactory(
return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer);
},
registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => {
return extHostDecorations.registerDecorationProvider(provider, extension.id);
return extHostDecorations.registerDecorationProvider(provider, extension.identifier);
}),
registerUriHandler(handler: vscode.UriHandler) {
return extHostUrls.registerUriHandler(extension.id, handler);
return extHostUrls.registerUriHandler(extension.identifier, handler);
},
createQuickPick<T extends vscode.QuickPickItem>(): vscode.QuickPick<T> {
return extHostQuickOpen.createQuickPick(extension.id, extension.enableProposedApi);
return extHostQuickOpen.createQuickPick(extension.identifier, extension.enableProposedApi);
},
createInputBox(): vscode.InputBox {
return extHostQuickOpen.createInputBox(extension.id);
return extHostQuickOpen.createInputBox(extension.identifier);
},
};
......@@ -528,7 +529,7 @@ export function createApiFactory(
return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace);
},
findFiles: (include, exclude, maxResults?, token?) => {
return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.id, token);
return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.identifier, token);
},
findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback, callbackOrToken?, token?: vscode.CancellationToken) => {
let options: vscode.FindTextInFilesOptions;
......@@ -543,7 +544,7 @@ export function createApiFactory(
token = callbackOrToken;
}
return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.id, token);
return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.identifier, token);
},
saveAll: (includeUntitled?) => {
return extHostWorkspace.saveAll(includeUntitled);
......@@ -601,7 +602,7 @@ export function createApiFactory(
},
getConfiguration(section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration {
resource = arguments.length === 1 ? void 0 : resource;
return extHostConfiguration.getConfiguration(section, resource, extension.id);
return extHostConfiguration.getConfiguration(section, resource, extension.identifier);
},
registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) {
return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider);
......@@ -629,7 +630,7 @@ export function createApiFactory(
return exthostCommentProviders.registerDocumentCommentProvider(provider);
}),
registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => {
return exthostCommentProviders.registerWorkspaceCommentProvider(extension.id, provider);
return exthostCommentProviders.registerWorkspaceCommentProvider(extension.identifier, provider);
}),
onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
......@@ -837,6 +838,7 @@ export function originalFSPath(uri: URI): string {
class Extension<T> implements vscode.Extension<T> {
private _extensionService: ExtHostExtensionService;
private _identifier: CanonicalExtensionIdentifier;
public id: string;
public extensionPath: string;
......@@ -844,21 +846,22 @@ class Extension<T> implements vscode.Extension<T> {
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) {
this._extensionService = extensionService;
this.id = description.id;
this._identifier = description.identifier;
this.id = description.identifier.value;
this.extensionPath = paths.normalize(originalFSPath(description.extensionLocation), true);
this.packageJSON = description;
}
get isActive(): boolean {
return this._extensionService.isActivated(this.id);
return this._extensionService.isActivated(this._identifier);
}
get exports(): T {
return <T>this._extensionService.getExtensionExports(this.id);
return <T>this._extensionService.getExtensionExports(this._identifier);
}
activate(): Thenable<T> {
return this._extensionService.activateByIdWithErrors(this.id, new ExtensionActivatedByAPI(false)).then(() => this.exports);
return this._extensionService.activateByIdWithErrors(this._identifier, new ExtensionActivatedByAPI(false)).then(() => this.exports);
}
}
......@@ -882,10 +885,10 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
// get extension id from filename and api for extension
const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
if (ext) {
let apiImpl = extApiImpl.get(ext.id);
let apiImpl = extApiImpl.get(CanonicalExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
apiImpl = factory(ext, extensionRegistry);
extApiImpl.set(ext.id, apiImpl);
extApiImpl.set(CanonicalExtensionIdentifier.toKey(ext.identifier), apiImpl);
}
return apiImpl;
}
......@@ -893,7 +896,7 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
// fall back to a default implementation
if (!defaultApiImpl) {
let extensionPathsPretty = '';
extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.id}\n`);
extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
defaultApiImpl = factory(nullExtensionDescription, extensionRegistry);
}
......
......@@ -43,6 +43,7 @@ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
......@@ -113,7 +114,7 @@ export interface CommentProviderFeatures {
export interface MainThreadCommentsShape extends IDisposable {
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void;
$unregisterDocumentCommentProvider(handle: number): void;
$registerWorkspaceCommentProvider(handle: number, extensionId: string): void;
$registerWorkspaceCommentProvider(handle: number, extensionId: CanonicalExtensionIdentifier): void;
$unregisterWorkspaceCommentProvider(handle: number): void;
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void;
}
......@@ -439,7 +440,7 @@ export interface MainThreadQuickOpenShape extends IDisposable {
}
export interface MainThreadStatusBarShape extends IDisposable {
$setEntry(id: number, extensionId: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void;
$setEntry(id: number, extensionId: CanonicalExtensionIdentifier, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void;
$dispose(id: number): void;
}
......@@ -460,7 +461,7 @@ export interface WebviewPanelShowOptions {
}
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: string, extensionLocation: UriComponents): void;
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: CanonicalExtensionIdentifier, extensionLocation: UriComponents): void;
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void;
$setTitle(handle: WebviewPanelHandle, value: string): void;
......@@ -487,7 +488,7 @@ export interface ExtHostWebviewsShape {
}
export interface MainThreadUrlsShape extends IDisposable {
$registerUriHandler(handle: number, extensionId: string): Promise<void>;
$registerUriHandler(handle: number, extensionId: CanonicalExtensionIdentifier): Promise<void>;
$unregisterUriHandler(handle: number): Promise<void>;
}
......@@ -537,11 +538,11 @@ export interface MainThreadTaskShape extends IDisposable {
export interface MainThreadExtensionServiceShape extends IDisposable {
$localShowMessage(severity: Severity, msg: string): void;
$onWillActivateExtension(extensionId: string): void;
$onDidActivateExtension(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void;
$onExtensionActivationFailed(extensionId: string): void;
$onExtensionRuntimeError(extensionId: string, error: SerializedError): void;
$addMessage(extensionId: string, severity: Severity, message: string): void;
$onWillActivateExtension(extensionId: CanonicalExtensionIdentifier): void;
$onDidActivateExtension(extensionId: CanonicalExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void;
$onExtensionActivationFailed(extensionId: CanonicalExtensionIdentifier): void;
$onExtensionRuntimeError(extensionId: CanonicalExtensionIdentifier, error: SerializedError): void;
$addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void;
}
export interface SCMProviderFeatures {
......@@ -736,7 +737,7 @@ export interface ExtHostSearchShape {
export interface ExtHostExtensionServiceShape {
$resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority>;
$startExtensionHost(enabledExtensionIds: string[]): Promise<void>;
$startExtensionHost(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise<void>;
$activateByEvent(activationEvent: string): Promise<void>;
}
......
......@@ -13,6 +13,7 @@ import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShap
import { CommandsConverter } from './extHostCommands';
import { IRange } from 'vs/editor/common/core/range';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtHostComments implements ExtHostCommentsShape {
private static handlePool = 0;
......@@ -31,7 +32,7 @@ export class ExtHostComments implements ExtHostCommentsShape {
}
registerWorkspaceCommentProvider(
extensionId: string,
extensionId: CanonicalExtensionIdentifier,
provider: vscode.WorkspaceCommentProvider
): vscode.Disposable {
const handle = ExtHostComments.handlePool++;
......
......@@ -16,6 +16,7 @@ import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configu
import { ResourceMap } from 'vs/base/common/map';
import { ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { isObject } from 'vs/base/common/types';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
function lookUp(tree: any, key: string) {
if (key) {
......@@ -60,7 +61,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData));
}
getConfiguration(section?: string, resource?: URI, extensionId?: string): vscode.WorkspaceConfiguration {
getConfiguration(section?: string, resource?: URI, extensionId?: CanonicalExtensionIdentifier): vscode.WorkspaceConfiguration {
const config = this._toReadonlyValue(section
? lookUp(this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace), section)
: this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace));
......@@ -187,9 +188,9 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
return readonlyProxy(result);
}
private _validateConfigurationAccess(key: string, resource: URI, extensionId: string): void {
private _validateConfigurationAccess(key: string, resource: URI, extensionId: CanonicalExtensionIdentifier): void {
const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes[key];
const extensionIdText = extensionId ? `[${extensionId}] ` : '';
const extensionIdText = extensionId ? `[${extensionId.value}] ` : '';
if (ConfigurationScope.RESOURCE === scope) {
if (resource === void 0) {
console.warn(`${extensionIdText}Accessing a resource scoped configuration without providing a resource is not expected. To get the effective value for '${key}', provide the URI of a resource or 'null' for any resource.`);
......
......@@ -8,10 +8,11 @@ import { URI } from 'vs/base/common/uri';
import { MainContext, IMainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, DecorationData, DecorationRequest, DecorationReply } from 'vs/workbench/api/node/extHost.protocol';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
interface ProviderData {
provider: vscode.DecorationProvider;
extensionId: string;
extensionId: CanonicalExtensionIdentifier;
}
export class ExtHostDecorations implements ExtHostDecorationsShape {
......@@ -25,10 +26,10 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadDecorations);
}
registerDecorationProvider(provider: vscode.DecorationProvider, extensionId: string): vscode.Disposable {
registerDecorationProvider(provider: vscode.DecorationProvider, extensionId: CanonicalExtensionIdentifier): vscode.Disposable {
const handle = ExtHostDecorations._handlePool++;
this._provider.set(handle, { provider, extensionId });
this._proxy.$registerDecorationProvider(handle, extensionId);
this._proxy.$registerDecorationProvider(handle, extensionId.value);
const listener = provider.onDidChangeDecorations(e => {
this._proxy.$onDidChange(handle, !e ? null : Array.isArray(e) ? e : [e]);
......@@ -52,7 +53,7 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
const { provider, extensionId } = this._provider.get(handle);
return Promise.resolve(provider.provideDecoration(URI.revive(uri), token)).then(data => {
if (data && data.letter && data.letter.length !== 1) {
console.warn(`INVALID decoration from extension '${extensionId}'. The 'letter' must be set and be one character, not '${data.letter}'.`);
console.warn(`INVALID decoration from extension '${extensionId.value}'. The 'letter' must be set and be one character, not '${data.letter}'.`);
}
result[id] = data && <DecorationData>[data.priority, data.bubble, data.title, data.letter, data.color, data.source];
}, err => {
......
......@@ -83,7 +83,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
}, err => {
this._logService.error(`onWillSaveTextDocument-listener from extension '${extension.id}' threw ERROR`);
this._logService.error(`onWillSaveTextDocument-listener from extension '${extension.identifier.value}' threw ERROR`);
this._logService.error(err);
if (!(err instanceof Error) || (<Error>err).message !== 'concurrent_edits') {
......@@ -91,7 +91,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
this._badListeners.set(listener, !errors ? 1 : errors + 1);
if (errors > this._thresholds.errors) {
this._logService.info(`onWillSaveTextDocument-listener from extension '${extension.id}' will now be IGNORED because of timeouts and/or errors`);
this._logService.info(`onWillSaveTextDocument-listener from extension '${extension.identifier.value}' will now be IGNORED because of timeouts and/or errors`);
}
}
return false;
......@@ -132,7 +132,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
const handle = setTimeout(() => reject(new Error('timeout')), this._thresholds.timeout);
return Promise.all(promises).then(edits => {
this._logService.debug(`onWillSaveTextDocument-listener from extension '${extension.id}' finished after ${(Date.now() - t1)}ms`);
this._logService.debug(`onWillSaveTextDocument-listener from extension '${extension.identifier.value}' finished after ${(Date.now() - t1)}ms`);
clearTimeout(handle);
resolve(edits);
}).catch(err => {
......
......@@ -8,8 +8,8 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = Promise.resolve<void>(void 0);
export interface IExtensionMemento {
......@@ -192,8 +192,8 @@ export class ExtensionsActivator {
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _host: IExtensionsActivatorHost;
private readonly _activatingExtensions: { [extensionId: string]: Promise<void>; };
private readonly _activatedExtensions: { [extensionId: string]: ActivatedExtension; };
private readonly _activatingExtensions: Map<string, Promise<void>>;
private readonly _activatedExtensions: Map<string, ActivatedExtension>;
/**
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
*/
......@@ -202,20 +202,24 @@ export class ExtensionsActivator {
constructor(registry: ExtensionDescriptionRegistry, host: IExtensionsActivatorHost) {
this._registry = registry;
this._host = host;
this._activatingExtensions = {};
this._activatedExtensions = {};
this._activatingExtensions = new Map<string, Promise<void>>();
this._activatedExtensions = new Map<string, ActivatedExtension>();
this._alreadyActivatedEvents = Object.create(null);
}
public isActivated(extensionId: string): boolean {
return hasOwnProperty.call(this._activatedExtensions, extensionId);
public isActivated(extensionId: CanonicalExtensionIdentifier): boolean {
const extensionKey = CanonicalExtensionIdentifier.toKey(extensionId);
return this._activatedExtensions.has(extensionKey);
}
public getActivatedExtension(extensionId: string): ActivatedExtension {
if (!hasOwnProperty.call(this._activatedExtensions, extensionId)) {
throw new Error('Extension `' + extensionId + '` is not known or not activated');
public getActivatedExtension(extensionId: CanonicalExtensionIdentifier): ActivatedExtension {
const extensionKey = CanonicalExtensionIdentifier.toKey(extensionId);
if (!this._activatedExtensions.has(extensionKey)) {
throw new Error('Extension `' + extensionId.value + '` is not known or not activated');
}
return this._activatedExtensions[extensionId];
return this._activatedExtensions.get(extensionKey);
}
public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): Promise<void> {
......@@ -228,7 +232,7 @@ export class ExtensionsActivator {
});
}
public activateById(extensionId: string, reason: ExtensionActivationReason): Promise<void> {
public activateById(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
let desc = this._registry.getExtensionDescription(extensionId);
if (!desc) {
throw new Error('Extension `' + extensionId + '` is not known');
......@@ -251,31 +255,31 @@ export class ExtensionsActivator {
if (!depDesc) {
// Error condition 1: unknown dependency
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' as the depending extension '{1}' is not found. Please install or enable the depending extension and reload the window.", currentExtension.displayName || currentExtension.id, depId));
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' as the depending extension '{1}' is not found. Please install or enable the depending extension and reload the window.", currentExtension.displayName || currentExtension.identifier.value, depId));
const error = new Error(`Unknown dependency '${depId}'`);
this._activatedExtensions[currentExtension.id] = new FailedExtension(error);
this._activatedExtensions.set(CanonicalExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
return;
}
if (hasOwnProperty.call(this._activatedExtensions, depId)) {
let dep = this._activatedExtensions[depId];
if (this._activatedExtensions.has(CanonicalExtensionIdentifier.toKey(depId))) {
let dep = this._activatedExtensions.get(CanonicalExtensionIdentifier.toKey(depId));
if (dep.activationFailed) {
// Error condition 2: a dependency has already failed activation
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' as the depending extension '{1}' is failed to activate.", currentExtension.displayName || currentExtension.id, depId));
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' as the depending extension '{1}' is failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId));
const error = new Error(`Dependency ${depId} failed to activate`);
(<any>error).detail = dep.activationFailedError;
this._activatedExtensions[currentExtension.id] = new FailedExtension(error);
this._activatedExtensions.set(CanonicalExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
return;
}
} else {
// must first wait for the dependency to activate
currentExtensionGetsGreenLight = false;
greenExtensions[depId] = depDesc;
greenExtensions[CanonicalExtensionIdentifier.toKey(depId)] = depDesc;
}
}
if (currentExtensionGetsGreenLight) {
greenExtensions[currentExtension.id] = currentExtension;
greenExtensions[CanonicalExtensionIdentifier.toKey(currentExtension.identifier)] = currentExtension;
} else {
redExtensions.push(currentExtension);
}
......@@ -287,7 +291,7 @@ export class ExtensionsActivator {
return Promise.resolve(void 0);
}
extensionDescriptions = extensionDescriptions.filter((p) => !hasOwnProperty.call(this._activatedExtensions, p.id));
extensionDescriptions = extensionDescriptions.filter((p) => !this._activatedExtensions.has(CanonicalExtensionIdentifier.toKey(p.identifier)));
if (extensionDescriptions.length === 0) {
return Promise.resolve(void 0);
}
......@@ -296,9 +300,9 @@ export class ExtensionsActivator {
// More than 10 dependencies deep => most likely a dependency loop
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
// Error condition 3: dependency loop
this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension '{0}' failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].id));
this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension '{0}' failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].identifier.value));
const error = new Error('More than 10 levels of dependencies (most likely a dependency loop)');
this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(error);
this._activatedExtensions.set(CanonicalExtensionIdentifier.toKey(extensionDescriptions[i].identifier), new FailedExtension(error));
}
return Promise.resolve(void 0);
}
......@@ -312,8 +316,9 @@ export class ExtensionsActivator {
// Make sure no red is also green
for (let i = 0, len = red.length; i < len; i++) {
if (greenMap[red[i].id]) {
delete greenMap[red[i].id];
const redExtensionKey = CanonicalExtensionIdentifier.toKey(red[i].identifier);
if (greenMap[redExtensionKey]) {
delete greenMap[redExtensionKey];
}
}
......@@ -333,25 +338,27 @@ export class ExtensionsActivator {
}
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<void> {
if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) {
const extensionKey = CanonicalExtensionIdentifier.toKey(extensionDescription.identifier);
if (this._activatedExtensions.has(extensionKey)) {
return Promise.resolve(void 0);
}
if (hasOwnProperty.call(this._activatingExtensions, extensionDescription.id)) {
return this._activatingExtensions[extensionDescription.id];
if (this._activatingExtensions.has(extensionKey)) {
return this._activatingExtensions.get(extensionKey);
}
this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription, reason).then(void 0, (err) => {
this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionDescription.id, err.message));
console.error('Activating extension `' + extensionDescription.id + '` failed: ', err.message);
this._activatingExtensions.set(extensionKey, this._host.actualActivateExtension(extensionDescription, reason).then(void 0, (err) => {
this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionDescription.identifier.value, err.message));
console.error('Activating extension `' + extensionDescription.identifier.value + '` failed: ', err.message);
console.log('Here is the error stack: ', err.stack);
// Treat the extension as being empty
return new FailedExtension(err);
}).then((x: ActivatedExtension) => {
this._activatedExtensions[extensionDescription.id] = x;
delete this._activatingExtensions[extensionDescription.id];
});
this._activatedExtensions.set(extensionKey, x);
this._activatingExtensions.delete(extensionKey);
}));
return this._activatingExtensions[extensionDescription.id];
return this._activatingExtensions.get(extensionKey);
}
}
......@@ -25,6 +25,7 @@ import { connectProxyResolver } from 'vs/workbench/node/proxyResolver';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
class ExtensionMemento implements IExtensionMemento {
......@@ -97,13 +98,13 @@ class ExtensionStoragePath {
workspaceValue(extension: IExtensionDescription): string {
if (this._value) {
return path.join(this._value, extension.id);
return path.join(this._value, extension.identifier.value);
}
return undefined;
}
globalValue(extension: IExtensionDescription): string {
return path.join(this._environment.globalStorageHome.fsPath, extension.id);
return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value);
}
private async _getOrCreateWorkspaceStoragePath(): Promise<string> {
......@@ -234,7 +235,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
let allPromises: Promise<void>[] = [];
try {
const allExtensions = this._registry.getAllExtensionDescriptions();
const allExtensionsIds = allExtensions.map(ext => ext.id);
const allExtensionsIds = allExtensions.map(ext => ext.identifier);
const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));
allPromises = activatedExtensions.map((extensionId) => {
......@@ -246,7 +247,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
await allPromises;
}
public isActivated(extensionId: string): boolean {
public isActivated(extensionId: CanonicalExtensionIdentifier): boolean {
if (this._barrier.isOpen()) {
return this._activator.isActivated(extensionId);
}
......@@ -258,11 +259,11 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return this._activator.activateByEvent(activationEvent, reason);
}
private _activateById(extensionId: string, reason: ExtensionActivationReason): Promise<void> {
private _activateById(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
return this._activator.activateById(extensionId, reason);
}
public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): Promise<void> {
public activateByIdWithErrors(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
return this._activateById(extensionId, reason).then(() => {
const extension = this._activator.getActivatedExtension(extensionId);
if (extension.activationFailed) {
......@@ -277,7 +278,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return this._barrier.wait().then(_ => this._registry);
}
public getExtensionExports(extensionId: string): IExtensionAPI {
public getExtensionExports(extensionId: CanonicalExtensionIdentifier): IExtensionAPI {
if (this._barrier.isOpen()) {
return this._activator.getActivatedExtension(extensionId).exports;
} else {
......@@ -300,7 +301,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return this._extensionPathIndex;
}
private _deactivate(extensionId: string): Promise<void> {
private _deactivate(extensionId: CanonicalExtensionIdentifier): Promise<void> {
let result = Promise.resolve(void 0);
if (!this._barrier.isOpen()) {
......@@ -338,22 +339,22 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return result;
}
public addMessage(extensionId: string, severity: Severity, message: string): void {
public addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void {
this._mainThreadExtensionsProxy.$addMessage(extensionId, severity, message);
}
// --- impl
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.id);
this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
const activationTimes = activatedExtension.activationTimes;
let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
return activatedExtension;
}, (err) => {
this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.id);
this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.identifier);
this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
throw err;
});
......@@ -392,23 +393,23 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
}
this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`);
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return Promise.all<any>([
loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
});
}
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<IExtensionContext> {
let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage);
let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage);
let globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
let workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
return Promise.all([
globalState.whenReady,
workspaceState.whenReady,
......@@ -423,12 +424,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
storagePath: this._storagePath.workspaceValue(extensionDescription),
get globalStoragePath(): string { checkProposedApiEnabled(extensionDescription); return that._storagePath.globalValue(extensionDescription); },
asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
logPath: that._extHostLogService.getLogDirectory(extensionDescription.id)
logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier)
});
});
}
private static _callActivate(logService: ILogService, extensionId: string, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
private static _callActivate(logService: ILogService, extensionId: CanonicalExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
// Make sure the extension's surface is not undefined
extensionModule = extensionModule || {
activate: undefined,
......@@ -440,11 +441,11 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
});
}
private static _callActivateOptional(logService: ILogService, extensionId: string, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
private static _callActivateOptional(logService: ILogService, extensionId: CanonicalExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
if (typeof extensionModule.activate === 'function') {
try {
activationTimesBuilder.activateCallStart();
logService.trace(`ExtensionService#_callActivateOptional ${extensionId}`);
logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);
const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
activationTimesBuilder.activateCallStop();
......@@ -509,13 +510,13 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return Promise.resolve(void 0);
}
const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.id, fileName))).then(() => { });
const globPatternPromise = this._activateIfGlobPatterns(desc.id, globPatterns);
const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.identifier, fileName))).then(() => { });
const globPatternPromise = this._activateIfGlobPatterns(desc.identifier, globPatterns);
return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
}
private async _activateIfFileName(workspace: IWorkspaceData, extensionId: string, fileName: string): Promise<void> {
private async _activateIfFileName(workspace: IWorkspaceData, extensionId: CanonicalExtensionIdentifier, fileName: string): Promise<void> {
// find exact path
for (const { uri } of workspace.folders) {
......@@ -531,8 +532,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return undefined;
}
private async _activateIfGlobPatterns(extensionId: string, globPatterns: string[]): Promise<void> {
this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId}, entryPoint: workspaceContains`);
private async _activateIfGlobPatterns(extensionId: CanonicalExtensionIdentifier, globPatterns: string[]): Promise<void> {
this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`);
if (globPatterns.length === 0) {
return Promise.resolve(void 0);
......@@ -634,7 +635,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
throw new Error(`Not implemented`);
}
public $startExtensionHost(enabledExtensionIds: string[]): Promise<void> {
public $startExtensionHost(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise<void> {
this._registry.keepOnly(enabledExtensionIds);
return this._startExtensionHost();
}
......@@ -678,7 +679,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
}
*/
let event = {
id: extensionDescription.id,
id: extensionDescription.identifier.value,
name: extensionDescription.name,
extensionVersion: extensionDescription.version,
publisherDisplayName: extensionDescription.publisher,
......
......@@ -25,6 +25,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
// --- adapter
......@@ -289,7 +290,7 @@ class CodeActionAdapter {
private readonly _diagnostics: ExtHostDiagnostics,
private readonly _provider: vscode.CodeActionProvider,
private readonly _logService: ILogService,
private readonly _extensionId: string
private readonly _extensionId: CanonicalExtensionIdentifier
) { }
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionDto[]> {
......@@ -330,9 +331,9 @@ class CodeActionAdapter {
} else {
if (codeActionContext.only) {
if (!candidate.kind) {
this._logService.warn(`${this._extensionId} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
this._logService.warn(`${this._extensionId.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
} else if (!codeActionContext.only.contains(candidate.kind)) {
this._logService.warn(`${this._extensionId} -Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
this._logService.warn(`${this._extensionId.value} -Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
}
}
......@@ -975,14 +976,14 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
let t1: number;
if (data.extension) {
t1 = Date.now();
this._logService.trace(`[${data.extension.id}] INVOKE provider '${(ctor as any).name}'`);
this._logService.trace(`[${data.extension.identifier.value}] INVOKE provider '${(ctor as any).name}'`);
}
let p = callback(data.adapter);
if (data.extension) {
Promise.resolve(p).then(
() => this._logService.trace(`[${data.extension.id}] provider DONE after ${Date.now() - t1}ms`),
() => this._logService.trace(`[${data.extension.identifier.value}] provider DONE after ${Date.now() - t1}ms`),
err => {
this._logService.error(`[${data.extension.id}] provider FAILED`);
this._logService.error(`[${data.extension.identifier.value}] provider FAILED`);
this._logService.error(err);
}
);
......@@ -1081,7 +1082,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- extra info
registerHoverProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable {
registerHoverProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: CanonicalExtensionIdentifier): vscode.Disposable {
const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider), extension);
this._proxy.$registerHoverProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
......@@ -1118,7 +1119,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- quick fix
registerCodeActionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.id), extension);
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.identifier), extension);
this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), metadata && metadata.providedCodeActionKinds ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined);
return this._createDisposable(handle);
}
......
......@@ -9,6 +9,7 @@ import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
......@@ -29,7 +30,7 @@ export class ExtHostLogService extends DelegatedLogService implements ILogServic
this.setLevel(level);
}
getLogDirectory(extensionID: string): string {
return join(this._logsPath, extensionID);
getLogDirectory(extensionID: CanonicalExtensionIdentifier): string {
return join(this._logsPath, extensionID.value);
}
}
......@@ -14,6 +14,7 @@ import { ExtHostQuickOpenShape, IMainContext, MainContext, MainThreadQuickOpenSh
import { URI } from 'vs/base/common/uri';
import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/node/extHostTypes';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export type Item = string | QuickPickItem;
......@@ -169,13 +170,13 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
// ---- QuickInput
createQuickPick<T extends QuickPickItem>(extensionId: string, enableProposedApi: boolean): QuickPick<T> {
createQuickPick<T extends QuickPickItem>(extensionId: CanonicalExtensionIdentifier, enableProposedApi: boolean): QuickPick<T> {
const session = new ExtHostQuickPick(this._proxy, extensionId, enableProposedApi, () => this._sessions.delete(session._id));
this._sessions.set(session._id, session);
return session;
}
createInputBox(extensionId: string): InputBox {
createInputBox(extensionId: CanonicalExtensionIdentifier): InputBox {
const session = new ExtHostInputBox(this._proxy, extensionId, () => this._sessions.delete(session._id));
this._sessions.set(session._id, session);
return session;
......@@ -256,7 +257,7 @@ class ExtHostQuickInput implements QuickInput {
this._onDidChangeValueEmitter
];
constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: string, private _onDidDispose: () => void) {
constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: CanonicalExtensionIdentifier, private _onDidDispose: () => void) {
}
get title() {
......@@ -479,7 +480,7 @@ class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implem
private _selectedItems: T[] = [];
private _onDidChangeSelectionEmitter = new Emitter<T[]>();
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, enableProposedApi: boolean, onDispose: () => void) {
constructor(proxy: MainThreadQuickOpenShape, extensionId: CanonicalExtensionIdentifier, enableProposedApi: boolean, onDispose: () => void) {
super(proxy, extensionId, onDispose);
this._disposables.push(
this._onDidChangeActiveEmitter,
......@@ -580,7 +581,7 @@ class ExtHostInputBox extends ExtHostQuickInput implements InputBox {
private _prompt: string;
private _validationMessage: string;
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, onDispose: () => void) {
constructor(proxy: MainThreadQuickOpenShape, extensionId: CanonicalExtensionIdentifier, onDispose: () => void) {
super(proxy, extensionId, onDispose);
this.update({ type: 'inputBox' });
}
......
......@@ -17,6 +17,7 @@ import * as vscode from 'vscode';
import { ISplice } from 'vs/base/common/sequence';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
type ProviderHandle = number;
type GroupHandle = number;
......@@ -178,7 +179,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
get validateInput(): IValidateInput {
if (!this._extension.enableProposedApi) {
throw new Error(`[${this._extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.id}`);
throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`);
}
return this._validateInput;
......@@ -186,7 +187,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
set validateInput(fn: IValidateInput) {
if (!this._extension.enableProposedApi) {
throw new Error(`[${this._extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.id}`);
throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`);
}
if (fn && typeof fn !== 'function') {
......@@ -584,24 +585,24 @@ export class ExtHostSCM implements ExtHostSCMShape {
}
createSourceControl(extension: IExtensionDescription, id: string, label: string, rootUri: vscode.Uri | undefined): vscode.SourceControl {
this.logService.trace('ExtHostSCM#createSourceControl', extension.id, id, label, rootUri);
this.logService.trace('ExtHostSCM#createSourceControl', extension.identifier.value, id, label, rootUri);
const handle = ExtHostSCM._handlePool++;
const sourceControl = new ExtHostSourceControl(extension, this._proxy, this._commands, id, label, rootUri);
this._sourceControls.set(handle, sourceControl);
const sourceControls = this._sourceControlsByExtension.get(extension.id) || [];
const sourceControls = this._sourceControlsByExtension.get(CanonicalExtensionIdentifier.toKey(extension.identifier)) || [];
sourceControls.push(sourceControl);
this._sourceControlsByExtension.set(extension.id, sourceControls);
this._sourceControlsByExtension.set(CanonicalExtensionIdentifier.toKey(extension.identifier), sourceControls);
return sourceControl;
}
// Deprecated
getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox {
this.logService.trace('ExtHostSCM#getLastInputBox', extension.id);
this.logService.trace('ExtHostSCM#getLastInputBox', extension.identifier.value);
const sourceControls = this._sourceControlsByExtension.get(extension.id);
const sourceControls = this._sourceControlsByExtension.get(CanonicalExtensionIdentifier.toKey(extension.identifier));
const sourceControl = sourceControls && sourceControls[sourceControls.length - 1];
const inputBox = sourceControl && sourceControl.inputBox;
......
......@@ -7,6 +7,7 @@ import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/
import { StatusBarAlignment as ExtHostStatusBarAlignment, Disposable, ThemeColor } from './extHostTypes';
import { StatusBarItem, StatusBarAlignment } from 'vscode';
import { MainContext, MainThreadStatusBarShape, IMainContext } from './extHost.protocol';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtHostStatusBarEntry implements StatusBarItem {
private static ID_GEN = 0;
......@@ -25,9 +26,9 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
private _timeoutHandle: any;
private _proxy: MainThreadStatusBarShape;
private _extensionId: string;
private _extensionId: CanonicalExtensionIdentifier;
constructor(proxy: MainThreadStatusBarShape, extensionId: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
constructor(proxy: MainThreadStatusBarShape, extensionId: CanonicalExtensionIdentifier, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
this._id = ExtHostStatusBarEntry.ID_GEN++;
this._proxy = proxy;
this._alignment = alignment;
......@@ -166,7 +167,7 @@ export class ExtHostStatusBar {
this._statusMessage = new StatusBarMessage(this);
}
createStatusBarEntry(extensionId: string, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
createStatusBarEntry(extensionId: CanonicalExtensionIdentifier, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, extensionId, alignment, priority);
}
......
......@@ -205,7 +205,7 @@ namespace TaskDTO {
definition,
name: value.name,
source: {
extensionId: extension.id,
extensionId: extension.identifier.value,
label: value.source,
scope: scope
},
......
......@@ -8,6 +8,7 @@ import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from
import { URI, UriComponents } from 'vs/base/common/uri';
import { toDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtHostUrls implements ExtHostUrlsShape {
......@@ -23,18 +24,18 @@ export class ExtHostUrls implements ExtHostUrlsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadUrls);
}
registerUriHandler(extensionId: string, handler: vscode.UriHandler): vscode.Disposable {
if (this.handles.has(extensionId)) {
registerUriHandler(extensionId: CanonicalExtensionIdentifier, handler: vscode.UriHandler): vscode.Disposable {
if (this.handles.has(CanonicalExtensionIdentifier.toKey(extensionId))) {
throw new Error(`Protocol handler already registered for extension ${extensionId}`);
}
const handle = ExtHostUrls.HandlePool++;
this.handles.add(extensionId);
this.handles.add(CanonicalExtensionIdentifier.toKey(extensionId));
this.handlers.set(handle, handler);
this._proxy.$registerUriHandler(handle, extensionId);
return toDisposable(() => {
this.handles.delete(extensionId);
this.handles.delete(CanonicalExtensionIdentifier.toKey(extensionId));
this.handlers.delete(handle);
this._proxy.$unregisterUriHandler(handle);
});
......
......@@ -252,7 +252,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
};
const handle = ExtHostWebviews.newHandle();
this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.id, extension.extensionLocation);
this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation);
const webview = new ExtHostWebview(handle, this._proxy, options);
const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview);
......
......@@ -24,6 +24,7 @@ import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/query
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
function isFolderEqual(folderA: URI, folderB: URI): boolean {
return isEqual(folderA, folderB, !isLinux);
......@@ -345,8 +346,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
// --- search ---
findFiles(include: string | RelativePattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.Uri[]> {
this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId}, entryPoint: findFiles`);
findFiles(include: string | RelativePattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: CanonicalExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.Uri[]> {
this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId.value}, entryPoint: findFiles`);
let includePattern: string;
let includeFolder: URI;
......@@ -380,8 +381,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
}
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete> {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`);
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: CanonicalExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete> {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId.value}, entryPoint: findTextInFiles`);
const requestId = this._requestIdProvider.getNext();
......
......@@ -18,6 +18,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IAction } from 'vs/base/common/actions';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test';
......@@ -44,7 +45,7 @@ export interface IViewContainersRegistry {
*
* @returns the registered ViewContainer.
*/
registerViewContainer(id: string, extensionId?: string): ViewContainer;
registerViewContainer(id: string, extensionId?: CanonicalExtensionIdentifier): ViewContainer;
/**
* Returns the view container with given id.
......@@ -56,7 +57,7 @@ export interface IViewContainersRegistry {
}
export class ViewContainer {
protected constructor(readonly id: string, readonly extensionId: string) { }
protected constructor(readonly id: string, readonly extensionId: CanonicalExtensionIdentifier) { }
}
class ViewContainersRegistryImpl implements IViewContainersRegistry {
......@@ -70,7 +71,7 @@ class ViewContainersRegistryImpl implements IViewContainersRegistry {
return values(this.viewContainers);
}
registerViewContainer(id: string, extensionId: string): ViewContainer {
registerViewContainer(id: string, extensionId: CanonicalExtensionIdentifier): ViewContainer {
if (!this.viewContainers.has(id)) {
const viewContainer = new class extends ViewContainer {
constructor() {
......
......@@ -107,7 +107,7 @@ export class ExtensionHostMain {
const data = errors.transformErrorForSerialization(err);
const extension = extensionErrors.get(err);
if (extension) {
mainThreadExtensions.$onExtensionRuntimeError(extension.id, data);
mainThreadExtensions.$onExtensionRuntimeError(extension.identifier, data);
} else {
mainThreadErrors.$onUnexpectedError(data);
}
......
......@@ -1030,7 +1030,7 @@ export class DebugService implements IDebugService {
breakpointCount: this.model.getBreakpoints().length,
exceptionBreakpoints: this.model.getExceptionBreakpoints(),
watchExpressionsCount: this.model.getWatchExpressions().length,
extensionName: extension.id,
extensionName: extension.identifier.value,
isBuiltin: extension.isBuiltin,
launchJsonExists: root && !!this.configurationService.getValue<IGlobalConfig>('launch', { resource: root.uri })
});
......
......@@ -12,6 +12,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
import { URI } from 'vs/base/common/uri';
import { ExecutableDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
suite('Debug - Debugger', () => {
......@@ -49,6 +50,7 @@ suite('Debug - Debugger', () => {
const extensionDescriptor0 = {
id: 'adapter',
identifier: new CanonicalExtensionIdentifier('adapter'),
name: 'myAdapter',
version: '1.0.0',
publisher: 'vscode',
......@@ -65,6 +67,7 @@ suite('Debug - Debugger', () => {
const extensionDescriptor1 = {
id: 'extension1',
identifier: new CanonicalExtensionIdentifier('extension1'),
name: 'extension1',
version: '1.0.0',
publisher: 'vscode',
......@@ -87,6 +90,7 @@ suite('Debug - Debugger', () => {
const extensionDescriptor2 = {
id: 'extension2',
identifier: new CanonicalExtensionIdentifier('extension2'),
name: 'extension2',
version: '1.0.0',
publisher: 'vscode',
......
......@@ -20,6 +20,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { randomPort } from 'vs/base/node/ports';
import product from 'vs/platform/node/product';
import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService {
......@@ -121,12 +122,12 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
this._onDidChangeLastProfile.fire(void 0);
}
getUnresponsiveProfile(extensionId: string): IExtensionHostProfile | undefined {
return this._unresponsiveProfiles.get(extensionId);
getUnresponsiveProfile(extensionId: CanonicalExtensionIdentifier): IExtensionHostProfile | undefined {
return this._unresponsiveProfiles.get(CanonicalExtensionIdentifier.toKey(extensionId));
}
setUnresponsiveProfile(extensionId: string, profile: IExtensionHostProfile): void {
this._unresponsiveProfiles.set(extensionId, profile);
setUnresponsiveProfile(extensionId: CanonicalExtensionIdentifier, profile: IExtensionHostProfile): void {
this._unresponsiveProfiles.set(CanonicalExtensionIdentifier.toKey(extensionId), profile);
this._setLastProfile(profile);
}
......
......@@ -18,7 +18,7 @@ import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IE
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate';
import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionManagementServerService, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { isUIExtension } from 'vs/platform/extensions/common/extensions';
import { isUIExtension, CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ShowViewletAction } from 'vs/workbench/browser/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
......@@ -804,7 +804,7 @@ export class InstallInRemoteServerAction extends Action implements IExtensionAct
return !!server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer!.authority;
});
if (!installedInRemoteServer) {
this.enabled = !this.runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id }));
this.enabled = !this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, { id: this.extension.id }));
}
}
}
......@@ -906,7 +906,7 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio
return;
}
this.enabled = false;
if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id }) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, { id: this.extension.id }) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && !!this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
}
}
......@@ -942,7 +942,7 @@ export class DisableGloballyAction extends Action implements IExtensionAction {
return;
}
this.enabled = false;
if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id }))) {
if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, { id: this.extension.id }))) {
this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && !!this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
}
}
......@@ -1248,7 +1248,7 @@ export class ReloadAction extends Action {
const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
const isDisabled = this.extension.local ? !this.extensionEnablementService.isEnabled(this.extension.local) : false;
const isEnabled = this.extension.local ? this.extensionEnablementService.isEnabled(this.extension.local) : false;
const runningExtension = runningExtensions.filter(e => areSameExtensions(e, this.extension))[0];
const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension))[0];
if (installed && installed.local) {
if (runningExtension) {
......@@ -2392,7 +2392,7 @@ export class DisabledStatusLabelAction extends Action {
}
this.class = `${DisabledStatusLabelAction.Class} hide`;
this.tooltip = '';
if (this.extension && this.extension.local && !this.extension.isMalicious && !this.runningExtensions.some(e => e.id === this.extension.id)) {
if (this.extension && this.extension.local && !this.extension.isMalicious && !this.runningExtensions.some(e => CanonicalExtensionIdentifier.equals(e.identifier, this.extension.id))) {
if (this.extensionManagementServerService.remoteExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService) && this.extension.locals) {
const installedInRemoteServer = this.extension.locals.some(local => {
const server = this.extensionManagementServerService.getExtensionManagementServer(local.location);
......
......@@ -20,6 +20,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput';
import { generateUuid } from 'vs/base/common/uuid';
import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution {
......@@ -132,7 +133,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
}
// add to running extensions view
this._extensionProfileService.setUnresponsiveProfile(extension.id, profile);
this._extensionProfileService.setUnresponsiveProfile(extension.identifier, profile);
// print message to log
const path = join(tmpdir(), `exthost-${Math.random().toString(16).slice(2, 8)}.cpuprofile`);
......@@ -162,7 +163,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
// prompt: only when you can file an issue
const reportAction = new ReportExtensionIssueAction({
marketplaceInfo: this._anotherExtensionService.local.filter(value => value.id === extension.id)[0],
marketplaceInfo: this._anotherExtensionService.local.filter(value => CanonicalExtensionIdentifier.equals(value.id, extension.identifier))[0],
description: extension,
unresponsiveProfile: profile,
status: undefined,
......@@ -172,10 +173,10 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
}
// only blame once per extension, don't blame too often
if (this._blame.has(extension.id) || this._blame.size >= 3) {
if (this._blame.has(CanonicalExtensionIdentifier.toKey(extension.identifier)) || this._blame.size >= 3) {
return;
}
this._blame.add(extension.id);
this._blame.add(CanonicalExtensionIdentifier.toKey(extension.identifier));
// user-facing message when very bad...
this._notificationService.prompt(
......
......@@ -152,7 +152,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
if (installed && installed.local) {
const installedExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location);
const isSameExtensionRunning = runningExtensions.some(e => {
if (!areSameExtensions(e, extension)) {
if (!areSameExtensions({ id: e.identifier.value }, extension)) {
return false;
}
const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation);
......
......@@ -293,7 +293,7 @@ export class ExtensionsListView extends ViewletPanel {
const result = local
.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))
.filter(e => runningExtensions.every(r => !areSameExtensions(r, e))
.filter(e => runningExtensions.every(r => !areSameExtensions({ id: r.identifier.value }, e))
&& (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)
&& (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category))));
......@@ -308,7 +308,7 @@ export class ExtensionsListView extends ViewletPanel {
const result = local
.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))
.filter(e => runningExtensions.some(r => areSameExtensions(r, e))
.filter(e => runningExtensions.some(r => areSameExtensions({ id: r.identifier.value }, e))
&& (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)
&& (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category))));
......
......@@ -42,6 +42,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { join } from 'path';
import { onUnexpectedError } from 'vs/base/common/errors';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey<string>('profileSessionState', 'none');
......@@ -66,8 +67,8 @@ export interface IExtensionHostProfileService {
startProfiling(): void;
stopProfiling(): void;
getUnresponsiveProfile(extensionId: string): IExtensionHostProfile;
setUnresponsiveProfile(extensionId: string, profile: IExtensionHostProfile): void;
getUnresponsiveProfile(extensionId: CanonicalExtensionIdentifier): IExtensionHostProfile;
setUnresponsiveProfile(extensionId: CanonicalExtensionIdentifier, profile: IExtensionHostProfile): void;
}
interface IExtensionProfileInformation {
......@@ -163,7 +164,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
private _resolveExtensions(): IRuntimeExtension[] {
let marketplaceMap: { [id: string]: IExtension; } = Object.create(null);
for (let extension of this._extensionsWorkbenchService.local) {
marketplaceMap[extension.id] = extension;
marketplaceMap[CanonicalExtensionIdentifier.toKey(extension.id)] = extension;
}
let statusMap = this._extensionService.getExtensionsStatus();
......@@ -177,10 +178,10 @@ export class RuntimeExtensionsEditor extends BaseEditor {
const id = this._profileInfo.ids[i];
const delta = this._profileInfo.deltas[i];
let extensionSegments = segments[id];
let extensionSegments = segments[CanonicalExtensionIdentifier.toKey(id)];
if (!extensionSegments) {
extensionSegments = [];
segments[id] = extensionSegments;
segments[CanonicalExtensionIdentifier.toKey(id)] = extensionSegments;
}
extensionSegments.push(currentStartTime);
......@@ -195,7 +196,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
let profileInfo: IExtensionProfileInformation | null = null;
if (this._profileInfo) {
let extensionSegments = segments[extensionDescription.id] || [];
let extensionSegments = segments[CanonicalExtensionIdentifier.toKey(extensionDescription.identifier)] || [];
let extensionTotalTime = 0;
for (let j = 0, lenJ = extensionSegments.length / 2; j < lenJ; j++) {
const startTime = extensionSegments[2 * j];
......@@ -211,10 +212,10 @@ export class RuntimeExtensionsEditor extends BaseEditor {
result[i] = {
originalIndex: i,
description: extensionDescription,
marketplaceInfo: marketplaceMap[extensionDescription.id],
status: statusMap[extensionDescription.id],
marketplaceInfo: marketplaceMap[CanonicalExtensionIdentifier.toKey(extensionDescription.identifier)],
status: statusMap[extensionDescription.identifier.value],
profileInfo: profileInfo,
unresponsiveProfile: this._extensionHostProfileService.getUnresponsiveProfile(extensionDescription.id)
unresponsiveProfile: this._extensionHostProfileService.getUnresponsiveProfile(extensionDescription.identifier)
};
}
......@@ -352,7 +353,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
clearNode(data.msgContainer);
if (this._extensionHostProfileService.getUnresponsiveProfile(element.description.id)) {
if (this._extensionHostProfileService.getUnresponsiveProfile(element.description.identifier)) {
const el = $('span');
el.innerHTML = renderOcticons(` $(alert) Unresponsive`);
el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze.");
......@@ -512,7 +513,7 @@ export class ReportExtensionIssueAction extends Action {
// unresponsive extension host caused
reason = 'Performance';
title = 'Extension causes high cpu load';
let path = join(os.homedir(), `${extension.description.id}-unresponsive.cpuprofile.txt`);
let path = join(os.homedir(), `${extension.description.identifier.value}-unresponsive.cpuprofile.txt`);
task = async () => {
const profiler = await import('v8-inspect-profiler');
const data = profiler.rewriteAbsolutePaths({ profile: <any>extension.unresponsiveProfile.data }, 'pii_removed');
......
......@@ -36,6 +36,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
suite('ExtensionsActions Test', () => {
......@@ -778,7 +779,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
assert.ok(testObject.enabled);
});
});
......@@ -823,7 +824,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
assert.ok(testObject.enabled);
});
});
......@@ -840,7 +841,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
assert.ok(testObject.enabled);
});
});
......@@ -853,7 +854,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
assert.ok(!testObject.enabled);
});
});
......@@ -867,7 +868,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
assert.ok(!testObject.enabled);
});
});
......@@ -879,7 +880,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryGallery()
.then(page => {
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, page.firstPage[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, page.firstPage[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
assert.ok(!testObject.enabled);
});
});
......@@ -890,7 +891,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryGallery()
.then(page => {
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, page.firstPage[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, page.firstPage[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
installEvent.fire({ identifier: gallery.identifier, gallery });
assert.ok(!testObject.enabled);
......@@ -903,7 +904,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
uninstallEvent.fire(local.identifier);
assert.ok(!testObject.enabled);
});
......@@ -1016,7 +1017,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is newly installed', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
......@@ -1037,7 +1038,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is installed and uninstalled', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
......@@ -1055,7 +1056,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is uninstalled', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -1074,7 +1075,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is uninstalled and installed', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.0', extensionLocation: URI.file('pub.a') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), version: '1.0.0', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -1094,7 +1095,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is updated while running', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.1', extensionLocation: URI.file('pub.a') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), version: '1.0.1', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a', { version: '1.0.1' });
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
......@@ -1115,7 +1116,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is updated when not running', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const local = aLocalExtension('a', { version: '1.0.1' });
return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled)
.then(() => {
......@@ -1136,7 +1137,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is disabled when running', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a');
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
......@@ -1152,7 +1153,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension enablement is toggled when running', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.0', extensionLocation: URI.file('pub.a') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), version: '1.0.0', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a');
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
......@@ -1167,7 +1168,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is enabled when not running', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const local = aLocalExtension('a');
return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled)
.then(() => {
......@@ -1187,7 +1188,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension enablement is toggled when not running', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const local = aLocalExtension('a');
return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled)
.then(() => {
......@@ -1205,7 +1206,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is updated when not running and enabled', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const local = aLocalExtension('a', { version: '1.0.1' });
return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled)
.then(() => {
......@@ -1230,7 +1231,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when a localization extension is newly installed', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
......@@ -1245,7 +1246,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when a localization extension is updated while running', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.1', extensionLocation: URI.file('pub.a') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), version: '1.0.1', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a', { version: '1.0.1', contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } });
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
......
......@@ -38,6 +38,7 @@ import { IExperimentService, ExperimentService, ExperimentState, ExperimentActio
import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
suite('ExtensionsListView Tests', () => {
......@@ -123,11 +124,11 @@ suite('ExtensionsListView Tests', () => {
instantiationService.stub(IExtensionService, {
getExtensions: () => {
return Promise.resolve([
{ id: localEnabledTheme.galleryIdentifier.id },
{ id: localEnabledLanguage.galleryIdentifier.id },
{ id: localRandom.galleryIdentifier.id },
{ id: builtInTheme.galleryIdentifier.id },
{ id: builtInBasic.galleryIdentifier.id }
{ identifier: new CanonicalExtensionIdentifier(localEnabledTheme.galleryIdentifier.id) },
{ identifier: new CanonicalExtensionIdentifier(localEnabledLanguage.galleryIdentifier.id) },
{ identifier: new CanonicalExtensionIdentifier(localRandom.galleryIdentifier.id) },
{ identifier: new CanonicalExtensionIdentifier(builtInTheme.galleryIdentifier.id) },
{ identifier: new CanonicalExtensionIdentifier(builtInBasic.galleryIdentifier.id) }
]);
}
});
......
......@@ -12,6 +12,7 @@ import * as Objects from 'vs/base/common/objects';
import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import * as Tasks from 'vs/workbench/parts/tasks/common/tasks';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
const taskDefinitionSchema: IJSONSchema = {
......@@ -45,7 +46,7 @@ namespace Configuration {
properties?: IJSONSchemaMap;
}
export function from(value: TaskDefinition, extensionId: string, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition | undefined {
export function from(value: TaskDefinition, extensionId: CanonicalExtensionIdentifier, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition | undefined {
if (!value) {
return undefined;
}
......@@ -62,7 +63,7 @@ namespace Configuration {
}
}
}
return { extensionId, taskType, required: required, properties: value.properties ? Objects.deepClone(value.properties) : {} };
return { extensionId: extensionId.value, taskType, required: required, properties: value.properties ? Objects.deepClone(value.properties) : {} };
}
}
......@@ -98,7 +99,7 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry {
for (let extension of extensions) {
let taskTypes = extension.value;
for (let taskType of taskTypes) {
let type = Configuration.from(taskType, extension.description.id, extension.collector);
let type = Configuration.from(taskType, extension.description.identifier, extension.collector);
if (type) {
this.taskTypes[type.taskType] = type;
}
......
......@@ -97,7 +97,7 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint<I
});
defaultConfigurationExtPoint.setHandler(extensions => {
registeredDefaultConfigurations = extensions.map(extension => {
const id = extension.description.id;
const id = extension.description.identifier;
const name = extension.description.name;
const defaults = objects.deepClone(extension.value);
return <IDefaultConfigurationExtension>{
......@@ -135,9 +135,9 @@ configurationExtPoint.setHandler(extensions => {
validateProperties(configuration, extension);
configuration.id = node.id || extension.description.id || extension.description.uuid;
configuration.id = node.id || extension.description.identifier.value;
configuration.contributedByExtension = true;
configuration.title = configuration.title || extension.description.displayName || extension.description.id;
configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value;
configurations.push(configuration);
}
......
......@@ -8,9 +8,10 @@ import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export interface IExtensionDescription {
readonly id: string;
readonly identifier: CanonicalExtensionIdentifier;
readonly name: string;
readonly uuid?: string;
readonly displayName?: string;
......@@ -34,7 +35,7 @@ export interface IExtensionDescription {
}
export const nullExtensionDescription = Object.freeze(<IExtensionDescription>{
id: 'nullExtensionDescription',
identifier: new CanonicalExtensionIdentifier('nullExtensionDescription'),
name: 'Null Extension Description',
version: '0.0.0',
publisher: 'vscode',
......@@ -49,7 +50,7 @@ export const IExtensionService = createDecorator<IExtensionService>('extensionSe
export interface IMessage {
type: Severity;
message: string;
extensionId: string;
extensionId: CanonicalExtensionIdentifier;
extensionPointId: string;
}
......@@ -154,7 +155,7 @@ export interface IExtensionService extends ICpuProfilerTarget {
* Fired when extensions status changes.
* The event contains the ids of the extensions that have changed.
*/
onDidChangeExtensionsStatus: Event<string[]>;
onDidChangeExtensionsStatus: Event<CanonicalExtensionIdentifier[]>;
/**
* An event that is fired when activation happens.
......@@ -244,5 +245,5 @@ export function checkProposedApiEnabled(extension: IExtensionDescription): void
}
export function throwProposedApiError(extension: IExtensionDescription): never {
throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`);
throw new Error(`[${extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.identifier.value}`);
}
......@@ -35,7 +35,7 @@ export class ExtensionMessageCollector {
this._messageHandler({
type: type,
message: message,
extensionId: this._extension.id,
extensionId: this._extension.identifier,
extensionPointId: this._extensionPointId
});
}
......
......@@ -16,7 +16,7 @@ import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE } from 'vs/platform/extensions/common/extensions';
import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import pkg from 'vs/platform/node/package';
import product from 'vs/platform/node/product';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
......@@ -66,24 +66,32 @@ export class CachedExtensionScanner {
public startScanningExtensions(log: ILog): void {
CachedExtensionScanner._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, this._extensionEnablementService, log)
.then(({ system, user, development }) => {
let result: { [extensionId: string]: IExtensionDescription; } = {};
let result = new Map<string, IExtensionDescription>();
system.forEach((systemExtension) => {
result[systemExtension.id] = systemExtension;
const extensionKey = CanonicalExtensionIdentifier.toKey(systemExtension.identifier);
if (result.has(extensionKey)) {
log.warn(systemExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, systemExtension.extensionLocation.fsPath));
}
result.set(extensionKey, systemExtension);
});
user.forEach((userExtension) => {
if (result.hasOwnProperty(userExtension.id)) {
log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionLocation.fsPath, userExtension.extensionLocation.fsPath));
const extensionKey = CanonicalExtensionIdentifier.toKey(userExtension.identifier);
if (result.has(extensionKey)) {
log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, userExtension.extensionLocation.fsPath));
}
result[userExtension.id] = userExtension;
result.set(extensionKey, userExtension);
});
development.forEach(developedExtension => {
log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionLocation.fsPath));
if (result.hasOwnProperty(developedExtension.id)) {
log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionLocation.fsPath, developedExtension.extensionLocation.fsPath));
const extensionKey = CanonicalExtensionIdentifier.toKey(developedExtension.identifier);
if (result.has(extensionKey)) {
log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, developedExtension.extensionLocation.fsPath));
}
result[developedExtension.id] = developedExtension;
result.set(extensionKey, developedExtension);
});
return Object.keys(result).map(name => result[name]);
let r: IExtensionDescription[] = [];
result.forEach((value) => r.push(value));
return r;
})
.then(this._scannedExtensionsResolve, this._scannedExtensionsReject);
}
......
......@@ -18,6 +18,7 @@ import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
// Enable to see detailed message communication between window and extension host
const LOG_EXTENSION_HOST_COMMUNICATION = false;
......@@ -177,7 +178,7 @@ export class ExtensionHostProcessManager extends Disposable {
return this._extensionHostProcessProxy.then(proxy => proxy.value.$resolveAuthority(remoteAuthority));
}
public start(enabledExtensionIds: string[]): Promise<void> {
public start(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise<void> {
return this._extensionHostProcessProxy.then(proxy => proxy.value.$startExtensionHost(enabledExtensionIds));
}
}
......
......@@ -56,7 +56,7 @@ export class ExtensionHostProfiler {
} else if (segmentId === 'self' && node.callFrame.url) {
let extension = searchTree.findSubstr(node.callFrame.url);
if (extension) {
segmentId = extension.id;
segmentId = extension.identifier.value;
}
}
idsToSegmentId.set(node.id, segmentId);
......
......@@ -29,6 +29,7 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/n
import { ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol';
import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner';
import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/electron-browser/extensionHostProcessManager';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = Promise.resolve<void>(void 0);
......@@ -36,6 +37,18 @@ const DYNAMIC_EXTENSION_POINTS = false;
schema.properties.engines.properties.vscode.default = `^${pkg.version}`;
let productAllowProposedApi: Set<string> = null;
function allowProposedApiFromProduct(id: CanonicalExtensionIdentifier): boolean {
// create set if needed
if (productAllowProposedApi === null) {
productAllowProposedApi = new Set<string>();
if (isNonEmptyArray(product.extensionAllowedProposedApi)) {
product.extensionAllowedProposedApi.forEach((id) => productAllowProposedApi.add(CanonicalExtensionIdentifier.toKey(id)));
}
}
return productAllowProposedApi.has(CanonicalExtensionIdentifier.toKey(id));
}
export class ExtensionService extends Disposable implements IExtensionService {
public _serviceBrand: any;
......@@ -44,15 +57,15 @@ export class ExtensionService extends Disposable implements IExtensionService {
private _registry: ExtensionDescriptionRegistry;
private readonly _installedExtensionsReady: Barrier;
private readonly _isDev: boolean;
private readonly _extensionsMessages: { [id: string]: IMessage[] };
private readonly _extensionsMessages: Map<string, IMessage[]>;
private _allRequestedActivateEvents: { [activationEvent: string]: boolean; };
private readonly _extensionScanner: CachedExtensionScanner;
private readonly _onDidRegisterExtensions: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event;
private readonly _onDidChangeExtensionsStatus: Emitter<string[]> = this._register(new Emitter<string[]>());
public readonly onDidChangeExtensionsStatus: Event<string[]> = this._onDidChangeExtensionsStatus.event;
private readonly _onDidChangeExtensionsStatus: Emitter<CanonicalExtensionIdentifier[]> = this._register(new Emitter<CanonicalExtensionIdentifier[]>());
public readonly onDidChangeExtensionsStatus: Event<CanonicalExtensionIdentifier[]> = this._onDidChangeExtensionsStatus.event;
private readonly _onWillActivateByEvent = this._register(new Emitter<IWillActivateEvent>());
public readonly onWillActivateByEvent: Event<IWillActivateEvent> = this._onWillActivateByEvent.event;
......@@ -62,9 +75,9 @@ export class ExtensionService extends Disposable implements IExtensionService {
// --- Members used per extension host process
private _extensionHostProcessManagers: ExtensionHostProcessManager[];
private _extensionHostActiveExtensions: { [id: string]: boolean; };
private _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; };
private _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; };
private _extensionHostActiveExtensions: Map<string, CanonicalExtensionIdentifier>;
private _extensionHostProcessActivationTimes: Map<string, ActivationTimes>;
private _extensionHostExtensionRuntimeErrors: Map<string, Error[]>;
constructor(
@IInstantiationService private readonly _instantiationService: IInstantiationService,
......@@ -81,14 +94,14 @@ export class ExtensionService extends Disposable implements IExtensionService {
this._registry = null;
this._installedExtensionsReady = new Barrier();
this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
this._extensionsMessages = {};
this._extensionsMessages = new Map<string, IMessage[]>();
this._allRequestedActivateEvents = Object.create(null);
this._extensionScanner = this._instantiationService.createInstance(CachedExtensionScanner);
this._extensionHostProcessManagers = [];
this._extensionHostActiveExtensions = Object.create(null);
this._extensionHostProcessActivationTimes = Object.create(null);
this._extensionHostExtensionRuntimeErrors = Object.create(null);
this._extensionHostActiveExtensions = new Map<string, CanonicalExtensionIdentifier>();
this._extensionHostProcessActivationTimes = new Map<string, ActivationTimes>();
this._extensionHostExtensionRuntimeErrors = new Map<string, Error[]>();
this._startDelayed(this._lifecycleService);
......@@ -121,7 +134,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
return false;
}
this._registry.remove(extension.id);
this._registry.remove(extension.identifier);
// TODO@Alex: remove from the extension host
const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null);
......@@ -147,7 +160,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
private _canRemoveExtension(extension: IExtensionDescription): boolean {
if (this._extensionHostActiveExtensions[extension.id]) {
if (this._extensionHostActiveExtensions.has(CanonicalExtensionIdentifier.toKey(extension.identifier))) {
// Extension is running, cannot remove it safely
return false;
}
......@@ -205,15 +218,18 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
private _stopExtensionHostProcess(): void {
const previouslyActivatedExtensionIds = Object.keys(this._extensionHostProcessActivationTimes);
let previouslyActivatedExtensionIds: CanonicalExtensionIdentifier[] = [];
this._extensionHostActiveExtensions.forEach((value) => {
previouslyActivatedExtensionIds.push(value);
});
for (let i = 0; i < this._extensionHostProcessManagers.length; i++) {
this._extensionHostProcessManagers[i].dispose();
}
this._extensionHostProcessManagers = [];
this._extensionHostActiveExtensions = Object.create(null);
this._extensionHostProcessActivationTimes = Object.create(null);
this._extensionHostExtensionRuntimeErrors = Object.create(null);
this._extensionHostActiveExtensions = new Map<string, CanonicalExtensionIdentifier>();
this._extensionHostProcessActivationTimes = new Map<string, ActivationTimes>();
this._extensionHostExtensionRuntimeErrors = new Map<string, Error[]>();
if (previouslyActivatedExtensionIds.length > 0) {
this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
......@@ -343,11 +359,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
const extensions = this._registry.getAllExtensionDescriptions();
for (let i = 0, len = extensions.length; i < len; i++) {
const extension = extensions[i];
const id = extension.id;
result[id] = {
messages: this._extensionsMessages[id],
activationTimes: this._extensionHostProcessActivationTimes[id],
runtimeErrors: this._extensionHostExtensionRuntimeErrors[id],
const extensionKey = CanonicalExtensionIdentifier.toKey(extension.identifier);
result[extension.identifier.value] = {
messages: this._extensionsMessages.get(extensionKey),
activationTimes: this._extensionHostProcessActivationTimes.get(extensionKey),
runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey),
};
}
}
......@@ -397,7 +413,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
const extensionHost = this._extensionHostProcessManagers[0];
const extensions = await this._extensionScanner.scannedExtensions;
const enabledExtensions = await this._getRuntimeExtensions(extensions);
extensionHost.start(enabledExtensions.map(extension => extension.id));
extensionHost.start(enabledExtensions.map(extension => extension.identifier));
this._onHasExtensions(enabledExtensions);
}
......@@ -416,28 +432,34 @@ export class ExtensionService extends Disposable implements IExtensionService {
perf.mark('extensionHostReady');
this._installedExtensionsReady.open();
this._onDidRegisterExtensions.fire(void 0);
this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.id));
this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.identifier));
}
private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise<IExtensionDescription[]> {
return this._extensionEnablementService.getDisabledExtensions()
.then(disabledExtensions => {
const result: { [extensionId: string]: IExtensionDescription; } = {};
const runtimeExtensions: IExtensionDescription[] = [];
const extensionsToDisable: IExtensionIdentifier[] = [];
const userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }];
const enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || [];
let enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || [];
const notFound = (id: string) => nls.localize('notFound', "Extension \`{0}\` cannot use PROPOSED API as it cannot be found", id);
if (enableProposedApiFor.length) {
let allProposed = (enableProposedApiFor instanceof Array ? enableProposedApiFor : [enableProposedApiFor]);
allProposed.forEach(id => {
if (!allExtensions.some(description => description.id === id)) {
if (!allExtensions.some(description => CanonicalExtensionIdentifier.equals(description.identifier, id))) {
console.error(notFound(id));
}
});
// Make enabled proposed API be lowercase for case insensitive comparison
if (Array.isArray(enableProposedApiFor)) {
enableProposedApiFor = enableProposedApiFor.map(id => id.toLowerCase());
} else {
enableProposedApiFor = enableProposedApiFor.toLowerCase();
}
}
const enableProposedApiForAll = !this._environmentService.isBuilt ||
......@@ -448,22 +470,21 @@ export class ExtensionService extends Disposable implements IExtensionService {
const isExtensionUnderDevelopment = this._environmentService.isExtensionDevelopment && isEqualOrParent(extension.extensionLocation, this._environmentService.extensionDevelopmentLocationURI);
// Do not disable extensions under development
if (!isExtensionUnderDevelopment) {
if (disabledExtensions.some(disabled => areSameExtensions(disabled, extension))) {
if (disabledExtensions.some(disabled => areSameExtensions(disabled, { id: extension.identifier.value }))) {
continue;
}
}
if (!extension.isBuiltin) {
// Check if the extension is changed to system extension
const userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: extension.id }))[0];
const userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: extension.identifier.value }))[0];
if (userMigratedSystemExtension) {
extensionsToDisable.push(userMigratedSystemExtension);
continue;
}
}
result[extension.id] = this._updateEnableProposedApi(extension, enableProposedApiForAll, enableProposedApiFor);
runtimeExtensions.push(this._updateEnableProposedApi(extension, enableProposedApiForAll, enableProposedApiFor));
}
const runtimeExtensions = Object.keys(result).map(name => result[name]);
this._telemetryService.publicLog('extensionsScanned', {
totalCount: runtimeExtensions.length,
......@@ -486,9 +507,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
private _updateEnableProposedApi(extension: IExtensionDescription, enableProposedApiForAll: boolean, enableProposedApiFor: string | string[]): IExtensionDescription {
if (isNonEmptyArray(product.extensionAllowedProposedApi)
&& product.extensionAllowedProposedApi.indexOf(extension.id) >= 0
) {
if (allowProposedApiFromProduct(extension.identifier)) {
// fast lane -> proposed api is available to all extensions
// that are listed in product.json-files
extension.enableProposedApi = true;
......@@ -496,29 +515,30 @@ export class ExtensionService extends Disposable implements IExtensionService {
} else if (extension.enableProposedApi && !extension.isBuiltin) {
if (
!enableProposedApiForAll &&
enableProposedApiFor.indexOf(extension.id) < 0
enableProposedApiFor.indexOf(extension.identifier.value.toLowerCase()) < 0
) {
extension.enableProposedApi = false;
console.error(`Extension '${extension.id} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`);
console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`);
} else {
// proposed api is available when developing or when an extension was explicitly
// spelled out via a command line argument
console.warn(`Extension '${extension.id}' uses PROPOSED API which is subject to change and removal without notice.`);
console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`);
}
}
return extension;
}
private _handleExtensionPointMessage(msg: IMessage) {
const extensionKey = CanonicalExtensionIdentifier.toKey(msg.extensionId);
if (!this._extensionsMessages[msg.extensionId]) {
this._extensionsMessages[msg.extensionId] = [];
if (!this._extensionsMessages.has(extensionKey)) {
this._extensionsMessages.set(extensionKey, []);
}
this._extensionsMessages[msg.extensionId].push(msg);
this._extensionsMessages.get(extensionKey).push(msg);
const extension = this._registry.getExtensionDescription(msg.extensionId);
const strMsg = `[${msg.extensionId}]: ${msg.message}`;
const strMsg = `[${msg.extensionId.value}]: ${msg.message}`;
if (extension && extension.isUnderDevelopment) {
// This message is about the extension currently being developed
this._showMessageToUser(msg.type, strMsg);
......@@ -537,7 +557,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
*/
this._telemetryService.publicLog('extensionsMessage', {
type, extensionId, extensionPointId, message
type, extensionId: extensionId.value, extensionPointId, message
});
}
}
......@@ -587,28 +607,30 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
}
public _onWillActivateExtension(extensionId: string): void {
this._extensionHostActiveExtensions[extensionId] = true;
public _onWillActivateExtension(extensionId: CanonicalExtensionIdentifier): void {
this._extensionHostActiveExtensions.set(CanonicalExtensionIdentifier.toKey(extensionId), extensionId);
}
public _onDidActivateExtension(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
public _onDidActivateExtension(extensionId: CanonicalExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
this._extensionHostProcessActivationTimes.set(CanonicalExtensionIdentifier.toKey(extensionId), new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent));
this._onDidChangeExtensionsStatus.fire([extensionId]);
}
public _onExtensionRuntimeError(extensionId: string, err: Error): void {
if (!this._extensionHostExtensionRuntimeErrors[extensionId]) {
this._extensionHostExtensionRuntimeErrors[extensionId] = [];
public _onExtensionRuntimeError(extensionId: CanonicalExtensionIdentifier, err: Error): void {
const extensionKey = CanonicalExtensionIdentifier.toKey(extensionId);
if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) {
this._extensionHostExtensionRuntimeErrors.set(extensionKey, []);
}
this._extensionHostExtensionRuntimeErrors[extensionId].push(err);
this._extensionHostExtensionRuntimeErrors.get(extensionKey).push(err);
this._onDidChangeExtensionsStatus.fire([extensionId]);
}
public _addMessage(extensionId: string, severity: Severity, message: string): void {
if (!this._extensionsMessages[extensionId]) {
this._extensionsMessages[extensionId] = [];
public _addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void {
const extensionKey = CanonicalExtensionIdentifier.toKey(extensionId);
if (!this._extensionsMessages.has(extensionKey)) {
this._extensionsMessages.set(extensionKey, []);
}
this._extensionsMessages[extensionId].push({
this._extensionsMessages.get(extensionKey).push({
type: severity,
message: message,
extensionId: null,
......
......@@ -16,6 +16,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { IURLHandler, IURLService } from 'vs/platform/url/common/url';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_SECONDS = 30 * 1000;
......@@ -29,8 +30,8 @@ export const IExtensionUrlHandler = createDecorator<IExtensionUrlHandler>('inact
export interface IExtensionUrlHandler {
readonly _serviceBrand: any;
registerExtensionHandler(extensionId: string, handler: IURLHandler): void;
unregisterExtensionHandler(extensionId: string): void;
registerExtensionHandler(extensionId: CanonicalExtensionIdentifier, handler: IURLHandler): void;
unregisterExtensionHandler(extensionId: CanonicalExtensionIdentifier): void;
}
/**
......@@ -80,7 +81,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
}
const extensionId = uri.authority;
const wasHandlerAvailable = this.extensionHandlers.has(extensionId);
const wasHandlerAvailable = this.extensionHandlers.has(CanonicalExtensionIdentifier.toKey(extensionId));
const extension = await this.extensionService.getExtension(extensionId);
if (!extension) {
......@@ -101,7 +102,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
}
}
const handler = this.extensionHandlers.get(extensionId);
const handler = this.extensionHandlers.get(CanonicalExtensionIdentifier.toKey(extensionId));
if (handler) {
if (!wasHandlerAvailable) {
......@@ -115,34 +116,34 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
// collect URI for eventual extension activation
const timestamp = new Date().getTime();
let uris = this.uriBuffer.get(extensionId);
let uris = this.uriBuffer.get(CanonicalExtensionIdentifier.toKey(extensionId));
if (!uris) {
uris = [];
this.uriBuffer.set(extensionId, uris);
this.uriBuffer.set(CanonicalExtensionIdentifier.toKey(extensionId), uris);
}
uris.push({ timestamp, uri });
// activate the extension
await this.extensionService.activateByEvent(`onUri:${extensionId}`);
await this.extensionService.activateByEvent(`onUri:${CanonicalExtensionIdentifier.toKey(extensionId)}`);
return true;
}
registerExtensionHandler(extensionId: string, handler: IURLHandler): void {
this.extensionHandlers.set(extensionId, handler);
registerExtensionHandler(extensionId: CanonicalExtensionIdentifier, handler: IURLHandler): void {
this.extensionHandlers.set(CanonicalExtensionIdentifier.toKey(extensionId), handler);
const uris = this.uriBuffer.get(extensionId) || [];
const uris = this.uriBuffer.get(CanonicalExtensionIdentifier.toKey(extensionId)) || [];
for (const { uri } of uris) {
handler.handleURL(uri);
}
this.uriBuffer.delete(extensionId);
this.uriBuffer.delete(CanonicalExtensionIdentifier.toKey(extensionId));
}
unregisterExtensionHandler(extensionId: string): void {
this.extensionHandlers.delete(extensionId);
unregisterExtensionHandler(extensionId: CanonicalExtensionIdentifier): void {
this.extensionHandlers.delete(CanonicalExtensionIdentifier.toKey(extensionId));
}
private async handleUnhandledURL(uri: URI, extensionIdentifier: IExtensionIdentifier): Promise<void> {
......
......@@ -4,14 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
const hasOwnProperty = Object.hasOwnProperty;
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtensionDescriptionRegistry {
private _extensionDescriptions: IExtensionDescription[];
private _extensionsMap: { [extensionId: string]: IExtensionDescription; };
private _extensionsMap: Map<string, IExtensionDescription>;
private _extensionsArr: IExtensionDescription[];
private _activationMap: { [activationEvent: string]: IExtensionDescription[]; };
private _activationMap: Map<string, IExtensionDescription[]>;
constructor(extensionDescriptions: IExtensionDescription[]) {
this._extensionDescriptions = extensionDescriptions;
......@@ -19,20 +18,20 @@ export class ExtensionDescriptionRegistry {
}
private _initialize(): void {
this._extensionsMap = {};
this._extensionsMap = new Map<string, IExtensionDescription>();
this._extensionsArr = [];
this._activationMap = {};
this._activationMap = new Map<string, IExtensionDescription[]>();
for (let i = 0, len = this._extensionDescriptions.length; i < len; i++) {
let extensionDescription = this._extensionDescriptions[i];
if (hasOwnProperty.call(this._extensionsMap, extensionDescription.id)) {
if (this._extensionsMap.has(CanonicalExtensionIdentifier.toKey(extensionDescription.identifier))) {
// No overwriting allowed!
console.error('Extension `' + extensionDescription.id + '` is already registered');
console.error('Extension `' + extensionDescription.identifier.value + '` is already registered');
continue;
}
this._extensionsMap[extensionDescription.id] = extensionDescription;
this._extensionsMap.set(CanonicalExtensionIdentifier.toKey(extensionDescription.identifier), extensionDescription);
this._extensionsArr.push(extensionDescription);
if (Array.isArray(extensionDescription.activationEvents)) {
......@@ -41,47 +40,49 @@ export class ExtensionDescriptionRegistry {
// TODO@joao: there's no easy way to contribute this
if (activationEvent === 'onUri') {
activationEvent = `onUri:${extensionDescription.id}`;
activationEvent = `onUri:${CanonicalExtensionIdentifier.toKey(extensionDescription.identifier)}`;
}
this._activationMap[activationEvent] = this._activationMap[activationEvent] || [];
this._activationMap[activationEvent].push(extensionDescription);
if (!this._activationMap.has(activationEvent)) {
this._activationMap.set(activationEvent, []);
}
this._activationMap.get(activationEvent).push(extensionDescription);
}
}
}
}
public keepOnly(extensionIds: string[]): void {
public keepOnly(extensionIds: CanonicalExtensionIdentifier[]): void {
let toKeep = new Set<string>();
extensionIds.forEach(extensionId => toKeep.add(extensionId));
this._extensionDescriptions = this._extensionDescriptions.filter(extension => toKeep.has(extension.id));
extensionIds.forEach(extensionId => toKeep.add(CanonicalExtensionIdentifier.toKey(extensionId)));
this._extensionDescriptions = this._extensionDescriptions.filter(extension => toKeep.has(CanonicalExtensionIdentifier.toKey(extension.identifier)));
this._initialize();
}
public remove(extensionId: string): void {
this._extensionDescriptions = this._extensionDescriptions.filter(extension => extension.id !== extensionId);
public remove(extensionId: CanonicalExtensionIdentifier): void {
this._extensionDescriptions = this._extensionDescriptions.filter(extension => !CanonicalExtensionIdentifier.equals(extension.identifier, extensionId));
this._initialize();
}
public containsActivationEvent(activationEvent: string): boolean {
return hasOwnProperty.call(this._activationMap, activationEvent);
return this._activationMap.has(activationEvent);
}
public getExtensionDescriptionsForActivationEvent(activationEvent: string): IExtensionDescription[] {
if (!hasOwnProperty.call(this._activationMap, activationEvent)) {
if (!this._activationMap.has(activationEvent)) {
return [];
}
return this._activationMap[activationEvent].slice(0);
return this._activationMap.get(activationEvent).slice(0);
}
public getAllExtensionDescriptions(): IExtensionDescription[] {
return this._extensionsArr.slice(0);
}
public getExtensionDescription(extensionId: string): IExtensionDescription | null {
if (!hasOwnProperty.call(this._extensionsMap, extensionId)) {
public getExtensionDescription(extensionId: CanonicalExtensionIdentifier | string): IExtensionDescription | null {
if (!this._extensionsMap.has(CanonicalExtensionIdentifier.toKey(extensionId))) {
return null;
}
return this._extensionsMap[extensionId];
return this._extensionsMap.get(CanonicalExtensionIdentifier.toKey(extensionId));
}
}
......@@ -16,6 +16,7 @@ import { getGalleryExtensionId, getLocalExtensionId, groupByExtension } from 'vs
import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
const MANIFEST_FILE = 'package.json';
......@@ -300,6 +301,8 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
// Relax the readonly properties here, it is the one place where we check and normalize values
export interface IRelaxedExtensionDescription {
id: string;
uuid?: string;
identifier: CanonicalExtensionIdentifier;
name: string;
version: string;
publisher: string;
......@@ -339,6 +342,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
// id := `publisher.name`
extensionDescription.id = `${extensionDescription.publisher}.${extensionDescription.name}`;
extensionDescription.identifier = new CanonicalExtensionIdentifier(extensionDescription.id);
// main := absolutePath(`main`)
if (extensionDescription.main) {
......@@ -577,7 +581,7 @@ export class ExtensionScanner {
if (!isBuiltin) {
// Filter out outdated extensions
const byExtension: IExtensionDescription[][] = groupByExtension(extensionDescriptions, e => ({ id: e.id, uuid: e.uuid }));
const byExtension: IExtensionDescription[][] = groupByExtension(extensionDescriptions, e => ({ id: e.identifier.value, uuid: e.uuid }));
extensionDescriptions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0]);
}
......@@ -624,11 +628,11 @@ export class ExtensionScanner {
return Promise.all([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => {
let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null);
for (let i = 0, len = builtinExtensions.length; i < len; i++) {
resultMap[builtinExtensions[i].id] = builtinExtensions[i];
resultMap[CanonicalExtensionIdentifier.toKey(builtinExtensions[i].identifier)] = builtinExtensions[i];
}
// Overwrite with extensions found in extra
for (let i = 0, len = extraBuiltinExtensions.length; i < len; i++) {
resultMap[extraBuiltinExtensions[i].id] = extraBuiltinExtensions[i];
resultMap[CanonicalExtensionIdentifier.toKey(extraBuiltinExtensions[i].identifier)] = extraBuiltinExtensions[i];
}
let resultArr = Object.keys(resultMap).map((id) => resultMap[id]);
......
......@@ -63,7 +63,7 @@ export class ColorThemeStore {
themesExtPoint.setHandler((extensions) => {
for (let ext of extensions) {
let extensionData = {
extensionId: ext.description.id,
extensionId: ext.description.identifier.value,
extensionPublisher: ext.description.publisher,
extensionName: ext.description.name,
extensionIsBuiltin: ext.description.isBuiltin
......
......@@ -58,7 +58,7 @@ export class FileIconThemeStore {
iconThemeExtPoint.setHandler((extensions) => {
for (let ext of extensions) {
let extensionData = {
extensionId: ext.description.id,
extensionId: ext.description.identifier.value,
extensionPublisher: ext.description.publisher,
extensionName: ext.description.name,
extensionIsBuiltin: ext.description.isBuiltin
......
......@@ -17,6 +17,7 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e
import { NullLogService } from 'vs/platform/log/common/log';
import { isResourceTextEdit, ResourceTextEdit } from 'vs/editor/common/modes';
import { timeout } from 'vs/base/common/async';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
suite('ExtHostDocumentSaveParticipant', () => {
......@@ -25,7 +26,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
let documents: ExtHostDocuments;
let nullLogService = new NullLogService();
let nullExtensionDescription: IExtensionDescription = {
id: 'nullExtensionDescription',
identifier: new CanonicalExtensionIdentifier('nullExtensionDescription'),
name: 'Null Extension Description',
publisher: 'vscode',
enableProposedApi: false,
......
......@@ -14,11 +14,12 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e
import { NullLogService } from 'vs/platform/log/common/log';
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import { Counter } from 'vs/base/common/numbers';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
suite('ExtHostWorkspace', function () {
const extensionDescriptor: IExtensionDescription = {
id: 'nullExtensionDescription',
identifier: new CanonicalExtensionIdentifier('nullExtensionDescription'),
name: 'ext',
publisher: 'vscode',
enableProposedApi: false,
......
......@@ -78,6 +78,7 @@ import { IViewlet } from 'vs/workbench/common/viewlet';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
return instantiationService.createInstance(FileEditorInput, resource, void 0);
......@@ -307,7 +308,7 @@ export class TestDecorationsService implements IDecorationsService {
export class TestExtensionService implements IExtensionService {
_serviceBrand: any;
onDidRegisterExtensions: Event<void> = Event.None;
onDidChangeExtensionsStatus: Event<string[]> = Event.None;
onDidChangeExtensionsStatus: Event<CanonicalExtensionIdentifier[]> = Event.None;
onWillActivateByEvent: Event<IWillActivateEvent> = Event.None;
onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent> = Event.None;
activateByEvent(_activationEvent: string): Promise<void> { return Promise.resolve(void 0); }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册