@codingame/monaco-vscode-extensions-service-override
Version:
VSCode public API plugged on the monaco editor - extensions service-override
997 lines (995 loc) • 66.8 kB
JavaScript
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
import { Barrier } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
import { toErrorMessage } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errorMessage';
import { Emitter } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
import { MarkdownString } from '@codingame/monaco-vscode-api/vscode/vs/base/common/htmlContent';
import { Disposable, DisposableStore } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
import { Schemas } from '@codingame/monaco-vscode-api/vscode/vs/base/common/network';
import { mark } from '@codingame/monaco-vscode-api/vscode/vs/base/common/performance';
import { isCI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/platform';
import { isEqualOrParent } from '@codingame/monaco-vscode-api/vscode/vs/base/common/resources';
import { StopWatch } from '@codingame/monaco-vscode-api/vscode/vs/base/common/stopwatch';
import { isDefined } from '@codingame/monaco-vscode-api/vscode/vs/base/common/types';
import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
import { IConfigurationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service';
import { IDialogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/dialogs/common/dialogs.service';
import { InstallOperation } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensionManagement/common/extensionManagement';
import { ImplicitActivationEvents } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensionManagement/common/implicitActivationEvents';
import { ExtensionIdentifierMap, ExtensionIdentifier } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensions/common/extensions';
import { IFileService } from '@codingame/monaco-vscode-api/vscode/vs/platform/files/common/files.service';
import { SyncDescriptor } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/descriptors';
import { IInstantiationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation';
import { handleVetos } from '../../../../platform/lifecycle/common/lifecycle.js';
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
import '@codingame/monaco-vscode-api/vscode/vs/platform/notification/common/notification';
import { INotificationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/notification/common/notification.service';
import { IProductService } from '@codingame/monaco-vscode-api/vscode/vs/platform/product/common/productService.service';
import { Registry } from '@codingame/monaco-vscode-api/vscode/vs/platform/registry/common/platform';
import { RemoteAuthorityResolverError, getRemoteAuthorityPrefix, RemoteAuthorityResolverErrorCode } from '@codingame/monaco-vscode-api/vscode/vs/platform/remote/common/remoteAuthorityResolver';
import { IRemoteAuthorityResolverService } from '@codingame/monaco-vscode-api/vscode/vs/platform/remote/common/remoteAuthorityResolver.service';
import { IRemoteExtensionsScannerService } from '@codingame/monaco-vscode-api/vscode/vs/platform/remote/common/remoteExtensionsScanner.service';
import { ITelemetryService } from '@codingame/monaco-vscode-api/vscode/vs/platform/telemetry/common/telemetry.service';
import { IWorkspaceContextService } from '@codingame/monaco-vscode-api/vscode/vs/platform/workspace/common/workspace.service';
import { IWorkbenchEnvironmentService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/environment/common/environmentService.service';
import { Extensions } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensionManagement/common/extensionFeatures';
import { IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensionManagement/common/extensionManagement.service';
import { LockableExtensionDescriptionRegistry } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { parseExtensionDevOptions } from './extensionDevOptions.js';
import { ExtensionRunningPreference, ExtensionHostKind } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionHostKind';
import { ExtensionHostManager } from './extensionHostManager.js';
import { IExtensionManifestPropertiesService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionManifestPropertiesService.service';
import { RemoteRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionRunningLocation';
import { ExtensionRunningLocationTracker, filterExtensionIdentifiers } from './extensionRunningLocationTracker.js';
import { toExtensionDescription, ActivationKind, ExtensionHostStartup, ExtensionPointContribution, ActivationTimes, toExtension } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions';
import { ExtensionsRegistry, ExtensionMessageCollector } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionsRegistry';
import { LazyCreateExtensionHostManager } from './lazyCreateExtensionHostManager.js';
import { ResponsiveState } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/rpcProtocol';
import { checkGlobFileExists, checkActivateWorkspaceContainsExtension } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/workspaceContains';
import { WillShutdownJoinerOrder } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/lifecycle/common/lifecycle';
import { ILifecycleService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/lifecycle/common/lifecycle.service';
import { IRemoteAgentService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/remote/common/remoteAgentService.service';
import Severity from '@codingame/monaco-vscode-api/vscode/vs/base/common/severity';
var AbstractExtensionService_1;
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = Promise.resolve(undefined);
let AbstractExtensionService = AbstractExtensionService_1 = class AbstractExtensionService extends Disposable {
constructor(
options,
_extensionsProposedApi,
_extensionHostFactory,
_extensionHostKindPicker,
_instantiationService,
_notificationService,
_environmentService,
_telemetryService,
_extensionEnablementService,
_fileService,
_productService,
_extensionManagementService,
_contextService,
_configurationService,
_extensionManifestPropertiesService,
_logService,
_remoteAgentService,
_remoteExtensionsScannerService,
_lifecycleService,
_remoteAuthorityResolverService,
_dialogService
) {
super();
this._extensionsProposedApi = _extensionsProposedApi;
this._extensionHostFactory = _extensionHostFactory;
this._extensionHostKindPicker = _extensionHostKindPicker;
this._instantiationService = _instantiationService;
this._notificationService = _notificationService;
this._environmentService = _environmentService;
this._telemetryService = _telemetryService;
this._extensionEnablementService = _extensionEnablementService;
this._fileService = _fileService;
this._productService = _productService;
this._extensionManagementService = _extensionManagementService;
this._contextService = _contextService;
this._configurationService = _configurationService;
this._extensionManifestPropertiesService = _extensionManifestPropertiesService;
this._logService = _logService;
this._remoteAgentService = _remoteAgentService;
this._remoteExtensionsScannerService = _remoteExtensionsScannerService;
this._lifecycleService = _lifecycleService;
this._remoteAuthorityResolverService = _remoteAuthorityResolverService;
this._dialogService = _dialogService;
this._onDidRegisterExtensions = this._register(( new Emitter()));
this.onDidRegisterExtensions = this._onDidRegisterExtensions.event;
this._onDidChangeExtensionsStatus = this._register(( new Emitter()));
this.onDidChangeExtensionsStatus = this._onDidChangeExtensionsStatus.event;
this._onDidChangeExtensions = this._register(( new Emitter({
leakWarningThreshold: 400,
leakWarningName: "ExtensionService._onDidChangeExtensions"
})));
this.onDidChangeExtensions = this._onDidChangeExtensions.event;
this._onWillActivateByEvent = this._register(( new Emitter()));
this.onWillActivateByEvent = this._onWillActivateByEvent.event;
this._onDidChangeResponsiveChange = this._register(( new Emitter()));
this.onDidChangeResponsiveChange = this._onDidChangeResponsiveChange.event;
this._onWillStop = this._register(( new Emitter()));
this.onWillStop = this._onWillStop.event;
this._activationEventReader = ( new ImplicitActivationAwareReader());
this._registry = ( new LockableExtensionDescriptionRegistry(this._activationEventReader));
this._installedExtensionsReady = ( new Barrier());
this._extensionStatus = ( new ExtensionIdentifierMap());
this._allRequestedActivateEvents = ( new Set());
this._pendingRemoteActivationEvents = ( new Set());
this._remoteCrashTracker = ( new ExtensionHostCrashTracker());
this._deltaExtensionsQueue = [];
this._inHandleDeltaExtensions = false;
this._extensionHostManagers = this._register(( new ExtensionHostCollection()));
this._resolveAuthorityAttempt = 0;
this._initializePromise = null;
this._hasLocalProcess = options.hasLocalProcess;
this._allowRemoteExtensionsInLocalWebWorker = options.allowRemoteExtensionsInLocalWebWorker;
this._register(this._fileService.onWillActivateFileSystemProvider(e => {
if (e.scheme !== Schemas.vscodeRemote) {
e.join(this.activateByEvent(`onFileSystem:${e.scheme}`));
}
}));
this._runningLocations = ( new ExtensionRunningLocationTracker(
this._registry,
this._extensionHostKindPicker,
this._environmentService,
this._configurationService,
this._logService,
this._extensionManifestPropertiesService
));
this._register(this._extensionEnablementService.onEnablementChanged(extensions => {
const toAdd = [];
const toRemove = [];
for (const extension of extensions) {
if (this._safeInvokeIsEnabled(extension)) {
toAdd.push(extension);
} else {
toRemove.push(extension);
}
}
if (isCI) {
this._logService.info(`AbstractExtensionService.onEnablementChanged fired for ${( extensions.map(e => e.identifier.id)).join(", ")}`);
}
this._handleDeltaExtensions(( new DeltaExtensionsQueueItem(toAdd, toRemove)));
}));
this._register(this._extensionManagementService.onDidChangeProfile((
{
added,
removed
}
) => {
if (added.length || removed.length) {
if (isCI) {
this._logService.info(`AbstractExtensionService.onDidChangeProfile fired`);
}
this._handleDeltaExtensions(( new DeltaExtensionsQueueItem(added, removed)));
}
}));
this._register(this._extensionManagementService.onDidEnableExtensions(extensions => {
if (extensions.length) {
if (isCI) {
this._logService.info(`AbstractExtensionService.onDidEnableExtensions fired`);
}
this._handleDeltaExtensions(( new DeltaExtensionsQueueItem(extensions, [])));
}
}));
this._register(this._extensionManagementService.onDidInstallExtensions(result => {
const extensions = [];
const toRemove = [];
for (const {
local,
operation
} of result) {
if (local && local.isValid && operation !== InstallOperation.Migrate && this._safeInvokeIsEnabled(local)) {
extensions.push(local);
if (operation === InstallOperation.Update) {
toRemove.push(local.identifier.id);
}
}
}
if (extensions.length) {
if (isCI) {
this._logService.info(
`AbstractExtensionService.onDidInstallExtensions fired for ${( extensions.map(e => e.identifier.id)).join(", ")}`
);
}
this._handleDeltaExtensions(( new DeltaExtensionsQueueItem(extensions, toRemove)));
}
}));
this._register(this._extensionManagementService.onDidUninstallExtension(event => {
if (!event.error) {
if (isCI) {
this._logService.info(
`AbstractExtensionService.onDidUninstallExtension fired for ${event.identifier.id}`
);
}
this._handleDeltaExtensions(( new DeltaExtensionsQueueItem([], [event.identifier.id])));
}
}));
this._register(this._lifecycleService.onWillShutdown(event => {
if (this._remoteAgentService.getConnection()) {
event.join(async () => {
try {
await this._remoteAgentService.endConnection();
await this._doStopExtensionHosts();
this._remoteAgentService.getConnection()?.dispose();
} catch {
this._logService.warn("Error while disconnecting remote agent");
}
}, {
id: "join.disconnectRemote",
label: ( localize(16632, "Disconnect Remote Agent")),
order: WillShutdownJoinerOrder.Last
});
} else {
event.join(this._doStopExtensionHosts(), {
id: "join.stopExtensionHosts",
label: ( localize(16633, "Stopping Extension Hosts"))
});
}
}));
}
_getExtensionHostManagers(kind) {
return this._extensionHostManagers.getByKind(kind);
}
async _handleDeltaExtensions(item) {
this._deltaExtensionsQueue.push(item);
if (this._inHandleDeltaExtensions) {
return;
}
let lock = null;
try {
this._inHandleDeltaExtensions = true;
await this._installedExtensionsReady.wait();
lock = await this._registry.acquireLock("handleDeltaExtensions");
while (this._deltaExtensionsQueue.length > 0) {
const item = this._deltaExtensionsQueue.shift();
await this._deltaExtensions(lock, item.toAdd, item.toRemove);
}
} finally {
this._inHandleDeltaExtensions = false;
lock?.dispose();
}
}
async _deltaExtensions(lock, _toAdd, _toRemove) {
if (isCI) {
this._logService.info(`AbstractExtensionService._deltaExtensions: toAdd: [${( _toAdd.map(e => e.identifier.id)).join(",")}] toRemove: [${( _toRemove.map(e => typeof e === "string" ? e : e.identifier.id)).join(",")}]`);
}
let toRemove = [];
for (let i = 0, len = _toRemove.length; i < len; i++) {
const extensionOrId = _toRemove[i];
const extensionId = (typeof extensionOrId === "string" ? extensionOrId : extensionOrId.identifier.id);
const extension = (typeof extensionOrId === "string" ? null : extensionOrId);
const extensionDescription = this._registry.getExtensionDescription(extensionId);
if (!extensionDescription) {
continue;
}
if (extension && extensionDescription.extensionLocation.scheme !== extension.location.scheme) {
continue;
}
if (!this.canRemoveExtension(extensionDescription)) {
continue;
}
toRemove.push(extensionDescription);
}
const toAdd = [];
for (let i = 0, len = _toAdd.length; i < len; i++) {
const extension = _toAdd[i];
const extensionDescription = toExtensionDescription(extension, false);
if (!extensionDescription) {
continue;
}
if (!this._canAddExtension(extensionDescription, toRemove)) {
continue;
}
toAdd.push(extensionDescription);
}
if (toAdd.length === 0 && toRemove.length === 0) {
return;
}
const result = this._registry.deltaExtensions(lock, toAdd, ( toRemove.map(e => e.identifier)));
this._onDidChangeExtensions.fire({
added: toAdd,
removed: toRemove
});
toRemove = toRemove.concat(result.removedDueToLooping);
if (result.removedDueToLooping.length > 0) {
this._notificationService.notify({
severity: Severity.Error,
message: ( localize(
16634,
"The following extensions contain dependency loops and have been disabled: {0}",
( result.removedDueToLooping.map(e => `'${e.identifier.value}'`)).join(", ")
))
});
}
this._extensionsProposedApi.updateEnabledApiProposals(toAdd);
this._doHandleExtensionPoints([].concat(toAdd).concat(toRemove), false);
await this._updateExtensionsOnExtHosts(result.versionId, toAdd, ( toRemove.map(e => e.identifier)));
for (let i = 0; i < toAdd.length; i++) {
this._activateAddedExtensionIfNeeded(toAdd[i]);
}
}
async _updateExtensionsOnExtHosts(versionId, toAdd, toRemove) {
const removedRunningLocation = this._runningLocations.deltaExtensions(toAdd, toRemove);
const promises = ( this._extensionHostManagers.map(
extHostManager => this._updateExtensionsOnExtHost(extHostManager, versionId, toAdd, toRemove, removedRunningLocation)
));
await Promise.all(promises);
}
async _updateExtensionsOnExtHost(extensionHostManager, versionId, toAdd, toRemove, removedRunningLocation) {
const myToAdd = this._runningLocations.filterByExtensionHostManager(toAdd, extensionHostManager);
const myToRemove = filterExtensionIdentifiers(
toRemove,
removedRunningLocation,
extRunningLocation => extensionHostManager.representsRunningLocation(extRunningLocation)
);
const addActivationEvents = ImplicitActivationEvents.createActivationEventsMap(toAdd);
if (isCI) {
const printExtIds = extensions => ( extensions.map(e => e.identifier.value)).join(",");
const printIds = extensions => ( extensions.map(e => e.value)).join(",");
this._logService.info(
`AbstractExtensionService: Calling deltaExtensions: toRemove: [${printIds(toRemove)}], toAdd: [${printExtIds(toAdd)}], myToRemove: [${printIds(myToRemove)}], myToAdd: [${printExtIds(myToAdd)}],`
);
}
await extensionHostManager.deltaExtensions({
versionId,
toRemove,
toAdd,
addActivationEvents,
myToRemove,
myToAdd: ( myToAdd.map(extension => extension.identifier))
});
}
canAddExtension(extension) {
return this._canAddExtension(extension, []);
}
_canAddExtension(extension, extensionsBeingRemoved) {
const existing = this._registry.getExtensionDescriptionByIdOrUUID(extension.identifier, extension.id);
if (existing) {
const isBeingRemoved = ( extensionsBeingRemoved.some(
extensionDescription => ExtensionIdentifier.equals(extension.identifier, extensionDescription.identifier)
));
if (!isBeingRemoved) {
return false;
}
}
const extensionKinds = this._runningLocations.readExtensionKinds(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const extensionHostKind = this._extensionHostKindPicker.pickExtensionHostKind(
extension.identifier,
extensionKinds,
!isRemote,
isRemote,
ExtensionRunningPreference.None
);
if (extensionHostKind === null) {
return false;
}
return true;
}
canRemoveExtension(extension) {
const extensionDescription = this._registry.getExtensionDescription(extension.identifier);
if (!extensionDescription) {
return false;
}
if (this._extensionStatus.get(extensionDescription.identifier)?.activationStarted) {
return false;
}
return true;
}
async _activateAddedExtensionIfNeeded(extensionDescription) {
let shouldActivateReason = null;
let hasWorkspaceContains = false;
const activationEvents = this._activationEventReader.readActivationEvents(extensionDescription);
for (const activationEvent of activationEvents) {
if (( this._allRequestedActivateEvents.has(activationEvent))) {
shouldActivateReason = activationEvent;
break;
}
if (activationEvent === "*") {
shouldActivateReason = activationEvent;
break;
}
if (/^workspaceContains/.test(activationEvent)) {
hasWorkspaceContains = true;
}
if (activationEvent === "onStartupFinished") {
shouldActivateReason = activationEvent;
break;
}
}
if (!shouldActivateReason && hasWorkspaceContains) {
const workspace = await this._contextService.getCompleteWorkspace();
const forceUsingSearch = !!this._environmentService.remoteAuthority;
const host = {
logService: this._logService,
folders: ( workspace.folders.map(folder => folder.uri)),
forceUsingSearch: forceUsingSearch,
exists: uri => this._fileService.exists(uri),
checkExists: (folders, includes, token) => this._instantiationService.invokeFunction(accessor => checkGlobFileExists(accessor, folders, includes, token))
};
const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription);
if (result) {
shouldActivateReason = result.activationEvent;
}
}
if (shouldActivateReason) {
await Promise.all(( this._extensionHostManagers.map(
extHostManager => extHostManager.activate(extensionDescription.identifier, {
startup: false,
extensionId: extensionDescription.identifier,
activationEvent: shouldActivateReason
})
)));
}
}
_initializeIfNeeded() {
if (!this._initializePromise) {
this._initializePromise = this._initialize();
}
return this._initializePromise;
}
async _initialize() {
mark("code/willLoadExtensions");
this._startExtensionHostsIfNecessary(true, []);
const lock = await this._registry.acquireLock("_initialize");
try {
await this._resolveAndProcessExtensions(lock);
this._startOnDemandExtensionHosts();
} finally {
lock.dispose();
}
this._releaseBarrier();
mark("code/didLoadExtensions");
this._activateDeferredRemoteEvents();
await this._handleExtensionTests();
}
async _activateDeferredRemoteEvents() {
if (this._pendingRemoteActivationEvents.size === 0) {
return;
}
const remoteExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.Remote);
if (remoteExtensionHosts.length === 0) {
this._pendingRemoteActivationEvents.clear();
return;
}
await Promise.all(( remoteExtensionHosts.map(extHost => extHost.ready())));
for (const activationEvent of this._pendingRemoteActivationEvents) {
const result = Promise.all(( remoteExtensionHosts.map(
extHostManager => extHostManager.activateByEvent(activationEvent, ActivationKind.Normal)
))).then(() => {});
this._onWillActivateByEvent.fire({
event: activationEvent,
activation: result,
activationKind: ActivationKind.Normal
});
}
this._pendingRemoteActivationEvents.clear();
}
async _resolveAndProcessExtensions(lock) {
let resolverExtensions = [];
let localExtensions = [];
let remoteExtensions = [];
for await (const extensions of this._resolveExtensions()) {
if (extensions instanceof ResolverExtensions) {
resolverExtensions = checkEnabledAndProposedAPI(
this._logService,
this._extensionEnablementService,
this._extensionsProposedApi,
extensions.extensions,
false
);
this._registry.deltaExtensions(lock, resolverExtensions, []);
this._doHandleExtensionPoints(resolverExtensions, true);
}
if (extensions instanceof LocalExtensions) {
localExtensions = checkEnabledAndProposedAPI(
this._logService,
this._extensionEnablementService,
this._extensionsProposedApi,
extensions.extensions,
false
);
}
if (extensions instanceof RemoteExtensions) {
remoteExtensions = checkEnabledAndProposedAPI(
this._logService,
this._extensionEnablementService,
this._extensionsProposedApi,
extensions.extensions,
false
);
}
}
this._runningLocations.initializeRunningLocation(localExtensions, remoteExtensions);
this._startExtensionHostsIfNecessary(true, []);
const remoteExtensionsThatNeedToRunLocally = (this._allowRemoteExtensionsInLocalWebWorker ? this._runningLocations.filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.LocalWebWorker) : []);
const localProcessExtensions = (this._hasLocalProcess ? this._runningLocations.filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalProcess) : []);
const localWebWorkerExtensions = this._runningLocations.filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalWebWorker);
remoteExtensions = this._runningLocations.filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.Remote);
for (const ext of remoteExtensionsThatNeedToRunLocally) {
if (!includes(localWebWorkerExtensions, ext.identifier)) {
localWebWorkerExtensions.push(ext);
}
}
const allExtensions = remoteExtensions.concat(localProcessExtensions).concat(localWebWorkerExtensions);
let toAdd = allExtensions;
if (resolverExtensions.length) {
toAdd = allExtensions.filter(extension => !( resolverExtensions.some(
e => ExtensionIdentifier.equals(e.identifier, extension.identifier) && ( e.extensionLocation.toString()) === ( extension.extensionLocation.toString())
)));
if (allExtensions.length < toAdd.length + resolverExtensions.length) {
const toRemove = resolverExtensions.filter(registered => !( allExtensions.some(
e => ExtensionIdentifier.equals(e.identifier, registered.identifier) && ( e.extensionLocation.toString()) === ( registered.extensionLocation.toString())
)));
if (toRemove.length) {
this._registry.deltaExtensions(lock, [], ( toRemove.map(e => e.identifier)));
this._doHandleExtensionPoints(toRemove, true);
}
}
}
const result = this._registry.deltaExtensions(lock, toAdd, []);
if (result.removedDueToLooping.length > 0) {
this._notificationService.notify({
severity: Severity.Error,
message: ( localize(
16634,
"The following extensions contain dependency loops and have been disabled: {0}",
( result.removedDueToLooping.map(e => `'${e.identifier.value}'`)).join(", ")
))
});
}
this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions(), false);
}
async _handleExtensionTests() {
if (!this._environmentService.isExtensionDevelopment || !this._environmentService.extensionTestsLocationURI) {
return;
}
const extensionHostManager = this.findTestExtensionHost(this._environmentService.extensionTestsLocationURI);
if (!extensionHostManager) {
const msg = ( localize(
16635,
"No extension host found that can launch the test runner at {0}.",
(this._environmentService.extensionTestsLocationURI.toString())
));
console.error(msg);
this._notificationService.error(msg);
return;
}
let exitCode;
try {
exitCode = await extensionHostManager.extensionTestsExecute();
if (isCI) {
this._logService.info(`Extension host test runner exit code: ${exitCode}`);
}
} catch (err) {
if (isCI) {
this._logService.error(`Extension host test runner error`, err);
}
console.error(err);
exitCode = 1 ;
}
this._onExtensionHostExit(exitCode);
}
findTestExtensionHost(testLocation) {
let runningLocation = null;
for (const extension of this._registry.getAllExtensionDescriptions()) {
if (isEqualOrParent(testLocation, extension.extensionLocation)) {
runningLocation = this._runningLocations.getRunningLocation(extension.identifier);
break;
}
}
if (runningLocation === null) {
if (testLocation.scheme === Schemas.vscodeRemote) {
runningLocation = ( new RemoteRunningLocation());
} else {
runningLocation = ( new LocalProcessRunningLocation(0));
}
}
if (runningLocation !== null) {
return this._extensionHostManagers.getByRunningLocation(runningLocation);
}
return null;
}
_releaseBarrier() {
this._installedExtensionsReady.open();
this._onDidRegisterExtensions.fire(undefined);
this._onDidChangeExtensionsStatus.fire(( this._registry.getAllExtensionDescriptions().map(e => e.identifier)));
}
async _resolveAuthorityInitial(remoteAuthority) {
const MAX_ATTEMPTS = 5;
for (let attempt = 1; ; attempt++) {
try {
return this._resolveAuthorityWithLogging(remoteAuthority);
} catch (err) {
if (RemoteAuthorityResolverError.isNoResolverFound(err)) {
throw err;
}
if (RemoteAuthorityResolverError.isNotAvailable(err)) {
throw err;
}
if (attempt >= MAX_ATTEMPTS) {
throw err;
}
}
}
}
async _resolveAuthorityAgain() {
const remoteAuthority = this._environmentService.remoteAuthority;
if (!remoteAuthority) {
return;
}
this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);
try {
const result = await this._resolveAuthorityWithLogging(remoteAuthority);
this._remoteAuthorityResolverService._setResolvedAuthority(result.authority, result.options);
} catch (err) {
this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);
}
}
async _resolveAuthorityWithLogging(remoteAuthority) {
const authorityPrefix = getRemoteAuthorityPrefix(remoteAuthority);
const sw = StopWatch.create(false);
this._logService.info(`Invoking resolveAuthority(${authorityPrefix})...`);
try {
mark(`code/willResolveAuthority/${authorityPrefix}`);
const result = await this._resolveAuthority(remoteAuthority);
mark(`code/didResolveAuthorityOK/${authorityPrefix}`);
this._logService.info(
`resolveAuthority(${authorityPrefix}) returned '${result.authority.connectTo}' after ${sw.elapsed()} ms`
);
return result;
} catch (err) {
mark(`code/didResolveAuthorityError/${authorityPrefix}`);
this._logService.error(
`resolveAuthority(${authorityPrefix}) returned an error after ${sw.elapsed()} ms`,
err
);
throw err;
}
}
async _resolveAuthorityOnExtensionHosts(kind, remoteAuthority) {
const extensionHosts = this._getExtensionHostManagers(kind);
if (extensionHosts.length === 0) {
throw ( new Error(`Cannot resolve authority`));
}
this._resolveAuthorityAttempt++;
const results = await Promise.all(( extensionHosts.map(
extHost => extHost.resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt)
)));
let bestErrorResult = null;
for (const result of results) {
if (result.type === "ok") {
return result.value;
}
if (!bestErrorResult) {
bestErrorResult = result;
continue;
}
const bestErrorIsUnknown = (bestErrorResult.error.code === RemoteAuthorityResolverErrorCode.Unknown);
const errorIsUnknown = (result.error.code === RemoteAuthorityResolverErrorCode.Unknown);
if (bestErrorIsUnknown && !errorIsUnknown) {
bestErrorResult = result;
}
}
throw ( new RemoteAuthorityResolverError(
bestErrorResult.error.message,
bestErrorResult.error.code,
bestErrorResult.error.detail
));
}
async stopExtensionHosts(reason, auto) {
await this._initializeIfNeeded();
return this._doStopExtensionHostsWithVeto(reason, auto);
}
async _doStopExtensionHosts() {
const previouslyActivatedExtensionIds = [];
for (const extensionStatus of ( this._extensionStatus.values())) {
if (extensionStatus.activationStarted) {
previouslyActivatedExtensionIds.push(extensionStatus.id);
}
}
await this._extensionHostManagers.stopAllInReverse();
for (const extensionStatus of ( this._extensionStatus.values())) {
extensionStatus.clearRuntimeStatus();
}
if (previouslyActivatedExtensionIds.length > 0) {
this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
}
}
async _doStopExtensionHostsWithVeto(reason, auto = false) {
if (auto && this._environmentService.isExtensionDevelopment) {
return false;
}
const vetos = [];
const vetoReasons = ( new Set());
this._onWillStop.fire({
reason,
auto,
veto(value, reason) {
vetos.push(value);
if (typeof value === "boolean") {
if (value === true) {
vetoReasons.add(reason);
}
} else {
value.then(value => {
if (value) {
vetoReasons.add(reason);
}
}).catch(error => {
vetoReasons.add(( localize(16636, "{0} (Error: {1})", reason, toErrorMessage(error))));
});
}
}
});
const veto = await handleVetos(vetos, error => this._logService.error(error));
if (!veto) {
await this._doStopExtensionHosts();
} else {
if (!auto) {
const vetoReasonsArray = Array.from(vetoReasons);
this._logService.warn(
`Extension host was not stopped because of veto (stop reason: ${reason}, veto reason: ${vetoReasonsArray.join(", ")})`
);
const {
confirmed
} = await this._dialogService.confirm({
type: Severity.Warning,
message: ( localize(16637, "Please confirm restart of extensions.")),
detail: vetoReasonsArray.length === 1 ? vetoReasonsArray[0] : vetoReasonsArray.join("\n -"),
primaryButton: ( localize(16638, "Restart Anyway"))
});
if (confirmed) {
return true;
}
}
}
return !veto;
}
_startExtensionHostsIfNecessary(isInitialStart, initialActivationEvents) {
const locations = [];
for (let affinity = 0; affinity <= this._runningLocations.maxLocalProcessAffinity; affinity++) {
locations.push(( new LocalProcessRunningLocation(affinity)));
}
for (let affinity = 0; affinity <= this._runningLocations.maxLocalWebWorkerAffinity; affinity++) {
locations.push(( new LocalWebWorkerRunningLocation(affinity)));
}
locations.push(( new RemoteRunningLocation()));
for (const location of locations) {
if (this._extensionHostManagers.getByRunningLocation(location)) {
continue;
}
const res = this._createExtensionHostManager(location, isInitialStart, initialActivationEvents);
if (res) {
const [extHostManager, disposableStore] = res;
this._extensionHostManagers.add(extHostManager, disposableStore);
}
}
}
_createExtensionHostManager(runningLocation, isInitialStart, initialActivationEvents) {
const extensionHost = this._extensionHostFactory.createExtensionHost(this._runningLocations, runningLocation, isInitialStart);
if (!extensionHost) {
return null;
}
const processManager = this._doCreateExtensionHostManager(extensionHost, initialActivationEvents);
const disposableStore = ( new DisposableStore());
disposableStore.add(processManager.onDidExit(
([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal)
));
disposableStore.add(processManager.onDidChangeResponsiveState(responsiveState => {
this._logService.info(
`Extension host (${processManager.friendyName}) is ${responsiveState === ResponsiveState.Responsive ? "responsive" : "unresponsive"}.`
);
this._onDidChangeResponsiveChange.fire({
extensionHostKind: processManager.kind,
isResponsive: responsiveState === ResponsiveState.Responsive,
getInspectListener: tryEnableInspector => {
return processManager.getInspectPort(tryEnableInspector);
}
});
}));
return [processManager, disposableStore];
}
_doCreateExtensionHostManager(extensionHost, initialActivationEvents) {
const internalExtensionService = this._acquireInternalAPI(extensionHost);
if (extensionHost.startup === ExtensionHostStartup.LazyAutoStart) {
return this._instantiationService.createInstance(
LazyCreateExtensionHostManager,
extensionHost,
initialActivationEvents,
internalExtensionService
);
}
return this._instantiationService.createInstance(
ExtensionHostManager,
extensionHost,
initialActivationEvents,
internalExtensionService
);
}
_onExtensionHostCrashOrExit(extensionHost, code, signal) {
const isExtensionDevHost = parseExtensionDevOptions(this._environmentService).isExtensionDevHost;
if (!isExtensionDevHost) {
this._onExtensionHostCrashed(extensionHost, code, signal);
return;
}
this._onExtensionHostExit(code);
}
_onExtensionHostCrashed(extensionHost, code, signal) {
console.error(
`Extension host (${extensionHost.friendyName}) terminated unexpectedly. Code: ${code}, Signal: ${signal}`
);
if (extensionHost.kind === ExtensionHostKind.LocalProcess) {
this._doStopExtensionHosts();
} else if (extensionHost.kind === ExtensionHostKind.Remote) {
if (signal) {
this._onRemoteExtensionHostCrashed(extensionHost, signal);
}
this._extensionHostManagers.stopOne(extensionHost);
}
}
_getExtensionHostExitInfoWithTimeout(reconnectionToken) {
return ( new Promise((resolve, reject) => {
const timeoutHandle = setTimeout(() => {
reject(( new Error("getExtensionHostExitInfo timed out")));
}, 2000);
this._remoteAgentService.getExtensionHostExitInfo(reconnectionToken).then(r => {
clearTimeout(timeoutHandle);
resolve(r);
}, reject);
}));
}
async _onRemoteExtensionHostCrashed(extensionHost, reconnectionToken) {
try {
const info = await this._getExtensionHostExitInfoWithTimeout(reconnectionToken);
if (info) {
this._logService.error(
`Extension host (${extensionHost.friendyName}) terminated unexpectedly with code ${info.code}.`
);
}
this._logExtensionHostCrash(extensionHost);
this._remoteCrashTracker.registerCrash();
if (this._remoteCrashTracker.shouldAutomaticallyRestart()) {
this._logService.info(`Automatically restarting the remote extension host.`);
this._notificationService.status(( localize(16639, "The remote extension host terminated unexpectedly. Restarting...")), {
hideAfter: 5000
});
this._startExtensionHostsIfNecessary(false, Array.from(( this._allRequestedActivateEvents.keys())));
} else {
this._notificationService.prompt(Severity.Error, ( localize(
16640,
"Remote Extension host terminated unexpectedly 3 times within the last 5 minutes."
)), [{
label: ( localize(16641, "Restart Remote Extension Host")),
run: () => {
this._startExtensionHostsIfNecessary(false, Array.from(( this._allRequestedActivateEvents.keys())));
}
}]);
}
} catch (err) {}
}
_logExtensionHostCrash(extensionHost) {
const activatedExtensions = [];
for (const extensionStatus of ( this._extensionStatus.values())) {
if (extensionStatus.activationStarted && extensionHost.containsExtension(extensionStatus.id)) {
activatedExtensions.push(extensionStatus.id);
}
}
if (activatedExtensions.length > 0) {
this._logService.error(
`Extension host (${extensionHost.friendyName}) terminated unexpectedly. The following extensions were running: ${( activatedExtensions.map(id => id.value)).join(", ")}`
);
} else {
this._logService.error(
`Extension host (${extensionHost.friendyName}) terminated unexpectedly. No extensions were activated.`
);
}
}
async startExtensionHosts(updates) {
await this._doStopExtensionHosts();
if (updates) {
await this._handleDeltaExtensions(( new DeltaExtensionsQueueItem(updates.toAdd, updates.toRemove)));
}
const lock = await this._registry.acquireLock("startExtensionHosts");
try {
this._startExtensionHostsIfNecessary(false, Array.from(( this._allRequestedActivateEvents.keys())));
this._startOnDemandExtensionHosts();
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
await Promise.all(( localProcessExtensionHosts.map(extHost => extHost.ready())));
} finally {
lock.dispose();
}
}
_startOnDemandExtensionHosts() {
const snapshot = this._registry.getSnapshot();
for (const extHostManager of this._extensionHostManagers) {
if (extHostManager.startup !== ExtensionHostStartup.EagerAutoStart) {
const extensions = this._runningLocations.filterByExtensionHostManager(snapshot.extensions, extHostManager);
extHostManager.start(snapshot.versionId, snapshot.extensions, ( extensions.map(extension => extension.identifier)));
}
}
}
activateByEvent(activationEvent, activationKind = ActivationKind.Normal) {
if (this._installedExtensionsReady.isOpen()) {
this._allRequestedActivateEvents.add(activationEvent);
if (!this._registry.containsActivationEvent(activationEvent)) {
return NO_OP_VOID_PROMISE;
}
return this._activateByEvent(activationEvent, activationKind);
} else {
this._allRequestedActivateEvents.add(activationEvent);
if (activationKind === ActivationKind.Immediate) {
void this._initializeIfNeeded();
return this._activateByEvent(activationEvent, activationKind);
}
return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent, activationKind));
}
}
_activateByEvent(activationEvent, activationKind) {
let managers;
if (activationKind === ActivationKind.Immediate) {
managers = this._extensionHostManagers.filter(
extHostManager => extHostManager.kind === ExtensionHostKind.LocalProcess || extHostManager.kind === ExtensionHostKind.LocalWebWorker || extHostManager.isReady
);
this._pendingRemoteActivationEvents.add(activationEvent);
} else {
managers = [...this._extensionHostManagers];
}
const result = Promise.all(( managers.map(
extHostManager => extHostManager.activateByEvent(activationEvent, activationKind)
))).then(() => {});
this._onWillActivateByEvent.fire({
event: activationEvent,
activation: result,
activationKind
});
return result;
}
activateById(extensionId, reason) {
return this._activateById(extensionId, reason);
}
activationEventIsDone(activationEvent) {
if (!this._installedExtensionsReady.isOpen()) {
return false;
}
if (!this._registry.containsActivationEvent(activationEvent)) {
return true;
}
return this._extensionHostManagers.every(manager => manager.activationEventIsDone(activationEvent));
}
whenInstalledExtensionsRegistered() {
return this._installedExtensionsReady.wait();
}
get extensions() {
return this._registry.getAllExtensionDescriptions();
}
_getExtensionRegistrySnapshotWhenReady() {
return this._installedExtensionsReady.wait().then(() => this._registry.getSnapshot());
}
getExtension(id) {
return this._installedExtensionsReady.wait().then(() => {
return this._registry.getExtensionDescription(id);
});
}
readExtensionPointContributions(extPoint) {
return this._installedExtensionsReady.wait().then(() => {
const availableExtensions = this._registry.getAllExtensionDescriptions();
const result = [];
for (const desc of availableExtensions) {
if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) {
result.push(( new ExtensionPointContribution(desc, desc.contributes[extPoint.name])));
}
}
return result;
});
}
getExtensionsStatus() {
const result = Object.create(null);
if (this._registry) {
const extensions = this._registry.getAllExtensionDescriptions();
for (const extension of extensions) {
con