diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index ccda0051a59c9..4cea7236d0d32 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -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]: {} }; } diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 25998a367b27a..101110e94030f 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -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; + } +} diff --git a/src/vs/platform/statusbar/common/statusbar.ts b/src/vs/platform/statusbar/common/statusbar.ts index a14dc1638066e..f2d4097c2b245 100644 --- a/src/vs/platform/statusbar/common/statusbar.ts +++ b/src/vs/platform/statusbar/common/statusbar.ts @@ -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('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. diff --git a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts index 299da54da2229..7de2b9f8ff9ba 100644 --- a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts @@ -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(ViewContainerExtensions.ViewContainersRegistry); const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); const id = descriptor.id; diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index a8738c55bc6a5..a45ff4401bf61 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -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); diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index 7a6cb1ecf4aa4..c0c0d3d4ee8db 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -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 }); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts b/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts index 3384488f2b4c4..04602c0ea6168 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts @@ -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); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts index b0ef3ef24c6d2..90540a4f020b7 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts @@ -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({ diff --git a/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts b/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts index 1dbe33201ce9a..3a12802a8836d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts @@ -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); diff --git a/src/vs/workbench/api/electron-browser/mainThreadUrls.ts b/src/vs/workbench/api/electron-browser/mainThreadUrls.ts index 5d550492b294f..268228eeed8d6 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadUrls.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadUrls.ts @@ -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 { - 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(); + private handlers = new Map(); constructor( context: IExtHostContext, @@ -41,7 +42,7 @@ export class MainThreadUrls implements MainThreadUrlsShape { this.proxy = context.getProxy(ExtHostContext.ExtHostUrls); } - $registerUriHandler(handle: number, extensionId: string): Promise { + $registerUriHandler(handle: number, extensionId: CanonicalExtensionIdentifier): Promise { const handler = new ExtensionUrlHandler(this.proxy, handle, extensionId); const disposable = this.urlService.registerHandler(handler); diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 054e832c0d1dc..8876f29adc928 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -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 { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index e22d865739ce1..3170bcc79b1e7 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -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 } }); } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 286b289ab1a9f..7ada96d59af3d 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -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, position, priority); + return extHostStatusBar.createStatusBarEntry(extension.identifier, position, priority); }, setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): 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(): vscode.QuickPick { - 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 implements vscode.Extension { private _extensionService: ExtHostExtensionService; + private _identifier: CanonicalExtensionIdentifier; public id: string; public extensionPath: string; @@ -844,21 +846,22 @@ class Extension implements vscode.Extension { 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 this._extensionService.getExtensionExports(this.id); + return this._extensionService.getExtensionExports(this._identifier); } activate(): Thenable { - 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); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 621dba4c7c3f4..2130c7de93b42 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -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; + $registerUriHandler(handle: number, extensionId: CanonicalExtensionIdentifier): Promise; $unregisterUriHandler(handle: number): Promise; } @@ -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; - $startExtensionHost(enabledExtensionIds: string[]): Promise; + $startExtensionHost(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise; $activateByEvent(activationEvent: string): Promise; } diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index 761547ff64fc7..1a50918b72a35 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -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++; diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index 724bf5863ab81..d6c7253da81b2 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -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.`); diff --git a/src/vs/workbench/api/node/extHostDecorations.ts b/src/vs/workbench/api/node/extHostDecorations.ts index 1afb2f4328279..fea3abefb1efc 100644 --- a/src/vs/workbench/api/node/extHostDecorations.ts +++ b/src/vs/workbench/api/node/extHostDecorations.ts @@ -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 && [data.priority, data.bubble, data.title, data.letter, data.color, data.source]; }, err => { diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index ea3ef4d05181b..226248d1699bf 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -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) || (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 => { diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index 1bc33c64cbf1c..c4621889421e5 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -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 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; }; - private readonly _activatedExtensions: { [extensionId: string]: ActivatedExtension; }; + private readonly _activatingExtensions: Map>; + private readonly _activatedExtensions: Map; /** * 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>(); + this._activatedExtensions = new Map(); 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 { @@ -228,7 +232,7 @@ export class ExtensionsActivator { }); } - public activateById(extensionId: string, reason: ExtensionActivationReason): Promise { + public activateById(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise { 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`); (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 { - 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); } } diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 5157534de1d95..8fb2668fae7fd 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -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 { @@ -234,7 +235,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { let allPromises: Promise[] = []; 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 { + private _activateById(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise { return this._activator.activateById(extensionId, reason); } - public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): Promise { + public activateByIdWithErrors(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise { 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 { + private _deactivate(extensionId: CanonicalExtensionIdentifier): Promise { 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 { - 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([ loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder), this._loadExtensionContext(extensionDescription) ]).then(values => { - return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, values[0], values[1], activationTimesBuilder); + return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); }); } private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { - 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 { + private static _callActivate(logService: ILogService, extensionId: CanonicalExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { // 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 { + private static _callActivateOptional(logService: ILogService, extensionId: CanonicalExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { if (typeof extensionModule.activate === 'function') { try { activationTimesBuilder.activateCallStart(); - logService.trace(`ExtensionService#_callActivateOptional ${extensionId}`); + logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); const activateResult: Promise = 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 { + private async _activateIfFileName(workspace: IWorkspaceData, extensionId: CanonicalExtensionIdentifier, fileName: string): Promise { // 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 { - this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId}, entryPoint: workspaceContains`); + private async _activateIfGlobPatterns(extensionId: CanonicalExtensionIdentifier, globPatterns: string[]): Promise { + 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 { + public $startExtensionHost(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise { 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, diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 5aed24a9c16cf..9c0aa3f310b32 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -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 { @@ -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); } diff --git a/src/vs/workbench/api/node/extHostLogService.ts b/src/vs/workbench/api/node/extHostLogService.ts index 5db0676d38696..f7381f5000af7 100644 --- a/src/vs/workbench/api/node/extHostLogService.ts +++ b/src/vs/workbench/api/node/extHostLogService.ts @@ -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); } } diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index cd4adf9759ede..bc64c05120d78 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -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(extensionId: string, enableProposedApi: boolean): QuickPick { + createQuickPick(extensionId: CanonicalExtensionIdentifier, enableProposedApi: boolean): QuickPick { 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 extends ExtHostQuickInput implem private _selectedItems: T[] = []; private _onDidChangeSelectionEmitter = new Emitter(); - 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' }); } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index 54013036c118b..e6bb1e7fe3338 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -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; diff --git a/src/vs/workbench/api/node/extHostStatusBar.ts b/src/vs/workbench/api/node/extHostStatusBar.ts index 7531afcb45fc9..5e3522c145198 100644 --- a/src/vs/workbench/api/node/extHostStatusBar.ts +++ b/src/vs/workbench/api/node/extHostStatusBar.ts @@ -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); } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 84224aeabe7a4..e5400b7e70c24 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -205,7 +205,7 @@ namespace TaskDTO { definition, name: value.name, source: { - extensionId: extension.id, + extensionId: extension.identifier.value, label: value.source, scope: scope }, diff --git a/src/vs/workbench/api/node/extHostUrls.ts b/src/vs/workbench/api/node/extHostUrls.ts index e458333f5173b..59d417f0e1c94 100644 --- a/src/vs/workbench/api/node/extHostUrls.ts +++ b/src/vs/workbench/api/node/extHostUrls.ts @@ -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); }); diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 6fa89896fa0ff..06544f1884639 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -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); diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 15cb4162e7772..dbd480222281d 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -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 { - 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 { + 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 { - 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 { + this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId.value}, entryPoint: findTextInFiles`); const requestId = this._requestIdProvider.getNext(); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 0ab9c6d9f783c..379098ff9f04c 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -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() { diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 54999cd59c551..af73f59ce6be2 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -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); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 886396ea8e664..562c430b66d68 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -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('launch', { resource: root.uri }) }); diff --git a/src/vs/workbench/parts/debug/test/node/debugger.test.ts b/src/vs/workbench/parts/debug/test/node/debugger.test.ts index 5dc901b29c0f6..d4f9160f8dde4 100644 --- a/src/vs/workbench/parts/debug/test/node/debugger.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugger.test.ts @@ -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', diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionProfileService.ts index 134e2bfe27bd1..f1c87433c215c 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionProfileService.ts @@ -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); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 5a9c1b9a72550..2adce4e694bdc 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -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 }, this.extension.identifier)); + this.enabled = !this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier)); } } } @@ -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 }, this.extension.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { + if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier) && 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 }, this.extension.identifier))) { + if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))) { 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.identifier))[0]; + const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[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 => areSameExtensions(e, this.extension.identifier))) { + if (this.extension && this.extension.local && !this.extension.isMalicious && !this.runningExtensions.some(e => CanonicalExtensionIdentifier.equals(e.identifier, this.extension.identifier.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); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts index 621bf021164b3..3fc50a54fd15c 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts @@ -20,7 +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 { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution { @@ -133,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`); @@ -163,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 => areSameExtensions(value.identifier, extension))[0], + marketplaceInfo: this._anotherExtensionService.local.filter(value => CanonicalExtensionIdentifier.equals(value.identifier.id, extension.identifier))[0], description: extension, unresponsiveProfile: profile, status: undefined, @@ -173,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( diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index 0557e2da529e6..2d566e09e56cd 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -152,7 +152,7 @@ export class Renderer implements IPagedRenderer { if (installed && installed.local) { const installedExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); const isSameExtensionRunning = runningExtensions.some(e => { - if (!areSameExtensions(e, extension.identifier)) { + if (!areSameExtensions({ id: e.identifier.value }, extension.identifier)) { return false; } const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index fd523f0a9fb2a..e138c76269ee4 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -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.identifier)) + .filter(e => runningExtensions.every(r => !areSameExtensions({ id: r.identifier.value }, e.identifier)) && (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.identifier)) + .filter(e => runningExtensions.some(r => areSameExtensions({ id: r.identifier.value }, e.identifier)) && (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)))); diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index 75ed14dcaf1eb..42d8379eb06b7 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -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('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('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.identifier.id] = extension; + marketplaceMap[CanonicalExtensionIdentifier.toKey(extension.identifier.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: extension.unresponsiveProfile.data }, 'pii_removed'); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 71efe2a83c6d2..b0dd6e41b18eb 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -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: { localizations: [{ languageId: 'de', translations: [] }] } }); const workbenchService = instantiationService.get(IExtensionsWorkbenchService); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts index a0da064c7a3e2..7984895518586 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts @@ -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) } ]); } }); diff --git a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts index 09f5e13d15918..daec033c515f8 100644 --- a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts @@ -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; } diff --git a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts index e44874aa4feb4..c3acf23eeedca 100644 --- a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts @@ -97,7 +97,7 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint { 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 { @@ -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); } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index ab067f5c2837c..e96de02654969 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -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({ - id: 'nullExtensionDescription', + identifier: new CanonicalExtensionIdentifier('nullExtensionDescription'), name: 'Null Extension Description', version: '0.0.0', publisher: 'vscode', @@ -49,7 +50,7 @@ export const IExtensionService = createDecorator('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; + onDidChangeExtensionsStatus: Event; /** * 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}`); } diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index d0d8adac63948..e9187014ccba9 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -35,7 +35,7 @@ export class ExtensionMessageCollector { this._messageHandler({ type: type, message: message, - extensionId: this._extension.id, + extensionId: this._extension.identifier, extensionPointId: this._extensionPointId }); } diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index ec7179e5bcac6..8a749895af59b 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -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(); 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); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index 55354a436747a..44cd272a47ed5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -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 { + public start(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise { return this._extensionHostProcessProxy.then(proxy => proxy.value.$startExtensionHost(enabledExtensionIds)); } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index aa86775141bfd..3cc306bd9d8b1 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -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); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 7b0caef6c98c9..1f168541e8a3a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -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 0); @@ -36,6 +37,18 @@ const DYNAMIC_EXTENSION_POINTS = false; schema.properties.engines.properties.vscode.default = `^${pkg.version}`; +let productAllowProposedApi: Set = null; +function allowProposedApiFromProduct(id: CanonicalExtensionIdentifier): boolean { + // create set if needed + if (productAllowProposedApi === null) { + productAllowProposedApi = new Set(); + 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; private _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; private readonly _extensionScanner: CachedExtensionScanner; private readonly _onDidRegisterExtensions: Emitter = this._register(new Emitter()); public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event; - private readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); - public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; + private readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); + public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; private readonly _onWillActivateByEvent = this._register(new Emitter()); public readonly onWillActivateByEvent: Event = 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; + private _extensionHostProcessActivationTimes: Map; + private _extensionHostExtensionRuntimeErrors: Map; 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(); 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(); + this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostExtensionRuntimeErrors = new Map(); 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(); + this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostExtensionRuntimeErrors = new Map(); 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 { 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, diff --git a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts index 81ea673d7e2b8..0d1958f91c270 100644 --- a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts @@ -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('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 { diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index c470b6198902b..1734019932dc1 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -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; private _extensionsArr: IExtensionDescription[]; - private _activationMap: { [activationEvent: string]: IExtensionDescription[]; }; + private _activationMap: Map; constructor(extensionDescriptions: IExtensionDescription[]) { this._extensionDescriptions = extensionDescriptions; @@ -19,20 +18,20 @@ export class ExtensionDescriptionRegistry { } private _initialize(): void { - this._extensionsMap = {}; + this._extensionsMap = new Map(); this._extensionsArr = []; - this._activationMap = {}; + this._activationMap = new Map(); 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(); - 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)); } } diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 50b38ceb6f2b6..62e8d76219bdd 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -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]); diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts index f40462cbae856..d9a95109accff 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts @@ -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 diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts index 333b229de080b..238a2644b24e7 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts @@ -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 diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index f9d84b6ed0953..695bb6098f72f 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -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, diff --git a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts index 3e01dbf407827..17af41aaced9e 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -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, diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index ec52bd52f9e58..5c5404d6e1507 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -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 = Event.None; - onDidChangeExtensionsStatus: Event = Event.None; + onDidChangeExtensionsStatus: Event = Event.None; onWillActivateByEvent: Event = Event.None; onDidChangeResponsiveChange: Event = Event.None; activateByEvent(_activationEvent: string): Promise { return Promise.resolve(void 0); }