Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extension host - show reason when veto (#180514) #182718

Merged
merged 2 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWor
if (environmentService.remoteAuthority) {
hostService.reload(); // TODO@aeschli, workaround
} else if (isNative) {
const stopped = await extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Restart of extensions required because of workspace folder change."));
const stopped = await extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Restart Extension Host."));
if (stopped) {
extensionService.startExtensionHosts();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { ExtensionKind } from 'vs/platform/environment/common/environment';
import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
Expand Down Expand Up @@ -63,6 +64,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService,
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,
@IDialogService dialogService: IDialogService,
) {
const extensionsProposedApi = instantiationService.createInstance(ExtensionsProposedApi);
const extensionHostFactory = new BrowserExtensionHostFactory(
Expand Down Expand Up @@ -93,7 +95,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
remoteAgentService,
remoteExtensionsScannerService,
lifecycleService,
remoteAuthorityResolverService
remoteAuthorityResolverService,
dialogService
);

// Initialize installed extensions first and do it only after workbench is ready
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { StopWatch } from 'vs/base/common/stopwatch';
import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ImplicitActivationEvents } from 'vs/platform/extensionManagement/common/implicitActivationEvents';
import { ExtensionIdentifier, ExtensionIdentifierMap, IExtension, IExtensionContributions, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
Expand Down Expand Up @@ -104,6 +105,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
@IRemoteExtensionsScannerService protected readonly _remoteExtensionsScannerService: IRemoteExtensionsScannerService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IRemoteAuthorityResolverService protected readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
@IDialogService private readonly _dialogService: IDialogService,
) {
super();

Expand Down Expand Up @@ -657,19 +659,39 @@ export abstract class AbstractExtensionService extends Disposable implements IEx

private async _doStopExtensionHostsWithVeto(reason: string): Promise<boolean> {
const vetos: (boolean | Promise<boolean>)[] = [];
const vetoReasons: string[] = [];

this._onWillStop.fire({
reason,
veto(value) {
veto(value, reason) {
vetos.push(value);

if (typeof value === 'boolean') {
if (value === true) {
vetoReasons.push(reason);
}
} else {
value.then(value => {
if (value) {
vetoReasons.push(reason);
}
});
}
}
});

const veto = await handleVetos(vetos, error => this._logService.error(error));
if (!veto) {
this._doStopExtensionHosts();
} else {
this._logService.warn('Extension Host stop request was vetoed');
this._logService.warn(`Extension host was not stopped because of veto (stop reason: ${reason}, veto reason: ${vetoReasons.join(', ')})`);

await this._dialogService.warn(
nls.localize('extensionStopVetoMessage', "The following operation was blocked: {0}", reason),
vetoReasons.length === 1 ?
nls.localize('extensionStopVetoDetailsOne', "The reason for blocking the operation: {0}", vetoReasons[0]) :
nls.localize('extensionStopVetoDetailsMany', "The reasons for blocking the operation:\n- {0}", vetoReasons.join('\n -')),
);
}

return !veto;
Expand Down
6 changes: 3 additions & 3 deletions src/vs/workbench/services/extensions/common/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,10 @@ export interface WillStopExtensionHostsEvent {
* Allows to veto the stopping of extension hosts. The veto can be a long running
* operation.
*
* @param id to identify the veto operation in case it takes very long or never
* completes.
* @param reason a human readable reason for vetoing the extension host stop in case
* where the resolved `value: true`.
*/
veto(value: boolean | Promise<boolean>, id: string): void;
veto(value: boolean | Promise<boolean>, reason: string): void;
}

export interface IExtensionService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { ExtensionKind } from 'vs/platform/environment/common/environment';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
Expand Down Expand Up @@ -83,6 +84,7 @@ export class NativeExtensionService extends AbstractExtensionService implements
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,
@IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService,
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
@IDialogService dialogService: IDialogService,
) {
const extensionsProposedApi = instantiationService.createInstance(ExtensionsProposedApi);
const extensionScanner = instantiationService.createInstance(CachedExtensionScanner);
Expand Down Expand Up @@ -116,7 +118,8 @@ export class NativeExtensionService extends AbstractExtensionService implements
remoteAgentService,
remoteExtensionsScannerService,
lifecycleService,
remoteAuthorityResolverService
remoteAuthorityResolverService,
dialogService
);

this._extensionScanner = extensionScanner;
Expand Down Expand Up @@ -720,7 +723,7 @@ class RestartExtensionHostAction extends Action2 {
async run(accessor: ServicesAccessor): Promise<void> {
const extensionService = accessor.get(IExtensionService);

const stopped = await extensionService.stopExtensionHosts(nls.localize('restartExtensionHost.reason', "Restart of extensions explicitly requested by user."));
const stopped = await extensionService.stopExtensionHosts(nls.localize('restartExtensionHost.reason', "Restart Extension Host."));
if (stopped) {
extensionService.startExtensionHosts();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { mock } from 'vs/base/test/common/mock';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { ExtensionKind, IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionIdentifier, IExtension, IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
Expand Down Expand Up @@ -176,7 +177,8 @@ suite('ExtensionService', () => {
remoteAgentService,
remoteExtensionsScannerService,
lifecycleService,
remoteAuthorityResolverService
remoteAuthorityResolverService,
new TestDialogService()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi
}

async enterWorkspace(workspaceUri: URI): Promise<void> {
const stopped = await this.extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Restart of extensions required because of opening a multi-root workspace."));
const stopped = await this.extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Opening a multi-root workspace."));
if (!stopped) {
return;
}
Expand Down