Skip to content

Commit

Permalink
#125417 restart extension host for updating extensions instead of reload
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 committed Mar 6, 2024
1 parent cd6bd0a commit 146fb04
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import {
InstallDropdownAction, InstallingLabelAction,
LocalInstallAction,
MigrateDeprecatedExtensionAction,
ReloadAction,
ExtensionRuntimeStateAction,
RemoteInstallAction,
SetColorThemeAction,
SetFileIconThemeAction,
Expand Down Expand Up @@ -315,7 +315,7 @@ export class ExtensionEditor extends EditorPane {

const installAction = this.instantiationService.createInstance(InstallDropdownAction);
const actions = [
this.instantiationService.createInstance(ReloadAction),
this.instantiationService.createInstance(ExtensionRuntimeStateAction),
this.instantiationService.createInstance(ExtensionStatusLabelAction),
this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.updateActions', '',
[[this.instantiationService.createInstance(UpdateAction, true)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, true, [true, 'onlyEnabledExtensions'])]]),
Expand Down
18 changes: 9 additions & 9 deletions src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1554,28 +1554,28 @@ export class DisableDropDownAction extends ActionWithDropDownAction {

}

export class ReloadAction extends ExtensionAction {
export class ExtensionRuntimeStateAction extends ExtensionAction {

private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} reload`;
private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`;
private static readonly DisabledClass = `${ExtensionRuntimeStateAction.EnabledClass} disabled`;

updateWhenCounterExtensionChanges: boolean = true;

constructor(
@IHostService private readonly hostService: IHostService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IUpdateService private readonly updateService: IUpdateService,
@IExtensionService private readonly extensionService: IExtensionService,
@IProductService private readonly productService: IProductService,
) {
super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false);
super('extensions.runtimeState', '', ExtensionRuntimeStateAction.DisabledClass, false);
this._register(this.extensionService.onDidChangeExtensions(() => this.update()));
this.update();
}

update(): void {
this.enabled = false;
this.tooltip = '';
this.class = ReloadAction.DisabledClass;
this.class = ExtensionRuntimeStateAction.DisabledClass;

if (!this.extension) {
return;
Expand All @@ -1596,18 +1596,18 @@ export class ReloadAction extends ExtensionAction {
}

this.enabled = true;
this.class = ReloadAction.EnabledClass;
this.class = ExtensionRuntimeStateAction.EnabledClass;
this.tooltip = runtimeState.reason;
this.label = runtimeState.action === ExtensionRuntimeActionType.Reload ? localize('reload window', 'Reload Window')
this.label = runtimeState.action === ExtensionRuntimeActionType.RestartExtHost ? localize('restart ext host', 'Restart Extension Host')
: runtimeState.action === ExtensionRuntimeActionType.QuitAndInstall ? localize('restart product', 'Restart {0}', this.productService.nameShort)
: runtimeState.action === ExtensionRuntimeActionType.ApplyUpdate || runtimeState.action === ExtensionRuntimeActionType.DownloadUpdate ? localize('update product', 'Update {0}', this.productService.nameShort) : '';
}

override async run(): Promise<any> {
const runtimeState = this.extension?.runtimeState;

if (runtimeState?.action === ExtensionRuntimeActionType.Reload) {
return this.hostService.reload();
if (runtimeState?.action === ExtensionRuntimeActionType.RestartExtHost) {
return this.extensionsWorkbenchService.updateRunningExtensions();
}

else if (runtimeState?.action === ExtensionRuntimeActionType.DownloadUpdate) {
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/contrib/extensions/browser/extensionsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { Event } from 'vs/base/common/event';
import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction, ToggleAutoUpdateForExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ManageExtensionAction, ExtensionRuntimeStateAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction, ToggleAutoUpdateForExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, extensionVerifiedPublisherIconColor, VerifiedPublisherWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets';
import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions';
Expand Down Expand Up @@ -117,7 +117,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
const actions = [
this.instantiationService.createInstance(ExtensionStatusLabelAction),
this.instantiationService.createInstance(MigrateDeprecatedExtensionAction, true),
this.instantiationService.createInstance(ReloadAction),
this.instantiationService.createInstance(ExtensionRuntimeStateAction),
this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.updateActions', '',
[[this.instantiationService.createInstance(UpdateAction, false)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, true, [true, 'onlyEnabledExtensions'])]]),
this.instantiationService.createInstance(InstallDropdownAction),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,39 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
return undefined;
}

async updateRunningExtensions(): Promise<void> {
const toAdd: ILocalExtension[] = [];
const toRemove: string[] = [];
for (const extension of this.local) {
const runtimeState = extension.runtimeState;
if (!runtimeState || runtimeState.action !== ExtensionRuntimeActionType.RestartExtHost) {
continue;
}
if (extension.state === ExtensionState.Uninstalled) {
toRemove.push(extension.identifier.id);
continue;
}
if (!extension.local) {
continue;
}
const isEnabled = this.extensionEnablementService.isEnabled(extension.local);
if (isEnabled) {
const runningExtension = this.extensionService.extensions.find(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, extension.identifier));
if (runningExtension) {
toRemove.push(runningExtension.identifier.value);
}
toAdd.push(extension.local);
} else {
toRemove.push(extension.identifier.id);
}
}
if (toAdd.length || toRemove.length) {
if (await this.extensionService.stopExtensionHosts(nls.localize('restart', "Enable or Disable extensions"))) {
await this.extensionService.startExtensionHosts({ toAdd, toRemove });
}
}
}

private getRuntimeState(extension: IExtension): ExtensionRuntimeState | undefined {
const isUninstalled = extension.state === ExtensionState.Uninstalled;
const runningExtension = this.extensionService.extensions.find(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, extension.identifier));
Expand All @@ -1119,7 +1152,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
const canRemoveRunningExtension = runningExtension && this.extensionService.canRemoveExtension(runningExtension);
const isSameExtensionRunning = runningExtension && (!extension.server || extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)));
if (!canRemoveRunningExtension && isSameExtensionRunning && !runningExtension.isUnderDevelopment) {
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('postUninstallTooltip', "Please reload {0} to complete the uninstallation of this extension.", this.productService.nameLong) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('postUninstallTooltip', "Please restart Extension Host to complete the uninstallation of this extension.") };
}
return undefined;
}
Expand Down Expand Up @@ -1157,20 +1190,20 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}
return undefined;
}
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('postUpdateTooltip', "Please reload {0} to enable the updated extension.", this.productService.nameLong) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('postUpdateTooltip', "Please restart Extension Host to enable the updated extension.") };
}

if (this.extensionsServers.length > 1) {
const extensionInOtherServer = this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier) && e.server !== extension.server)[0];
if (extensionInOtherServer) {
// This extension prefers to run on UI/Local side but is running in remote
if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnUI(extension.local.manifest) && extensionInOtherServer.server === this.extensionManagementServerService.localExtensionManagementServer) {
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('enable locally', "Please reload {0} to enable this extension locally.", this.productService.nameLong) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('enable locally', "Please restart Extension Host to enable this extension locally.") };
}

// This extension prefers to run on Workspace/Remote side but is running in local
if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(extension.local.manifest) && extensionInOtherServer.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('enable remote', "Please reload {0} to enable this extension in {1}.", this.productService.nameLong, this.extensionManagementServerService.remoteExtensionManagementServer?.label) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('enable remote', "Please restart Extension Host to enable this extension in {1}.", this.extensionManagementServerService.remoteExtensionManagementServer?.label) };
}
}
}
Expand All @@ -1180,20 +1213,20 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
if (extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {
// This extension prefers to run on UI/Local side but is running in remote
if (this.extensionManifestPropertiesService.prefersExecuteOnUI(extension.local.manifest)) {
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('postEnableTooltip', "Please reload {0} to enable this extension.", this.productService.nameLong) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('postEnableTooltip', "Please restart Extension Host to enable this extension.") };
}
}
if (extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {
// This extension prefers to run on Workspace/Remote side but is running in local
if (this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(extension.local.manifest)) {
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('postEnableTooltip', "Please reload {0} to enable this extension.", this.productService.nameLong) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('postEnableTooltip', "Please restart Extension Host to enable this extension.") };
}
}
}
return undefined;
} else {
if (isSameExtensionRunning) {
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('postDisableTooltip', "Please reload {0} to disable this extension.", this.productService.nameLong) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('postDisableTooltip', "Please restart Extension Host to disable this extension.") };
}
}
return undefined;
Expand All @@ -1202,15 +1235,15 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
// Extension is not running
else {
if (isEnabled && !this.extensionService.canAddExtension(toExtensionDescription(extension.local))) {
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('postEnableTooltip', "Please reload {0} to enable this extension.", this.productService.nameLong) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('postEnableTooltip', "Please restart Extension Host to enable this extension.") };
}

const otherServer = extension.server ? extension.server === this.extensionManagementServerService.localExtensionManagementServer ? this.extensionManagementServerService.remoteExtensionManagementServer : this.extensionManagementServerService.localExtensionManagementServer : null;
if (otherServer && extension.enablementState === EnablementState.DisabledByExtensionKind) {
const extensionInOtherServer = this.local.filter(e => areSameExtensions(e.identifier, extension.identifier) && e.server === otherServer)[0];
// Same extension in other server exists and
if (extensionInOtherServer && extensionInOtherServer.local && this.extensionEnablementService.isEnabled(extensionInOtherServer.local)) {
return { action: ExtensionRuntimeActionType.Reload, reason: nls.localize('postEnableTooltip', "Please reload {0} to enable this extension.", this.productService.nameLong) };
return { action: ExtensionRuntimeActionType.RestartExtHost, reason: nls.localize('postEnableTooltip', "Please restart Extension Host to enable this extension.") };
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/contrib/extensions/common/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const enum ExtensionState {
}

export const enum ExtensionRuntimeActionType {
Reload = 'reload',
RestartExtHost = 'restartExtHosts',
DownloadUpdate = 'downloadUpdate',
ApplyUpdate = 'applyUpdate',
QuitAndInstall = 'quitAndInstall',
Expand Down Expand Up @@ -139,6 +139,7 @@ export interface IExtensionsWorkbenchService {
checkForUpdates(): Promise<void>;
getExtensionStatus(extension: IExtension): IExtensionsStatus | undefined;
updateAll(): Promise<InstallExtensionResult[]>;
updateRunningExtensions(): Promise<void>;

// Sync APIs
isExtensionIgnoredToSync(extension: IExtension): boolean;
Expand Down
Loading

0 comments on commit 146fb04

Please sign in to comment.