diff --git a/src/vs/workbench/api/browser/mainThreadExtensionService.ts b/src/vs/workbench/api/browser/mainThreadExtensionService.ts index 41ab45643b9156bdfb261300e57b756c629ae0ca..35d36d1814f01376d01e08e8a21028a6d9607a54 100644 --- a/src/vs/workbench/api/browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/browser/mainThreadExtensionService.ts @@ -18,6 +18,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; @extHostNamedCustomer(MainContext.MainThreadExtensionService) export class MainThreadExtensionService implements MainThreadExtensionServiceShape { @@ -46,14 +47,14 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha public dispose(): void { } - $activateExtension(extensionId: ExtensionIdentifier, activationEvent: string): Promise { - return this._extensionService._activateById(extensionId, activationEvent); + $activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { + return this._extensionService._activateById(extensionId, reason); } $onWillActivateExtension(extensionId: ExtensionIdentifier): void { this._extensionService._onWillActivateExtension(extensionId); } - $onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { - this._extensionService._onDidActivateExtension(extensionId, startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent); + $onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void { + this._extensionService._onDidActivateExtension(extensionId, codeLoadingTime, activateCallTime, activateResolvedTime, activationReason); } $onExtensionRuntimeError(extensionId: ExtensionIdentifier, data: SerializedError): void { const error = new Error(); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index fda704b981019d1d42ce5d0f40c2bad266310671..3b70e8a445ea7c78dd46af8142e5dc2c467709e5 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -274,12 +274,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I getExtension(extensionId: string): Extension | undefined { const desc = extensionRegistry.getExtensionDescription(extensionId); if (desc) { - return new Extension(extensionService, desc, extensionKind); + return new Extension(extensionService, extension.identifier, desc, extensionKind); } return undefined; }, get all(): Extension[] { - return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc, extensionKind)); + return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, extension.identifier, desc, extensionKind)); }, get onDidChange() { return extensionRegistry.onDidChange; @@ -914,6 +914,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I class Extension implements vscode.Extension { private _extensionService: IExtHostExtensionService; + private _originExtensionId: ExtensionIdentifier; private _identifier: ExtensionIdentifier; readonly id: string; @@ -921,8 +922,9 @@ class Extension implements vscode.Extension { readonly packageJSON: IExtensionDescription; readonly extensionKind: vscode.ExtensionKind; - constructor(extensionService: IExtHostExtensionService, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) { + constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) { this._extensionService = extensionService; + this._originExtensionId = originExtensionId; this._identifier = description.identifier; this.id = description.identifier.value; this.extensionPath = path.normalize(originalFSPath(description.extensionLocation)); @@ -942,6 +944,6 @@ class Extension implements vscode.Extension { } activate(): Thenable { - return this._extensionService.activateByIdWithErrors(this._identifier, new ExtensionActivatedByAPI(false)).then(() => this.exports); + return this._extensionService.activateByIdWithErrors(this._identifier, new ExtensionActivatedByAPI(false, this._originExtensionId)).then(() => this.exports); } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d0ca5f56fd9fb2365029dec9ac742943a0e8365c..e90101a602c7131bcc254ab4a349fc22f551519c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -46,6 +46,7 @@ import { ExtensionActivationError } from 'vs/workbench/services/extensions/commo import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as search from 'vs/workbench/services/search/common/search'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -645,9 +646,9 @@ export interface MainThreadTaskShape extends IDisposable { } export interface MainThreadExtensionServiceShape extends IDisposable { - $activateExtension(extensionId: ExtensionIdentifier, activationEvent: string | null): Promise; + $activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; $onWillActivateExtension(extensionId: ExtensionIdentifier): void; - $onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string | null): void; + $onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void; $onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise; $onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void; $onExtensionHostExit(code: number): void; @@ -879,7 +880,7 @@ export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; $activateByEvent(activationEvent: string): Promise; - $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise; + $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; $setRemoteEnvironment(env: { [key: string]: string | null }): Promise; $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; diff --git a/src/vs/workbench/api/common/extHostExtensionActivator.ts b/src/vs/workbench/api/common/extHostExtensionActivator.ts index 5a3646ac4b05b4c723dab6c785bca96035183af0..5c035599a86a9cb8b125528bc76f06643548606e 100644 --- a/src/vs/workbench/api/common/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/common/extHostExtensionActivator.ts @@ -164,18 +164,22 @@ export interface IExtensionsActivatorHost { export class ExtensionActivatedByEvent { constructor( public readonly startup: boolean, + public readonly extensionId: ExtensionIdentifier, public readonly activationEvent: string ) { } } export class ExtensionActivatedByAPI { constructor( - public readonly startup: boolean + public readonly startup: boolean, + public readonly extensionId: ExtensionIdentifier ) { } } export type ExtensionActivationReason = ExtensionActivatedByEvent | ExtensionActivatedByAPI; +type ActivationIdAndReason = { id: ExtensionIdentifier, reason: ExtensionActivationReason }; + export class ExtensionsActivator { private readonly _registry: ExtensionDescriptionRegistry; @@ -217,12 +221,15 @@ export class ExtensionsActivator { return activatedExtension; } - public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): Promise { + public activateByEvent(activationEvent: string, startup: boolean): Promise { if (this._alreadyActivatedEvents[activationEvent]) { return NO_OP_VOID_PROMISE; } const activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent); - return this._activateExtensions(activateExtensions.map(e => e.identifier), reason).then(() => { + return this._activateExtensions(activateExtensions.map(e => ({ + id: e.identifier, + reason: new ExtensionActivatedByEvent(startup, e.identifier, activationEvent) + }))).then(() => { this._alreadyActivatedEvents[activationEvent] = true; }); } @@ -233,20 +240,23 @@ export class ExtensionsActivator { throw new Error('Extension `' + extensionId + '` is not known'); } - return this._activateExtensions([desc.identifier], reason); + return this._activateExtensions([{ + id: desc.identifier, + reason + }]); } /** * Handle semantics related to dependencies for `currentExtension`. * semantics: `redExtensions` must wait for `greenExtensions`. */ - private _handleActivateRequest(currentExtensionId: ExtensionIdentifier, greenExtensions: { [id: string]: ExtensionIdentifier; }, redExtensions: ExtensionIdentifier[]): void { - if (this._hostExtensionsMap.has(ExtensionIdentifier.toKey(currentExtensionId))) { - greenExtensions[ExtensionIdentifier.toKey(currentExtensionId)] = currentExtensionId; + private _handleActivateRequest(currentActivation: ActivationIdAndReason, greenExtensions: { [id: string]: ActivationIdAndReason; }, redExtensions: ActivationIdAndReason[]): void { + if (this._hostExtensionsMap.has(ExtensionIdentifier.toKey(currentActivation.id))) { + greenExtensions[ExtensionIdentifier.toKey(currentActivation.id)] = currentActivation; return; } - const currentExtension = this._registry.getExtensionDescription(currentExtensionId)!; + const currentExtension = this._registry.getExtensionDescription(currentActivation.id)!; const depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies); let currentExtensionGetsGreenLight = true; @@ -276,7 +286,10 @@ export class ExtensionsActivator { if (this._hostExtensionsMap.has(ExtensionIdentifier.toKey(depId))) { // must first wait for the dependency to activate currentExtensionGetsGreenLight = false; - greenExtensions[ExtensionIdentifier.toKey(depId)] = this._hostExtensionsMap.get(ExtensionIdentifier.toKey(depId))!; + greenExtensions[ExtensionIdentifier.toKey(depId)] = { + id: this._hostExtensionsMap.get(ExtensionIdentifier.toKey(depId))!, + reason: currentActivation.reason + }; continue; } @@ -284,7 +297,10 @@ export class ExtensionsActivator { if (depDesc) { // must first wait for the dependency to activate currentExtensionGetsGreenLight = false; - greenExtensions[ExtensionIdentifier.toKey(depId)] = depDesc.identifier; + greenExtensions[ExtensionIdentifier.toKey(depId)] = { + id: depDesc.identifier, + reason: currentActivation.reason + }; continue; } @@ -296,33 +312,33 @@ export class ExtensionsActivator { } if (currentExtensionGetsGreenLight) { - greenExtensions[ExtensionIdentifier.toKey(currentExtension.identifier)] = currentExtensionId; + greenExtensions[ExtensionIdentifier.toKey(currentExtension.identifier)] = currentActivation; } else { - redExtensions.push(currentExtensionId); + redExtensions.push(currentActivation); } } - private _activateExtensions(extensionIds: ExtensionIdentifier[], reason: ExtensionActivationReason): Promise { - // console.log('_activateExtensions: ', extensionIds.map(p => p.value)); - if (extensionIds.length === 0) { + private _activateExtensions(extensions: ActivationIdAndReason[]): Promise { + // console.log('_activateExtensions: ', extensions.map(p => p.id.value)); + if (extensions.length === 0) { return Promise.resolve(undefined); } - extensionIds = extensionIds.filter((p) => !this._activatedExtensions.has(ExtensionIdentifier.toKey(p))); - if (extensionIds.length === 0) { + extensions = extensions.filter((p) => !this._activatedExtensions.has(ExtensionIdentifier.toKey(p.id))); + if (extensions.length === 0) { return Promise.resolve(undefined); } - const greenMap: { [id: string]: ExtensionIdentifier; } = Object.create(null), - red: ExtensionIdentifier[] = []; + const greenMap: { [id: string]: ActivationIdAndReason; } = Object.create(null), + red: ActivationIdAndReason[] = []; - for (let i = 0, len = extensionIds.length; i < len; i++) { - this._handleActivateRequest(extensionIds[i], greenMap, red); + for (let i = 0, len = extensions.length; i < len; i++) { + this._handleActivateRequest(extensions[i], greenMap, red); } // Make sure no red is also green for (let i = 0, len = red.length; i < len; i++) { - const redExtensionKey = ExtensionIdentifier.toKey(red[i]); + const redExtensionKey = ExtensionIdentifier.toKey(red[i].id); if (greenMap[redExtensionKey]) { delete greenMap[redExtensionKey]; } @@ -330,16 +346,16 @@ export class ExtensionsActivator { const green = Object.keys(greenMap).map(id => greenMap[id]); - // console.log('greenExtensions: ', green.map(p => p.id)); - // console.log('redExtensions: ', red.map(p => p.id)); + // console.log('greenExtensions: ', green.map(p => p.id.value)); + // console.log('redExtensions: ', red.map(p => p.id.value)); if (red.length === 0) { // Finally reached only leafs! - return Promise.all(green.map((p) => this._activateExtension(p, reason))).then(_ => undefined); + return Promise.all(green.map((p) => this._activateExtension(p.id, p.reason))).then(_ => undefined); } - return this._activateExtensions(green, reason).then(_ => { - return this._activateExtensions(red, reason); + return this._activateExtensions(green).then(_ => { + return this._activateExtensions(red); }); } diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index dab30a8701655b418b38010842e20a612947ff3d..9c705b2d5f7eae6cbe1fb36897804b0bf1b3a698 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostExtensionServiceShape, IInitData, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; -import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; +import { ActivatedExtension, EmptyExtension, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; @@ -60,6 +60,7 @@ type TelemetryActivationEventFragment = { activationEvents: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; reason: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + reasonId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; }; export abstract class AbstractExtHostExtensionService implements ExtHostExtensionServiceShape { @@ -139,8 +140,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) { - const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); - await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent); + await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason); return new HostExtension(); } const extensionDescription = this._registry.getExtensionDescription(extensionId)!; @@ -195,8 +195,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } private _activateByEvent(activationEvent: string, startup: boolean): Promise { - const reason = new ExtensionActivatedByEvent(startup, activationEvent); - return this._activator.activateByEvent(activationEvent, reason); + return this._activator.activateByEvent(activationEvent, startup); } private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { @@ -285,8 +284,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier); return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => { const activationTimes = activatedExtension.activationTimes; - const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); - this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent); + this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, reason); this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes); return activatedExtension; }, (err) => { @@ -467,7 +465,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) { // the file was found return ( - this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`)) + this._activateById(extensionId, new ExtensionActivatedByEvent(true, extensionId, `workspaceContains:${fileName}`)) .then(undefined, err => console.error(err)) ); } @@ -488,7 +486,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio const timer = setTimeout(async () => { tokenSource.cancel(); - this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`)) + this._activateById(extensionId, new ExtensionActivatedByEvent(true, extensionId, `workspaceContainsTimeout:${globPatterns.join(',')}`)) .then(undefined, err => console.error(err)); }, AbstractExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT); @@ -507,7 +505,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio if (exists) { // a file was found matching one of the glob patterns return ( - this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`)) + this._activateById(extensionId, new ExtensionActivatedByEvent(true, extensionId, `workspaceContains:${globPatterns.join(',')}`)) .then(undefined, err => console.error(err)) ); } @@ -681,13 +679,13 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio ); } - public async $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise { + public async $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { await this._readyToRunExtensions.wait(); if (!this._registry.getExtensionDescription(extensionId)) { // unknown extension => ignore return false; } - await this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent)); + await this._activateById(extensionId, reason); return true; } @@ -743,12 +741,11 @@ type TelemetryActivationEvent = { activationEvents: string | null; isBuiltin: boolean; reason: string; + reasonId: string; }; function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent { - const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : - reason instanceof ExtensionActivatedByAPI ? 'api' : - ''; + const reasonStr = 'activationEvent' in reason ? reason.activationEvent : 'api'; const event = { id: extensionDescription.identifier.value, name: extensionDescription.name, @@ -756,7 +753,8 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription publisherDisplayName: extensionDescription.publisher, activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null, isBuiltin: extensionDescription.isBuiltin, - reason: reasonStr + reason: reasonStr, + reasonId: reason.extensionId.value, }; return event; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index f830027784b86f27e5ac4b86da21ceba3fd252b8..0ea3d9cb61991d9b05b3688ba3a6d9753f8cba7f 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -308,7 +308,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { const activationTimes = element.status.activationTimes!; let syncTime = activationTimes.codeLoadingTime + activationTimes.activateCallTime; - data.activationTime.textContent = activationTimes.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`; + data.activationTime.textContent = activationTimes.activationReason.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`; data.actionbar.clear(); if (element.unresponsiveProfile) { @@ -319,43 +319,45 @@ export class RuntimeExtensionsEditor extends BaseEditor { } let title: string; - if (activationTimes.activationEvent === '*') { - title = nls.localize('starActivation', "Activated on start-up"); - } else if (/^workspaceContains:/.test(activationTimes.activationEvent)) { - let fileNameOrGlob = activationTimes.activationEvent.substr('workspaceContains:'.length); + const activationId = activationTimes.activationReason.extensionId.value; + const activationEvent = 'activationEvent' in activationTimes.activationReason ? activationTimes.activationReason.activationEvent : 'api'; + if (activationEvent === '*') { + title = nls.localize('starActivation', "Activated by {0} on start-up", activationId); + } else if (/^workspaceContains:/.test(activationEvent)) { + let fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) { title = nls.localize({ key: 'workspaceContainsGlobActivation', comment: [ '{0} will be a glob pattern' ] - }, "Activated because a file matching {0} exists in your workspace", fileNameOrGlob); + }, "Activated by {1} because a file matching {1} exists in your workspace", fileNameOrGlob, activationId); } else { title = nls.localize({ key: 'workspaceContainsFileActivation', comment: [ '{0} will be a file name' ] - }, "Activated because file {0} exists in your workspace", fileNameOrGlob); + }, "Activated by {1} because file {0} exists in your workspace", fileNameOrGlob, activationId); } - } else if (/^workspaceContainsTimeout:/.test(activationTimes.activationEvent)) { - const glob = activationTimes.activationEvent.substr('workspaceContainsTimeout:'.length); + } else if (/^workspaceContainsTimeout:/.test(activationEvent)) { + const glob = activationEvent.substr('workspaceContainsTimeout:'.length); title = nls.localize({ key: 'workspaceContainsTimeout', comment: [ '{0} will be a glob pattern' ] - }, "Activated because searching for {0} took too long", glob); - } else if (/^onLanguage:/.test(activationTimes.activationEvent)) { - let language = activationTimes.activationEvent.substr('onLanguage:'.length); - title = nls.localize('languageActivation', "Activated because you opened a {0} file", language); + }, "Activated by {1} because searching for {0} took too long", glob, activationId); + } else if (/^onLanguage:/.test(activationEvent)) { + let language = activationEvent.substr('onLanguage:'.length); + title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId); } else { title = nls.localize({ key: 'workspaceGenericActivation', comment: [ 'The {0} placeholder will be an activation event, like e.g. \'language:typescript\', \'debug\', etc.' ] - }, "Activated on {0}", activationTimes.activationEvent); + }, "Activated by {1} on {0}", activationEvent, activationId); } data.activationTime.title = title; diff --git a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts index bf30364efb72db0c9896c500386bfdb76fa28ce5..32b850b937e413b4434b33012c71ec260e89267c 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts @@ -177,10 +177,11 @@ class PerfModelContentProvider implements ITextModelContentProvider { if (!times) { continue; } - if (times.startup) { - eager.push([id, times.startup, times.codeLoadingTime, times.activateCallTime, times.activateResolvedTime, times.activationEvent]); + const event = 'activationEvent' in times.activationReason ? times.activationReason.activationEvent : 'api'; + if (times.activationReason.startup) { + eager.push([id, times.activationReason.startup, times.codeLoadingTime, times.activateCallTime, times.activateResolvedTime, event, times.activationReason.extensionId.value]); } else { - normal.push([id, times.startup, times.codeLoadingTime, times.activateCallTime, times.activateResolvedTime, times.activationEvent]); + normal.push([id, times.activationReason.startup, times.codeLoadingTime, times.activateCallTime, times.activateResolvedTime, event, times.activationReason.extensionId.value]); } } @@ -188,7 +189,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { if (table.length > 0) { md.heading(2, 'Extension Activation Stats'); md.table( - ['Extension', 'Eager', 'Load Code', 'Call Activate', 'Finish Activate', 'Event'], + ['Extension', 'Eager', 'Load Code', 'Call Activate', 'Finish Activate', 'Event', 'By'], table ); } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index f83cfa89774fe940843258fd00915342b4405dbd..2f31043b5ff44094a24aed49ba5460d3957efc27 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -24,6 +24,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -406,9 +407,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } } - public async _activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise { + public async _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { const results = await Promise.all( - this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, activationEvent)) + this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, reason)) ); const activated = results.some(e => e); if (!activated) { @@ -420,8 +421,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId); } - public _onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { - this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent)); + public _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void { + this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(codeLoadingTime, activateCallTime, activateResolvedTime, activationReason)); this._onDidChangeExtensionsStatus.fire([extensionId]); } diff --git a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts index 44f76d95fd03e82bf316317cb53cfc66c7de5b68..3aff4a5e57f95c2f6096571386a4791c821ce6b3 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts @@ -26,6 +26,7 @@ import { IUntitledResourceInput } from 'vs/workbench/common/editor'; import { StopWatch } from 'vs/base/common/stopwatch'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; // Enable to see detailed message communication between window and extension host const LOG_EXTENSION_HOST_COMMUNICATION = false; @@ -213,12 +214,12 @@ export class ExtensionHostProcessManager extends Disposable { return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService); } - public async activate(extension: ExtensionIdentifier, activationEvent: string): Promise { + public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { const proxy = await this._getExtensionHostProcessProxy(); if (!proxy) { return false; } - return proxy.$activate(extension, activationEvent); + return proxy.$activate(extension, reason); } public activateByEvent(activationEvent: string): Promise { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index b127082c0c3dddf29169f3c392c211ab7353e83a..d68777ae83b30b0916ce7aba55211fba4d3e7e4e 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -11,6 +11,7 @@ import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensi import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; export const nullExtensionDescription = Object.freeze({ identifier: new ExtensionIdentifier('nullExtensionDescription'), @@ -99,11 +100,10 @@ export type ProfileSegmentId = string | 'idle' | 'program' | 'gc' | 'self'; export class ActivationTimes { constructor( - public readonly startup: boolean, public readonly codeLoadingTime: number, public readonly activateCallTime: number, public readonly activateResolvedTime: number, - public readonly activationEvent: string + public readonly activationReason: ExtensionActivationReason ) { } } @@ -226,9 +226,9 @@ export interface IExtensionService { setRemoteEnvironment(env: { [key: string]: string | null }): Promise; _logOrShowMessage(severity: Severity, msg: string): void; - _activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise; + _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; _onWillActivateExtension(extensionId: ExtensionIdentifier): void; - _onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void; + _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void; _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void; _onExtensionHostExit(code: number): void; } @@ -276,9 +276,9 @@ export class NullExtensionService implements IExtensionService { canAddExtension(): boolean { return false; } canRemoveExtension(): boolean { return false; } _logOrShowMessage(_severity: Severity, _msg: string): void { } - _activateById(_extensionId: ExtensionIdentifier, _activationEvent: string): Promise { return Promise.resolve(); } + _activateById(_extensionId: ExtensionIdentifier, _reason: ExtensionActivationReason): Promise { return Promise.resolve(); } _onWillActivateExtension(_extensionId: ExtensionIdentifier): void { } - _onDidActivateExtension(_extensionId: ExtensionIdentifier, _startup: boolean, _codeLoadingTime: number, _activateCallTime: number, _activateResolvedTime: number, _activationEvent: string): void { } + _onDidActivateExtension(_extensionId: ExtensionIdentifier, _codeLoadingTime: number, _activateCallTime: number, _activateResolvedTime: number, _activationReason: ExtensionActivationReason): void { } _onExtensionRuntimeError(_extensionId: ExtensionIdentifier, _err: Error): void { } _onExtensionHostExit(code: number): void { } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 8c65cc8e9498f27f7d085833186fbe5c53e7a6d1..a3eda5b258712b1d014f7e518d2e3d800f939be5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -37,6 +37,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; import { flatten } from 'vs/base/common/arrays'; import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; +import { ExtensionActivatedByEvent } from 'vs/workbench/api/common/extHostExtensionActivator'; class DeltaExtensionsQueueItem { constructor( @@ -322,7 +323,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten if (shouldActivate) { await Promise.all( - this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, shouldActivateReason!)) + this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, new ExtensionActivatedByEvent(false, extensionDescription.identifier, shouldActivateReason!))) ).then(() => { }); } }