mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-30 00:30:29 -04:00
Merge from vscode 2cfc8172e533e50c90e6a3152f6bfb1f82f963f3 (#6516)
* Merge from vscode 2cfc8172e533e50c90e6a3152f6bfb1f82f963f3 * fix tests
This commit is contained in:
@@ -44,6 +44,7 @@ import { ExtensionDependencyChecker } from 'vs/workbench/contrib/extensions/brow
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { RemoteExtensionsInstaller } from 'vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller';
|
||||
|
||||
// Singletons
|
||||
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
|
||||
@@ -188,12 +189,16 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
description: localize('extensionsCloseExtensionDetailsOnViewChange', "When enabled, editors with extension details will be automatically closed upon navigating away from the Extensions View."),
|
||||
default: false
|
||||
},
|
||||
// {{SQL CARBON EDIT}}
|
||||
'extensions.extensionsPolicy': {
|
||||
'extensions.extensionsPolicy': { // {{SQL CARBON EDIT}}
|
||||
type: 'string',
|
||||
description: localize('extensionsPolicy', "Sets the security policy for downloading extensions."),
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
default: ExtensionsPolicy.allowAll
|
||||
},
|
||||
'extensions.confirmedUriHandlerExtensionIds': {
|
||||
type: 'array',
|
||||
description: localize('handleUriConfirmedExtensions', "When an extension is listed here, a confirmation prompt will not be shown when that extension handles a URI."),
|
||||
default: []
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -379,3 +384,4 @@ workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase
|
||||
workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting);
|
||||
workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually);
|
||||
workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually);
|
||||
workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInstaller, LifecyclePhase.Eventually);
|
||||
|
||||
@@ -62,6 +62,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; // {{SQL CARBON EDIT}}
|
||||
|
||||
@@ -287,7 +288,7 @@ export class InstallAction extends ExtensionAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallInOtherServerAction extends ExtensionAction {
|
||||
export abstract class InstallInOtherServerAction extends ExtensionAction {
|
||||
|
||||
protected static INSTALL_LABEL = localize('install', "Install");
|
||||
protected static INSTALLING_LABEL = localize('installing', "Installing");
|
||||
@@ -296,7 +297,6 @@ export class InstallInOtherServerAction extends ExtensionAction {
|
||||
private static readonly InstallingClass = 'extension-action install installing';
|
||||
|
||||
updateWhenCounterExtensionChanges: boolean = true;
|
||||
protected installing: boolean = false;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -304,70 +304,63 @@ export class InstallInOtherServerAction extends ExtensionAction {
|
||||
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
) {
|
||||
super(id, InstallInOtherServerAction.INSTALL_LABEL, InstallInOtherServerAction.Class, false);
|
||||
this.updateLabel();
|
||||
this.update();
|
||||
}
|
||||
|
||||
private updateLabel(): void {
|
||||
this.label = this.getLabel();
|
||||
this.tooltip = this.label;
|
||||
}
|
||||
|
||||
protected getLabel(): string {
|
||||
return this.installing ? InstallInOtherServerAction.INSTALLING_LABEL :
|
||||
this.server ? `${InstallInOtherServerAction.INSTALL_LABEL} on ${this.server.label}`
|
||||
: InstallInOtherServerAction.INSTALL_LABEL;
|
||||
|
||||
}
|
||||
|
||||
update(): void {
|
||||
this.enabled = false;
|
||||
this.class = InstallInOtherServerAction.Class;
|
||||
if (this.installing) {
|
||||
this.enabled = true;
|
||||
this.class = InstallInOtherServerAction.InstallingClass;
|
||||
this.updateLabel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this.extension && this.extension.local && this.server && this.extension.state === ExtensionState.Installed
|
||||
this.extension && this.extension.local && this.server && this.extension.state === ExtensionState.Installed && this.extension.type === ExtensionType.User
|
||||
// disabled by extension kind or it is a language pack extension
|
||||
&& (this.extension.enablementState === EnablementState.DisabledByExtensionKind || isLanguagePackExtension(this.extension.local.manifest))
|
||||
// Not installed in other server and can install in other server
|
||||
&& !this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.server)
|
||||
&& this.extensionsWorkbenchService.canInstall(this.extension)
|
||||
) {
|
||||
this.enabled = true;
|
||||
this.updateLabel();
|
||||
return;
|
||||
const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.server)[0];
|
||||
if (extensionInOtherServer) {
|
||||
// Getting installed in other server
|
||||
if (extensionInOtherServer.state === ExtensionState.Installing && !extensionInOtherServer.local) {
|
||||
this.enabled = true;
|
||||
this.label = InstallInOtherServerAction.INSTALLING_LABEL;
|
||||
this.class = InstallInOtherServerAction.InstallingClass;
|
||||
}
|
||||
} else {
|
||||
// Not installed in other server
|
||||
this.enabled = true;
|
||||
this.label = this.getInstallLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
if (this.server && !this.installing) {
|
||||
this.installing = true;
|
||||
this.update();
|
||||
if (this.server) {
|
||||
this.extensionsWorkbenchService.open(this.extension);
|
||||
alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));
|
||||
if (this.extension.gallery) {
|
||||
await this.server.extensionManagementService.installFromGallery(this.extension.gallery);
|
||||
this.installing = false;
|
||||
this.update();
|
||||
} else {
|
||||
const vsix = await this.extension.server!.extensionManagementService.zip(this.extension.local!);
|
||||
await this.server.extensionManagementService.install(vsix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract getInstallLabel(): string;
|
||||
}
|
||||
|
||||
export class RemoteInstallAction extends InstallInOtherServerAction {
|
||||
|
||||
constructor(
|
||||
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
|
||||
) {
|
||||
super(`extensions.remoteinstall`, extensionManagementServerService.remoteExtensionManagementServer, extensionsWorkbenchService);
|
||||
}
|
||||
|
||||
protected getInstallLabel(): string {
|
||||
return this.extensionManagementServerService.remoteExtensionManagementServer ? localize('Install on Server', "Install on {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label) : InstallInOtherServerAction.INSTALL_LABEL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class LocalInstallAction extends InstallInOtherServerAction {
|
||||
@@ -379,8 +372,8 @@ export class LocalInstallAction extends InstallInOtherServerAction {
|
||||
super(`extensions.localinstall`, extensionManagementServerService.localExtensionManagementServer, extensionsWorkbenchService);
|
||||
}
|
||||
|
||||
protected getLabel(): string {
|
||||
return this.installing ? InstallInOtherServerAction.INSTALLING_LABEL : localize('install locally', "Install Locally");
|
||||
protected getInstallLabel(): string {
|
||||
return localize('install locally', "Install Locally");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1220,7 +1213,7 @@ export class ReloadAction extends ExtensionAction {
|
||||
const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation);
|
||||
|
||||
if (isUninstalled) {
|
||||
if (isSameExtensionRunning) {
|
||||
if (isSameExtensionRunning && !this.extensionService.canRemoveExtension(runningExtension)) {
|
||||
this.enabled = true;
|
||||
this.label = localize('reloadRequired', "Reload Required");
|
||||
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
|
||||
@@ -1725,7 +1718,7 @@ export class InstallRecommendedExtensionAction extends Action {
|
||||
return this.viewletService.openViewlet(VIEWLET_ID, true)
|
||||
.then(viewlet => viewlet as IExtensionsViewlet)
|
||||
.then(viewlet => {
|
||||
viewlet.search('@recommended ');
|
||||
viewlet.search(`@id:${this.extensionId}`);
|
||||
viewlet.focus();
|
||||
return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None)
|
||||
.then(pager => {
|
||||
@@ -3092,6 +3085,119 @@ export class InstallSpecificVersionOfExtensionAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
interface IExtensionPickItem extends IQuickPickItem {
|
||||
extension?: IExtension;
|
||||
}
|
||||
|
||||
export class InstallLocalExtensionsOnRemoteAction extends Action {
|
||||
|
||||
constructor(
|
||||
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IProgressService private readonly progressService: IProgressService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
super('workbench.extensions.actions.installLocalExtensionsOnRemote');
|
||||
this.update();
|
||||
this._register(this.extensionsWorkbenchService.onChange(() => this.update()));
|
||||
}
|
||||
|
||||
get label(): string {
|
||||
return this.extensionManagementServerService.remoteExtensionManagementServer ?
|
||||
localize('install local extensions', "Install Local Extensions on {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label) : '';
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
this.enabled = this.getLocalExtensionsToInstall().length > 0;
|
||||
}
|
||||
|
||||
private getLocalExtensionsToInstall(): IExtension[] {
|
||||
return this.extensionsWorkbenchService.local.filter(extension => {
|
||||
const action = this.instantiationService.createInstance(RemoteInstallAction);
|
||||
action.extension = extension;
|
||||
return action.enabled;
|
||||
});
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
this.selectAndInstallLocalExtensions();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private selectAndInstallLocalExtensions(): void {
|
||||
const quickPick = this.quickInputService.createQuickPick<IExtensionPickItem>();
|
||||
quickPick.busy = true;
|
||||
const disposable = quickPick.onDidAccept(() => {
|
||||
disposable.dispose();
|
||||
quickPick.hide();
|
||||
quickPick.dispose();
|
||||
this.onDidAccept(quickPick.selectedItems);
|
||||
});
|
||||
quickPick.show();
|
||||
const localExtensionsToInstall = this.getLocalExtensionsToInstall();
|
||||
quickPick.busy = false;
|
||||
if (localExtensionsToInstall.length) {
|
||||
quickPick.placeholder = localize('select extensions to install', "Select extensions to install");
|
||||
quickPick.canSelectMany = true;
|
||||
localExtensionsToInstall.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName));
|
||||
quickPick.items = localExtensionsToInstall.map<IExtensionPickItem>(extension => ({ extension, label: extension.displayName, description: extension.version }));
|
||||
} else {
|
||||
quickPick.hide();
|
||||
quickPick.dispose();
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Info,
|
||||
message: localize('no local extensions', "There are no extensions to install.")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onDidAccept(selectedItems: ReadonlyArray<IExtensionPickItem>): void {
|
||||
if (selectedItems.length) {
|
||||
const localExtensionsToInstall = selectedItems.filter(r => !!r.extension).map(r => r.extension!);
|
||||
if (localExtensionsToInstall.length) {
|
||||
this.progressService.withProgress(
|
||||
{
|
||||
location: ProgressLocation.Notification,
|
||||
title: localize('installing extensions', "Installing Extensions...")
|
||||
},
|
||||
() => this.installLocalExtensions(localExtensionsToInstall));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async installLocalExtensions(localExtensionsToInstall: IExtension[]): Promise<void> {
|
||||
const galleryExtensions: IGalleryExtension[] = [];
|
||||
const vsixs: URI[] = [];
|
||||
await Promise.all(localExtensionsToInstall.map(async extension => {
|
||||
if (this.extensionGalleryService.isEnabled()) {
|
||||
const gallery = await this.extensionGalleryService.getCompatibleExtension(extension.identifier, extension.version);
|
||||
if (gallery) {
|
||||
galleryExtensions.push(gallery);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const vsix = await this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.zip(extension.local!);
|
||||
vsixs.push(vsix);
|
||||
}));
|
||||
|
||||
await Promise.all(galleryExtensions.map(gallery => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(gallery)));
|
||||
await Promise.all(vsixs.map(vsix => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.install(vsix)));
|
||||
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Info,
|
||||
message: localize('finished installing', "Completed installing the extensions. Please reload the window now."),
|
||||
actions: {
|
||||
primary: [new Action('realod', localize('reload', "Realod Window"), '', true,
|
||||
() => this.windowService.reloadWindow())]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForLanguage', function (accessor: ServicesAccessor, fileExtension: string) {
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@ import { append, $, addClass, toggleClass, Dimension } from 'vs/base/browser/dom
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState, AutoUpdateConfigurationKey, ShowRecommendationsOnlyOnDemandKey, CloseExtensionDetailsOnViewChangeKey, VIEW_CONTAINER } from '../common/extensions';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, AutoUpdateConfigurationKey, ShowRecommendationsOnlyOnDemandKey, CloseExtensionDetailsOnViewChangeKey, VIEW_CONTAINER } from '../common/extensions';
|
||||
import {
|
||||
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction,
|
||||
ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
|
||||
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction
|
||||
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, InstallLocalExtensionsOnRemoteAction
|
||||
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
@@ -32,7 +32,7 @@ import { OpenGlobalSettingsAction } from 'vs/workbench/contrib/preferences/brows
|
||||
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewsRegistry, IViewDescriptor, Extensions } from 'vs/workbench/common/views';
|
||||
@@ -55,7 +55,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { RemoteAuthorityContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { RemoteNameContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { MementoObject } from 'vs/workbench/common/memento';
|
||||
|
||||
@@ -146,7 +146,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
|
||||
id,
|
||||
name: viewIdNameMappings[id],
|
||||
ctorDescriptor: { ctor: EnabledExtensionsView },
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')),
|
||||
weight: 40,
|
||||
canToggleVisibility: true,
|
||||
order: 1
|
||||
@@ -161,7 +161,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
|
||||
id,
|
||||
name: viewIdNameMappings[id],
|
||||
ctorDescriptor: { ctor: DisabledExtensionsView },
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')),
|
||||
weight: 10,
|
||||
canToggleVisibility: true,
|
||||
order: 3,
|
||||
@@ -212,7 +212,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
|
||||
id: `extensions.${server.authority}.default`,
|
||||
get name() { return getInstalledViewName(); },
|
||||
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server, EventOf.map<void, string>(onDidChangeServerLabel, () => getInstalledViewName())] },
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.notEqualsTo('')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.notEqualsTo('')),
|
||||
weight: 40,
|
||||
order: 1
|
||||
}];
|
||||
@@ -352,6 +352,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -483,6 +484,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
|
||||
this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL),
|
||||
...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]),
|
||||
this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL),
|
||||
...(this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer ? [this.instantiationService.createInstance(InstallLocalExtensionsOnRemoteAction)] : []),
|
||||
new Separator(),
|
||||
this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL),
|
||||
this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL)
|
||||
@@ -635,11 +637,6 @@ export class StatusUpdater extends Disposable implements IWorkbenchContribution
|
||||
private onServiceChange(): void {
|
||||
this.badgeHandle.clear();
|
||||
|
||||
if (this.extensionsWorkbenchService.local.some(e => e.state === ExtensionState.Installing)) {
|
||||
this.badgeHandle.value = this.activityService.showActivity(VIEWLET_ID, new ProgressBadge(() => localize('extensions', "Extensions")), 'extensions-badge progress-badge');
|
||||
return;
|
||||
}
|
||||
|
||||
const outdated = this.extensionsWorkbenchService.outdated.reduce((r, e) => r + (this.extensionEnablementService.isEnabled(e.local!) ? 1 : 0), 0);
|
||||
if (outdated > 0) {
|
||||
const badge = new NumberBadge(outdated, n => localize('outdatedExtensions', '{0} Outdated Extensions', n));
|
||||
|
||||
@@ -555,6 +555,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
this.resetIgnoreAutoUpdateExtensions();
|
||||
this.eventuallySyncWithGallery(true);
|
||||
});
|
||||
|
||||
this._register(this.onChange(() => this.updateActivity()));
|
||||
}
|
||||
|
||||
get local(): IExtension[] {
|
||||
@@ -1082,6 +1084,21 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
return this._extensionAllowedBadgeProviders;
|
||||
}
|
||||
|
||||
private _activityCallBack: (() => void) | null = null;
|
||||
private updateActivity(): void {
|
||||
if ((this.localExtensions && this.localExtensions.local.some(e => e.state === ExtensionState.Installing || e.state === ExtensionState.Uninstalling))
|
||||
|| (this.remoteExtensions && this.remoteExtensions.local.some(e => e.state === ExtensionState.Installing || e.state === ExtensionState.Uninstalling))) {
|
||||
if (!this._activityCallBack) {
|
||||
this.progressService.withProgress({ location: ProgressLocation.Extensions }, () => new Promise(c => this._activityCallBack = c));
|
||||
}
|
||||
} else {
|
||||
if (this._activityCallBack) {
|
||||
this._activityCallBack();
|
||||
}
|
||||
this._activityCallBack = null;
|
||||
}
|
||||
}
|
||||
|
||||
private onError(err: any): void {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { InstallLocalExtensionsOnRemoteAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||
|
||||
export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IInstantiationService instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
const installLocalExtensionsOnRemoteAction = instantiationService.createInstance(InstallLocalExtensionsOnRemoteAction);
|
||||
CommandsRegistry.registerCommand('workbench.extensions.installLocalExtensions', () => installLocalExtensionsOnRemoteAction.run());
|
||||
let disposable = Disposable.None;
|
||||
const appendMenuItem = () => {
|
||||
disposable.dispose();
|
||||
disposable = MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: 'workbench.extensions.installLocalExtensions',
|
||||
category: localize('extensions', "Extensions"),
|
||||
title: installLocalExtensionsOnRemoteAction.label
|
||||
}
|
||||
});
|
||||
};
|
||||
appendMenuItem();
|
||||
this._register(labelService.onDidChangeFormatters(e => appendMenuItem()));
|
||||
this._register(toDisposable(() => disposable.dispose()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { join, basename } from 'vs/base/common/path';
|
||||
import { forEach } from 'vs/base/common/collections';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { match } from 'vs/base/common/glob';
|
||||
@@ -44,6 +44,8 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { extname } from 'vs/base/common/resources';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IExeBasedExtensionTip } from 'vs/platform/product/common/product';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
const milliSecondsInADay = 1000 * 60 * 60 * 24;
|
||||
const choiceNever = localize('neverShowAgain', "Don't Show Again");
|
||||
@@ -72,9 +74,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
_serviceBrand: any;
|
||||
|
||||
private _fileBasedRecommendations: { [id: string]: { recommendedTime: number, sources: ExtensionRecommendationSource[] }; } = Object.create(null);
|
||||
// {{SQL CARBON EDIT}}
|
||||
private _recommendations: string[] = [];
|
||||
private _exeBasedRecommendations: { [id: string]: string; } = Object.create(null);
|
||||
private _recommendations: string[] = []; // {{SQL CARBON EDIT}}
|
||||
private _exeBasedRecommendations: { [id: string]: IExeBasedExtensionTip; } = Object.create(null);
|
||||
private _importantExeBasedRecommendations: { [id: string]: IExeBasedExtensionTip; } = Object.create(null);
|
||||
private _availableRecommendations: { [pattern: string]: string[] } = Object.create(null);
|
||||
private _allWorkspaceRecommendedExtensions: IExtensionRecommendation[] = [];
|
||||
private _dynamicWorkspaceRecommendations: string[] = [];
|
||||
@@ -188,7 +190,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
|
||||
forEach(this._exeBasedRecommendations, entry => output[entry.key.toLowerCase()] = {
|
||||
reasonId: ExtensionRecommendationReason.Executable,
|
||||
reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", entry.value)
|
||||
reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", entry.value.friendlyName)
|
||||
});
|
||||
|
||||
forEach(this._fileBasedRecommendations, entry => output[entry.key.toLowerCase()] = {
|
||||
@@ -504,6 +506,100 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region important exe based extension
|
||||
|
||||
private async promptForImportantExeBasedExtension(): Promise<boolean> {
|
||||
|
||||
const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore';
|
||||
const config = this.configurationService.getValue<IExtensionsConfiguration>(ConfigurationKey);
|
||||
|
||||
if (config.ignoreRecommendations
|
||||
|| config.showRecommendationsOnlyOnDemand
|
||||
|| this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const installed = await this.extensionManagementService.getInstalled(ExtensionType.User);
|
||||
let recommendationsToSuggest = Object.keys(this._importantExeBasedRecommendations);
|
||||
recommendationsToSuggest = this.filterAllIgnoredInstalledAndNotAllowed(recommendationsToSuggest, installed);
|
||||
if (recommendationsToSuggest.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const id = recommendationsToSuggest[0];
|
||||
const tip = this._importantExeBasedRecommendations[id];
|
||||
const message = localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.friendlyName!, tip.exeFriendlyName || basename(tip.windowsPath!));
|
||||
|
||||
this.notificationService.prompt(Severity.Info, message,
|
||||
[{
|
||||
label: localize('install', 'Install'),
|
||||
run: () => {
|
||||
/* __GDPR__
|
||||
"exeExtensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId: name });
|
||||
this.instantiationService.createInstance(InstallRecommendedExtensionAction, id).run();
|
||||
}
|
||||
}, {
|
||||
label: localize('showRecommendations', "Show Recommendations"),
|
||||
run: () => {
|
||||
/* __GDPR__
|
||||
"exeExtensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId: name });
|
||||
|
||||
const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations"));
|
||||
recommendationsAction.run();
|
||||
recommendationsAction.dispose();
|
||||
}
|
||||
}, {
|
||||
label: choiceNever,
|
||||
isSecondary: true,
|
||||
run: () => {
|
||||
this.addToImportantRecommendationsIgnore(id);
|
||||
/* __GDPR__
|
||||
"exeExtensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name });
|
||||
this.notificationService.prompt(
|
||||
Severity.Info,
|
||||
localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"),
|
||||
[{
|
||||
label: localize('ignoreAll', "Yes, Ignore All"),
|
||||
run: () => this.setIgnoreRecommendationsConfig(true)
|
||||
}, {
|
||||
label: localize('no', "No"),
|
||||
run: () => this.setIgnoreRecommendationsConfig(false)
|
||||
}]
|
||||
);
|
||||
}
|
||||
}],
|
||||
{
|
||||
sticky: true,
|
||||
onCancel: () => {
|
||||
/* __GDPR__
|
||||
"exeExtensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//#region fileBasedRecommendations
|
||||
|
||||
getFileBasedRecommendations(): IExtensionRecommendation[] {
|
||||
@@ -607,7 +703,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
const now = Date.now();
|
||||
forEach(this._availableRecommendations, entry => {
|
||||
let { key: pattern, value: ids } = entry;
|
||||
if (match(pattern, model.uri.path)) {
|
||||
if (match(pattern, model.uri.toString())) {
|
||||
for (let id of ids) {
|
||||
if (caseInsensitiveGet(product.extensionImportantTips, id)) {
|
||||
recommendationsToSuggest.push(id);
|
||||
@@ -656,21 +752,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
}
|
||||
|
||||
private async promptRecommendedExtensionForFileType(recommendationsToSuggest: string[], installed: ILocalExtension[]): Promise<boolean> {
|
||||
const importantRecommendationsIgnoreList = <string[]>JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]'));
|
||||
const installedExtensionsIds = installed.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set<string>());
|
||||
recommendationsToSuggest = recommendationsToSuggest.filter(id => {
|
||||
if (importantRecommendationsIgnoreList.indexOf(id) !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (!this.isExtensionAllowedToBeRecommended(id)) {
|
||||
return false;
|
||||
}
|
||||
if (installedExtensionsIds.has(id.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
recommendationsToSuggest = this.filterAllIgnoredInstalledAndNotAllowed(recommendationsToSuggest, installed);
|
||||
if (recommendationsToSuggest.length === 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -680,22 +763,12 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
const name = entry['name'];
|
||||
|
||||
const name = entry.name;
|
||||
let message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", name);
|
||||
// Temporary fix for the only extension pack we recommend. See https://github.com/Microsoft/vscode/issues/35364
|
||||
if (id === 'vscjava.vscode-java-pack') {
|
||||
if (entry.isExtensionPack) {
|
||||
message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", name);
|
||||
}
|
||||
|
||||
const setIgnoreRecommendationsConfig = (configVal: boolean) => {
|
||||
this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER);
|
||||
if (configVal) {
|
||||
const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore';
|
||||
this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE);
|
||||
}
|
||||
};
|
||||
|
||||
this.notificationService.prompt(Severity.Info, message,
|
||||
[{
|
||||
label: localize('install', 'Install'),
|
||||
@@ -728,12 +801,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
label: choiceNever,
|
||||
isSecondary: true,
|
||||
run: () => {
|
||||
importantRecommendationsIgnoreList.push(id);
|
||||
this.storageService.store(
|
||||
'extensionsAssistant/importantRecommendationsIgnore',
|
||||
JSON.stringify(importantRecommendationsIgnoreList),
|
||||
StorageScope.GLOBAL
|
||||
);
|
||||
this.addToImportantRecommendationsIgnore(id);
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
@@ -746,10 +814,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"),
|
||||
[{
|
||||
label: localize('ignoreAll', "Yes, Ignore All"),
|
||||
run: () => setIgnoreRecommendationsConfig(true)
|
||||
run: () => this.setIgnoreRecommendationsConfig(true)
|
||||
}, {
|
||||
label: localize('no', "No"),
|
||||
run: () => setIgnoreRecommendationsConfig(false)
|
||||
run: () => this.setIgnoreRecommendationsConfig(false)
|
||||
}]
|
||||
);
|
||||
}
|
||||
@@ -841,6 +909,42 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
);
|
||||
}
|
||||
|
||||
private filterAllIgnoredInstalledAndNotAllowed(recommendationsToSuggest: string[], installed: ILocalExtension[]): string[] {
|
||||
|
||||
const importantRecommendationsIgnoreList = <string[]>JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]'));
|
||||
const installedExtensionsIds = installed.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set<string>());
|
||||
return recommendationsToSuggest.filter(id => {
|
||||
if (importantRecommendationsIgnoreList.indexOf(id) !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (!this.isExtensionAllowedToBeRecommended(id)) {
|
||||
return false;
|
||||
}
|
||||
if (installedExtensionsIds.has(id.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private addToImportantRecommendationsIgnore(id: string) {
|
||||
const importantRecommendationsIgnoreList = <string[]>JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]'));
|
||||
importantRecommendationsIgnoreList.push(id);
|
||||
this.storageService.store(
|
||||
'extensionsAssistant/importantRecommendationsIgnore',
|
||||
JSON.stringify(importantRecommendationsIgnoreList),
|
||||
StorageScope.GLOBAL
|
||||
);
|
||||
}
|
||||
|
||||
private setIgnoreRecommendationsConfig(configVal: boolean) {
|
||||
this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER);
|
||||
if (configVal) {
|
||||
const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore';
|
||||
this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region otherRecommendations
|
||||
@@ -863,18 +967,18 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
}
|
||||
|
||||
private fetchProactiveRecommendations(calledDuringStartup?: boolean): Promise<void> {
|
||||
let fetchPromise = Promise.resolve(undefined);
|
||||
let fetchPromise = Promise.resolve<any>(undefined);
|
||||
if (!this.proactiveRecommendationsFetched) {
|
||||
this.proactiveRecommendationsFetched = true;
|
||||
|
||||
// Executable based recommendations carry out a lot of file stats, so run them after 10 secs
|
||||
// So that the startup is not affected
|
||||
// Executable based recommendations carry out a lot of file stats, delay the resolution so that the startup is not affected
|
||||
// 10 sec for regular extensions
|
||||
// 3 secs for important
|
||||
|
||||
fetchPromise = new Promise((c, e) => {
|
||||
setTimeout(() => {
|
||||
Promise.all([this.fetchExecutableRecommendations(), this.fetchDynamicWorkspaceRecommendations()]).then(() => c(undefined));
|
||||
}, calledDuringStartup ? 10000 : 0);
|
||||
});
|
||||
const importantExeBasedRecommendations = timeout(calledDuringStartup ? 3000 : 0).then(_ => this.fetchExecutableRecommendations(true));
|
||||
importantExeBasedRecommendations.then(_ => this.promptForImportantExeBasedExtension());
|
||||
|
||||
fetchPromise = timeout(calledDuringStartup ? 10000 : 0).then(_ => Promise.all([this.fetchDynamicWorkspaceRecommendations(), this.fetchExecutableRecommendations(false), importantExeBasedRecommendations]));
|
||||
|
||||
}
|
||||
return fetchPromise;
|
||||
@@ -883,20 +987,22 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
/**
|
||||
* If user has any of the tools listed in product.exeBasedExtensionTips, fetch corresponding recommendations
|
||||
*/
|
||||
private fetchExecutableRecommendations(): Promise<void> {
|
||||
private fetchExecutableRecommendations(important: boolean): Promise<void> {
|
||||
const homeDir = os.homedir();
|
||||
let foundExecutables: Set<string> = new Set<string>();
|
||||
|
||||
let findExecutable = (exeName: string, path: string) => {
|
||||
let findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => {
|
||||
return pfs.fileExists(path).then(exists => {
|
||||
if (exists && !foundExecutables.has(exeName)) {
|
||||
foundExecutables.add(exeName);
|
||||
(product.exeBasedExtensionTips[exeName]['recommendations'] || [])
|
||||
.forEach(extensionId => {
|
||||
if (product.exeBasedExtensionTips[exeName]['friendlyName']) {
|
||||
this._exeBasedRecommendations[extensionId.toLowerCase()] = product.exeBasedExtensionTips[exeName]['friendlyName'];
|
||||
(tip['recommendations'] || []).forEach(extensionId => {
|
||||
if (tip.friendlyName) {
|
||||
if (important) {
|
||||
this._importantExeBasedRecommendations[extensionId.toLowerCase()] = tip;
|
||||
}
|
||||
});
|
||||
this._exeBasedRecommendations[extensionId.toLowerCase()] = tip;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -907,8 +1013,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
let exeName = entry.key;
|
||||
if (important !== !!entry.value.important) {
|
||||
return;
|
||||
}
|
||||
const exeName = entry.key;
|
||||
if (process.platform === 'win32') {
|
||||
let windowsPath = entry.value['windowsPath'];
|
||||
if (!windowsPath || typeof windowsPath !== 'string') {
|
||||
@@ -919,10 +1027,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
.replace('%ProgramFiles%', process.env['ProgramFiles']!)
|
||||
.replace('%APPDATA%', process.env['APPDATA']!)
|
||||
.replace('%WINDIR%', process.env['WINDIR']!);
|
||||
promises.push(findExecutable(exeName, windowsPath));
|
||||
promises.push(findExecutable(exeName, entry.value, windowsPath));
|
||||
} else {
|
||||
promises.push(findExecutable(exeName, join('/usr/local/bin', exeName)));
|
||||
promises.push(findExecutable(exeName, join(homeDir, exeName)));
|
||||
promises.push(findExecutable(exeName, entry.value, join('/usr/local/bin', exeName)));
|
||||
promises.push(findExecutable(exeName, entry.value, join(homeDir, exeName)));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -405,7 +405,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
|
||||
multipleSelectionSupport: false,
|
||||
setRowLineHeight: false,
|
||||
horizontalScrolling: false
|
||||
}) as WorkbenchList<IRuntimeExtension>;
|
||||
});
|
||||
|
||||
this._list.splice(0, this._list.length, this._elements || undefined);
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { ProgressService } from 'vs/workbench/services/progress/browser/progressService';
|
||||
|
||||
suite('ExtensionsActions Test', () => {
|
||||
|
||||
@@ -69,6 +71,7 @@ suite('ExtensionsActions Test', () => {
|
||||
|
||||
instantiationService.stub(IWorkspaceContextService, new TestContextService());
|
||||
instantiationService.stub(IConfigurationService, new TestConfigurationService());
|
||||
instantiationService.stub(IProgressService, ProgressService);
|
||||
|
||||
instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService);
|
||||
instantiationService.stub(ISharedProcessService, TestSharedProcessService);
|
||||
@@ -1160,15 +1163,29 @@ suite('ExtensionsActions Test', () => {
|
||||
const extensions = await instantiationService.get(IExtensionsWorkbenchService).queryLocal();
|
||||
testObject.extension = extensions[0];
|
||||
|
||||
return new Promise(c => {
|
||||
testObject.onDidChange(() => {
|
||||
if (testObject.enabled && testObject.tooltip === 'Please reload Azure Data Studio to complete the uninstallation of this extension.') { // {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
|
||||
c();
|
||||
}
|
||||
});
|
||||
uninstallEvent.fire(local.identifier);
|
||||
didUninstallEvent.fire({ identifier: local.identifier });
|
||||
uninstallEvent.fire(local.identifier);
|
||||
didUninstallEvent.fire({ identifier: local.identifier });
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal(testObject.tooltip, 'Please reload Azure Data Studio to complete the uninstallation of this extension.'); // {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
|
||||
});
|
||||
|
||||
test('Test ReloadAction when extension is uninstalled and can be removed', async () => {
|
||||
const local = aLocalExtension('a');
|
||||
instantiationService.stub(IExtensionService, <Partial<IExtensionService>>{
|
||||
getExtensions: () => Promise.resolve([ExtensionsActions.toExtensionDescription(local)]),
|
||||
onDidChangeExtensions: new Emitter<void>().event,
|
||||
canRemoveExtension: (extension) => true,
|
||||
canAddExtension: (extension) => true
|
||||
});
|
||||
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
|
||||
const extensions = await instantiationService.get(IExtensionsWorkbenchService).queryLocal();
|
||||
testObject.extension = extensions[0];
|
||||
|
||||
uninstallEvent.fire(local.identifier);
|
||||
didUninstallEvent.fire({ identifier: local.identifier });
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test ReloadAction when extension is uninstalled and installed', async () => {
|
||||
@@ -1505,9 +1522,11 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
});
|
||||
|
||||
test('Test remote install action when installing local workspace extension', async (done) => {
|
||||
test('Test remote install action when installing local workspace extension', async () => {
|
||||
// multi server setup
|
||||
const remoteExtensionManagementService: IExtensionManagementService = createExtensionManagementService();
|
||||
const onInstallExtension = new Emitter<InstallExtensionEvent>();
|
||||
remoteExtensionManagementService.onInstallExtension = onInstallExtension.event;
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]), remoteExtensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
@@ -1516,7 +1535,8 @@ suite('ExtensionsActions Test', () => {
|
||||
instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const gallery = aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier });
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
@@ -1527,19 +1547,17 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
|
||||
remoteExtensionManagementService.installFromGallery = () => new Promise<ILocalExtension>(c => c(aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) })));
|
||||
const disposable = testObject.onDidChange(() => {
|
||||
if (testObject.label === 'Installing' && testObject.enabled) {
|
||||
disposable.dispose();
|
||||
done();
|
||||
}
|
||||
});
|
||||
testObject.run();
|
||||
onInstallExtension.fire({ identifier: localWorkspaceExtension.identifier, gallery });
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Installing', testObject.label);
|
||||
assert.equal('extension-action install installing', testObject.class);
|
||||
});
|
||||
|
||||
test('Test remote install action when installing local workspace extension is finished', async (done) => {
|
||||
test('Test remote install action when installing local workspace extension is finished', async () => {
|
||||
// multi server setup
|
||||
const remoteExtensionManagementService: IExtensionManagementService = createExtensionManagementService();
|
||||
const onInstallExtension = new Emitter<InstallExtensionEvent>();
|
||||
remoteExtensionManagementService.onInstallExtension = onInstallExtension.event;
|
||||
const onDidInstallEvent = new Emitter<DidInstallExtensionEvent>();
|
||||
remoteExtensionManagementService.onDidInstallExtension = onDidInstallEvent.event;
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
@@ -1550,7 +1568,8 @@ suite('ExtensionsActions Test', () => {
|
||||
instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const gallery = aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier });
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
@@ -1561,19 +1580,14 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
|
||||
const installedExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
remoteExtensionManagementService.installFromGallery = () => new Promise<ILocalExtension>(c => c(installedExtension));
|
||||
await testObject.run();
|
||||
onInstallExtension.fire({ identifier: localWorkspaceExtension.identifier, gallery });
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
assert.equal('Installing', testObject.label);
|
||||
assert.equal('extension-action install installing', testObject.class);
|
||||
|
||||
const disposable = testObject.onDidChange(() => {
|
||||
if (testObject.label === 'Install on remote' && !testObject.enabled) {
|
||||
disposable.dispose();
|
||||
done();
|
||||
}
|
||||
});
|
||||
const installedExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
onDidInstallEvent.fire({ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install });
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is enabled for disabled local workspace extension', async () => {
|
||||
@@ -1717,7 +1731,7 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for local workspace extension if it cannot be installed', async () => {
|
||||
test('Test remote install action is enabled for local workspace extension if it has not gallery', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]));
|
||||
@@ -1730,6 +1744,25 @@ suite('ExtensionsActions Test', () => {
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for local workspace system extension', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceSystemExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`), type: ExtensionType.System });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceSystemExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceSystemExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
@@ -1842,9 +1875,11 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
});
|
||||
|
||||
test('Test local install action when installing remote ui extension', async (done) => {
|
||||
test('Test local install action when installing remote ui extension', async () => {
|
||||
// multi server setup
|
||||
const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService();
|
||||
const onInstallExtension = new Emitter<InstallExtensionEvent>();
|
||||
localExtensionManagementService.onInstallExtension = onInstallExtension.event;
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, localExtensionManagementService, createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
@@ -1853,7 +1888,8 @@ suite('ExtensionsActions Test', () => {
|
||||
instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const gallery = aGalleryExtension('a', { identifier: remoteUIExtension.identifier });
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
@@ -1864,19 +1900,17 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
|
||||
localExtensionManagementService.installFromGallery = () => new Promise<ILocalExtension>(c => c(aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) })));
|
||||
const disposable = testObject.onDidChange(() => {
|
||||
if (testObject.label === 'Installing' && testObject.enabled) {
|
||||
disposable.dispose();
|
||||
done();
|
||||
}
|
||||
});
|
||||
testObject.run();
|
||||
onInstallExtension.fire({ identifier: remoteUIExtension.identifier, gallery });
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Installing', testObject.label);
|
||||
assert.equal('extension-action install installing', testObject.class);
|
||||
});
|
||||
|
||||
test('Test local install action when installing remote ui extension is finished', async (done) => {
|
||||
test('Test local install action when installing remote ui extension is finished', async () => {
|
||||
// multi server setup
|
||||
const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService();
|
||||
const onInstallExtension = new Emitter<InstallExtensionEvent>();
|
||||
localExtensionManagementService.onInstallExtension = onInstallExtension.event;
|
||||
const onDidInstallEvent = new Emitter<DidInstallExtensionEvent>();
|
||||
localExtensionManagementService.onDidInstallExtension = onDidInstallEvent.event;
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
@@ -1887,7 +1921,8 @@ suite('ExtensionsActions Test', () => {
|
||||
instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const gallery = aGalleryExtension('a', { identifier: remoteUIExtension.identifier });
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
@@ -1898,19 +1933,14 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
|
||||
const installedExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) });
|
||||
localExtensionManagementService.installFromGallery = () => new Promise<ILocalExtension>(c => c(installedExtension));
|
||||
await testObject.run();
|
||||
onInstallExtension.fire({ identifier: remoteUIExtension.identifier, gallery });
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
assert.equal('Installing', testObject.label);
|
||||
assert.equal('extension-action install installing', testObject.class);
|
||||
|
||||
const disposable = testObject.onDidChange(() => {
|
||||
if (testObject.label === 'Install Locally' && !testObject.enabled) {
|
||||
disposable.dispose();
|
||||
done();
|
||||
}
|
||||
});
|
||||
const installedExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) });
|
||||
onDidInstallEvent.fire({ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install });
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is enabled for disabled remote ui extension', async () => {
|
||||
@@ -2057,7 +2087,7 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for remote UI extension if it cannot be installed', async () => {
|
||||
test('Test local install action is enabled for remote UI extension if it has gallery', async () => {
|
||||
// multi server setup
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension]));
|
||||
@@ -2070,6 +2100,25 @@ suite('ExtensionsActions Test', () => {
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for remote UI system extension', async () => {
|
||||
// multi server setup
|
||||
const remoteUISystemExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }), type: ExtensionType.System });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUISystemExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUISystemExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
|
||||
Reference in New Issue
Block a user