Merge from vscode cfbd1999769f4f08dce29629fb92fdc0fac53829

This commit is contained in:
ADS Merger
2020-08-06 07:08:52 +00:00
parent 9c67832880
commit 540046ba00
362 changed files with 7588 additions and 6584 deletions

View File

@@ -21,8 +21,13 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations {
private importantTips: IConfigBasedExtensionTip[] = [];
private otherTips: IConfigBasedExtensionTip[] = [];
private _recommendations: ExtensionRecommendation[] = [];
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return this._recommendations; }
private _otherRecommendations: ExtensionRecommendation[] = [];
get otherRecommendations(): ReadonlyArray<ExtensionRecommendation> { return this._otherRecommendations; }
private _importantRecommendations: ExtensionRecommendation[] = [];
get importantRecommendations(): ReadonlyArray<ExtensionRecommendation> { return this._importantRecommendations; }
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return [...this.importantRecommendations, ...this.otherRecommendations]; }
constructor(
isExtensionAllowedToBeRecommended: (extensionId: string) => boolean,
@@ -61,7 +66,8 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations {
}
this.importantTips = [...importantTips.values()];
this.otherTips = [...otherTips.values()].filter(tip => !importantTips.has(tip.extensionId));
this._recommendations = [...this.importantTips, ...this.otherTips].map(tip => this.toExtensionRecommendation(tip));
this._otherRecommendations = this.otherTips.map(tip => this.toExtensionRecommendation(tip));
this._importantRecommendations = this.importantTips.map(tip => this.toExtensionRecommendation(tip));
}
private async promptWorkspaceRecommendations(): Promise<void> {
@@ -88,7 +94,7 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations {
const tip = this.importantTips.filter(tip => tip.extensionId === extension)[0];
const message = tip.isExtensionPack ? localize('extensionPackRecommended', "The '{0}' extension pack is recommended for this workspace.", tip.extensionName)
: localize('extensionRecommended', "The '{0}' extension is recommended for this workspace.", tip.extensionName);
this.promptImportantExtensionInstallNotification(extension, message);
this.promptImportantExtensionsInstallNotification([extension], message);
}
}

View File

@@ -9,13 +9,14 @@ import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/
import { timeout } from 'vs/base/common/async';
import { localize } from 'vs/nls';
import { IStringDictionary } from 'vs/base/common/collections';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { basename } from 'vs/base/common/path';
import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService';
type ExeExtensionRecommendationsClassification = {
extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
@@ -24,13 +25,22 @@ type ExeExtensionRecommendationsClassification = {
export class ExeBasedRecommendations extends ExtensionRecommendations {
readonly _recommendations: ExtensionRecommendation[] = [];
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return this._recommendations; }
private readonly _otherRecommendations: ExtensionRecommendation[] = [];
get otherRecommendations(): ReadonlyArray<ExtensionRecommendation> { return this._otherRecommendations; }
private readonly _importantRecommendations: ExtensionRecommendation[] = [];
get importantRecommendations(): ReadonlyArray<ExtensionRecommendation> { return this._importantRecommendations; }
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return [...this.importantRecommendations, ...this.otherRecommendations]; }
private readonly tasExperimentService: ITASExperimentService | undefined;
constructor(
isExtensionAllowedToBeRecommended: (extensionId: string) => boolean,
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@optional(ITASExperimentService) tasExperimentService: ITASExperimentService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@INotificationService notificationService: INotificationService,
@@ -39,6 +49,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations {
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService);
this.tasExperimentService = tasExperimentService;
/*
3s has come out to be the good number to fetch and prompt important exe based recommendations
@@ -49,16 +60,30 @@ export class ExeBasedRecommendations extends ExtensionRecommendations {
protected async doActivate(): Promise<void> {
const otherExectuableBasedTips = await this.extensionTipsService.getOtherExecutableBasedTips();
otherExectuableBasedTips.forEach(tip => this._recommendations.push(this.toExtensionRecommendation(tip)));
otherExectuableBasedTips.forEach(tip => this._otherRecommendations.push(this.toExtensionRecommendation(tip)));
await this.fetchImportantExeBasedRecommendations();
}
private async fetchAndPromptImportantExeBasedRecommendations(): Promise<void> {
private _importantExeBasedRecommendations: Promise<IStringDictionary<IExecutableBasedExtensionTip>> | undefined;
private async fetchImportantExeBasedRecommendations(): Promise<IStringDictionary<IExecutableBasedExtensionTip>> {
if (!this._importantExeBasedRecommendations) {
this._importantExeBasedRecommendations = this.doFetchImportantExeBasedRecommendations();
}
return this._importantExeBasedRecommendations;
}
private async doFetchImportantExeBasedRecommendations(): Promise<IStringDictionary<IExecutableBasedExtensionTip>> {
const importantExeBasedRecommendations: IStringDictionary<IExecutableBasedExtensionTip> = {};
const importantExectuableBasedTips = await this.extensionTipsService.getImportantExecutableBasedTips();
importantExectuableBasedTips.forEach(tip => {
this._recommendations.push(this.toExtensionRecommendation(tip));
this._importantRecommendations.push(this.toExtensionRecommendation(tip));
importantExeBasedRecommendations[tip.extensionId.toLowerCase()] = tip;
});
return importantExeBasedRecommendations;
}
private async fetchAndPromptImportantExeBasedRecommendations(): Promise<void> {
const importantExeBasedRecommendations = await this.fetchImportantExeBasedRecommendations();
const local = await this.extensionManagementService.getInstalled();
const { installed, uninstalled } = this.groupByInstalled(Object.keys(importantExeBasedRecommendations), local);
@@ -76,7 +101,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations {
this.promptImportantExeBasedRecommendations(uninstalled, importantExeBasedRecommendations);
}
private promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: IStringDictionary<IExecutableBasedExtensionTip>): void {
private async promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: IStringDictionary<IExecutableBasedExtensionTip>): Promise<void> {
if (this.hasToIgnoreRecommendationNotifications()) {
return;
}
@@ -85,11 +110,39 @@ export class ExeBasedRecommendations extends ExtensionRecommendations {
return;
}
const recommendationsByExe = new Map<string, IExecutableBasedExtensionTip[]>();
for (const extensionId of recommendations) {
const tip = importantExeBasedRecommendations[extensionId];
const message = tip.isExtensionPack ? localize('extensionPackRecommended', "The '{0}' extension pack is recommended as you have {1} installed on your system.", tip.extensionName!, tip.exeFriendlyName || basename(tip.windowsPath!))
: localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.extensionName!, tip.exeFriendlyName || basename(tip.windowsPath!));
this.promptImportantExtensionInstallNotification(extensionId, message);
let tips = recommendationsByExe.get(tip.exeFriendlyName);
if (!tips) {
tips = [];
recommendationsByExe.set(tip.exeFriendlyName, tips);
}
tips.push(tip);
}
for (const [, tips] of recommendationsByExe) {
const extensionIds = tips.map(({ extensionId }) => extensionId.toLowerCase());
if (this.tasExperimentService && extensionIds.indexOf('ms-vscode-remote.remote-wsl') !== -1) {
await this.tasExperimentService.getTreatment<boolean>('wslpopupaa');
}
if (tips.length === 1) {
const tip = tips[0];
const message = tip.isExtensionPack ? localize('extensionPackRecommended', "The '{0}' extension pack is recommended as you have {1} installed on your system.", tip.extensionName, tip.exeFriendlyName || basename(tip.windowsPath!))
: localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.extensionName, tip.exeFriendlyName || basename(tip.windowsPath!));
this.promptImportantExtensionsInstallNotification(extensionIds, message);
}
else if (tips.length === 2) {
const message = localize('two extensions recommended', "The '{0}' and '{1}' extensions are recommended as you have {2} installed on your system.", tips[0].extensionName, tips[1].extensionName, tips[0].exeFriendlyName || basename(tips[0].windowsPath!));
this.promptImportantExtensionsInstallNotification(extensionIds, message);
}
else if (tips.length > 2) {
const message = localize('more than two extensions recommended', "The '{0}', '{1}' and other extensions are recommended as you have {2} installed on your system.", tips[0].extensionName, tips[1].extensionName, tips[0].exeFriendlyName || basename(tips[0].windowsPath!));
this.promptImportantExtensionsInstallNotification(extensionIds, message);
}
}
}
@@ -112,7 +165,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations {
source: 'executable',
reason: {
reasonId: ExtensionRecommendationReason.Executable,
reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", tip.extensionName)
reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", tip.exeFriendlyName || basename(tip.windowsPath!))
}
};
}

View File

@@ -8,7 +8,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { localize } from 'vs/nls';
import { InstallRecommendedExtensionAction, ShowRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { InstallRecommendedExtensionAction, ShowRecommendedExtensionAction, ShowRecommendedExtensionsAction, InstallRecommendedExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ExtensionRecommendationSource, IExtensionRecommendationReson } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
@@ -65,26 +65,40 @@ export abstract class ExtensionRecommendations extends Disposable {
}
}
protected promptImportantExtensionInstallNotification(extensionId: string, message: string): void {
protected promptImportantExtensionsInstallNotification(extensionIds: string[], message: string): void {
this.notificationService.prompt(Severity.Info, message,
[{
label: localize('install', 'Install'),
run: () => {
this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId });
this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionId));
label: extensionIds.length === 1 ? localize('install', 'Install') : localize('installAll', "Install All"),
run: async () => {
for (const extensionId of extensionIds) {
this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId });
}
if (extensionIds.length === 1) {
this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionIds[0]));
} else {
this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionsAction, InstallRecommendedExtensionsAction.ID, InstallRecommendedExtensionsAction.LABEL, extensionIds, 'install-recommendations'));
}
}
}, {
label: localize('moreInformation', "More Information"),
label: extensionIds.length === 1 ? localize('moreInformation', "More Information") : localize('showRecommendations', "Show Recommendations"),
run: () => {
this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId });
this.runAction(this.instantiationService.createInstance(ShowRecommendedExtensionAction, extensionId));
for (const extensionId of extensionIds) {
this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId });
}
if (extensionIds.length === 1) {
this.runAction(this.instantiationService.createInstance(ShowRecommendedExtensionAction, extensionIds[0]));
} else {
this.runAction(this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL));
}
}
}, {
label: choiceNever,
isSecondary: true,
run: () => {
this.addToImportantRecommendationsIgnore(extensionId);
this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId });
for (const extensionId of extensionIds) {
this.addToImportantRecommendationsIgnore(extensionId);
this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId });
}
this.notificationService.prompt(
Severity.Info,
localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"),
@@ -101,7 +115,9 @@ export abstract class ExtensionRecommendations extends Disposable {
{
sticky: true,
onCancel: () => {
this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId });
for (const extensionId of extensionIds) {
this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId });
}
}
}
);

View File

@@ -152,8 +152,8 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
await this.activateProactiveRecommendations();
const recommendations = [
...this.configBasedRecommendations.recommendations,
...this.exeBasedRecommendations.recommendations,
...this.configBasedRecommendations.otherRecommendations,
...this.exeBasedRecommendations.otherRecommendations,
...this.dynamicWorkspaceRecommendations.recommendations,
...this.experimentalRecommendations.recommendations,
...this.staticRecommendations.recommendations
@@ -170,6 +170,26 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
});
}
async getImportantRecommendations(): Promise<IExtensionRecommendation[]> {
await this.activateProactiveRecommendations();
const recommendations = [
...this.fileBasedRecommendations.importantRecommendations,
...this.configBasedRecommendations.importantRecommendations,
...this.exeBasedRecommendations.importantRecommendations,
];
const extensionIds = distinct(recommendations.map(e => e.extensionId))
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId));
shuffle(extensionIds, this.sessionSeed);
return extensionIds.map(extensionId => {
const sources: ExtensionRecommendationSource[] = distinct(recommendations.filter(r => r.extensionId === extensionId).map(r => r.source));
return (<IExtensionRecommendation>{ extensionId, sources });
});
}
getKeymapRecommendations(): IExtensionRecommendation[] {
return this.toExtensionRecommendations(this.keymapRecommendations.recommendations);
}

View File

@@ -1111,7 +1111,6 @@ export class CheckForUpdatesAction extends Action {
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
@IViewletService private readonly viewletService: IViewletService,
@IDialogService private readonly dialogService: IDialogService,
@INotificationService private readonly notificationService: INotificationService
) {
super(id, label, '', true);
@@ -1120,7 +1119,7 @@ export class CheckForUpdatesAction extends Action {
private checkUpdatesAndNotify(): void {
const outdated = this.extensionsWorkbenchService.outdated;
if (!outdated.length) {
this.dialogService.show(Severity.Info, localize('noUpdatesAvailable', "All extensions are up to date."), [localize('ok', "OK")]);
this.notificationService.info(localize('noUpdatesAvailable', "All extensions are up to date."));
return;
}
@@ -1851,19 +1850,20 @@ export class ShowRecommendedExtensionsAction extends Action {
}
}
export class InstallWorkspaceRecommendedExtensionsAction extends Action {
export class InstallRecommendedExtensionsAction extends Action {
static readonly ID = 'workbench.extensions.action.installWorkspaceRecommendedExtensions';
static readonly LABEL = localize('installWorkspaceRecommendedExtensions', "Install All Workspace Recommended Extensions");
static readonly ID = 'workbench.extensions.action.installRecommendedExtensions';
static readonly LABEL = localize('installRecommendedExtensions', "Install Recommended Extensions");
private _recommendations: string[] = [];
get recommendations(): string[] { return this._recommendations; }
set recommendations(recommendations: string[]) { this._recommendations = recommendations; this.enabled = this._recommendations.length > 0; }
constructor(
id: string = InstallWorkspaceRecommendedExtensionsAction.ID,
label: string = InstallWorkspaceRecommendedExtensionsAction.LABEL,
id: string,
label: string,
recommendations: string[],
private readonly source: string,
@IViewletService private readonly viewletService: IViewletService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,
@@ -1882,7 +1882,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action {
viewlet.search('@recommended ');
viewlet.focus();
const names = this.recommendations;
return this.extensionWorkbenchService.queryGallery({ names, source: 'install-all-workspace-recommendations' }, CancellationToken.None).then(pager => {
return this.extensionWorkbenchService.queryGallery({ names, source: this.source }, CancellationToken.None).then(pager => {
let installPromises: Promise<any>[] = [];
let model = new PagedModel(pager);
for (let i = 0; i < pager.total; i++) {
@@ -1914,6 +1914,22 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action {
}
}
export class InstallWorkspaceRecommendedExtensionsAction extends InstallRecommendedExtensionsAction {
constructor(
recommendations: string[],
@IViewletService viewletService: IViewletService,
@IInstantiationService instantiationService: IInstantiationService,
@IExtensionsWorkbenchService extensionWorkbenchService: IExtensionsWorkbenchService,
@IConfigurationService configurationService: IConfigurationService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
@IProductService productService: IProductService,
) {
super('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), recommendations, 'install-all-workspace-recommendations',
viewletService, instantiationService, extensionWorkbenchService, configurationService, extensionManagementServerService, productService);
}
}
export class ShowRecommendedExtensionAction extends Action {
static readonly ID = 'workbench.extensions.action.showRecommendedExtension';
@@ -2807,7 +2823,7 @@ export class SyncIgnoredIconAction extends ExtensionAction {
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
) {
super('extensions.syncignore', '', SyncIgnoredIconAction.DISABLE_CLASS, false);
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('sync.ignoredExtensions'))(() => this.update()));
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('settingsSync.ignoredExtensions'))(() => this.update()));
this.update();
this.tooltip = localize('syncingore.label', "This extension is ignored during sync.");
}

View File

@@ -271,7 +271,8 @@ export class ExtensionsTree extends WorkbenchAsyncDataTree<IExtensionData, IExte
overrideStyles,
accessibilityProvider: <IListAccessibilityProvider<IExtensionData>>{
getAriaLabel(extensionData: IExtensionData): string {
return localize('extension-arialabel', "{0}. Press enter for extension details.", extensionData.extension.displayName);
const extension = extensionData.extension;
return localize('extension-arialabel', "{0}, {1}, {2}, press enter for extension details.", extension.displayName, extension.version, extension.publisherDisplayName);
},
getWidgetAriaLabel(): string {
return localize('extensions', "Extensions");

View File

@@ -130,6 +130,9 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
servers.push(this.extensionManagementServerService.remoteExtensionManagementServer);
}
if (servers.length === 0 && this.extensionManagementServerService.webExtensionManagementServer) {
servers.push(this.extensionManagementServerService.webExtensionManagementServer);
}
const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => {
return servers.length > 1 ? `${server.label} - ${viewTitle}` : viewTitle;
};
@@ -337,6 +340,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
private root: HTMLElement | undefined;
private searchBox: SuggestEnabledInput | undefined;
private readonly searchViewletState: MementoObject;
private readonly sortActions: ChangeSortAction[];
constructor(
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@@ -384,6 +388,13 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this.updateTitleArea();
}
}, this));
this.sortActions = [
// this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Install Count"), this.onSearchChange, 'installs')), // {{SQL CARBON EDIT}}
// this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Rating"), this.onSearchChange, 'rating')), // {{SQL CARBON EDIT}}
this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Name"), this.onSearchChange, 'name')),
this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.publishedDate', localize('sort by date', "Published Date"), this.onSearchChange, 'publishedDate')),
];
}
create(parent: HTMLElement): void {
@@ -518,12 +529,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
]);
filterActions.push(...[
new Separator(),
new SubmenuAction('workbench.extensions.action.sortBy', localize('sorty by', "Sort By"), [
// this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Install Count"), this.onSearchChange, 'installs'), // {{SQL CARBON EDIT}}
// this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Rating"), this.onSearchChange, 'rating'), // {{SQL CARBON EDIT}}
this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Name"), this.onSearchChange, 'name'),
this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.publishedDate', localize('sort by date', "Published Date"), this.onSearchChange, 'publishedDate'),
]),
new SubmenuAction('workbench.extensions.action.sortBy', localize('sorty by', "Sort By"), this.sortActions),
]);
}

View File

@@ -139,7 +139,7 @@ export class ExtensionsListView extends ViewPane {
horizontalScrolling: false,
accessibilityProvider: <IListAccessibilityProvider<IExtension | null>>{
getAriaLabel(extension: IExtension | null): string {
return extension ? localize('extension-arialabel', "{0}. Press enter for extension details.", extension.displayName) : '';
return extension ? localize('extension-arialabel', "{0}, {1}, {2}, press enter for extension details.", extension.displayName, extension.version, extension.publisherDisplayName) : '';
},
getWidgetAriaLabel(): string {
return localize('extensions', "Extensions");
@@ -568,10 +568,11 @@ export class ExtensionsListView extends ViewPane {
const configBasedRecommendationsPromise = this.tipsService.getConfigBasedRecommendations();
const othersPromise = this.tipsService.getOtherRecommendations();
const workspacePromise = this.tipsService.getWorkspaceRecommendations();
const importantRecommendationsPromise = this.tipsService.getImportantRecommendations();
return Promise.all([othersPromise, workspacePromise, configBasedRecommendationsPromise])
.then(([others, workspaceRecommendations, configBasedRecommendations]) => {
const names = this.getTrimmedRecommendations(local, value, fileBasedRecommendations, configBasedRecommendations, others, workspaceRecommendations);
return Promise.all([othersPromise, workspacePromise, configBasedRecommendationsPromise, importantRecommendationsPromise])
.then(([others, workspaceRecommendations, configBasedRecommendations, importantRecommendations]) => {
const names = this.getTrimmedRecommendations(local, value, importantRecommendations, fileBasedRecommendations, configBasedRecommendations, others, workspaceRecommendations);
const recommendationsWithReason = this.tipsService.getAllRecommendationsWithReason();
/* __GDPR__
"extensionAllRecommendations:open" : {
@@ -624,14 +625,15 @@ export class ExtensionsListView extends ViewPane {
const configBasedRecommendationsPromise = this.tipsService.getConfigBasedRecommendations();
const othersPromise = this.tipsService.getOtherRecommendations();
const workspacePromise = this.tipsService.getWorkspaceRecommendations();
const importantRecommendationsPromise = this.tipsService.getImportantRecommendations();
return Promise.all([othersPromise, workspacePromise, configBasedRecommendationsPromise])
.then(([others, workspaceRecommendations, configBasedRecommendations]) => {
return Promise.all([othersPromise, workspacePromise, configBasedRecommendationsPromise, importantRecommendationsPromise])
.then(([others, workspaceRecommendations, configBasedRecommendations, importantRecommendations]) => {
configBasedRecommendations = configBasedRecommendations.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId));
fileBasedRecommendations = fileBasedRecommendations.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId));
others = others.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId));
const names = this.getTrimmedRecommendations(local, value, fileBasedRecommendations, configBasedRecommendations, others, []);
const names = this.getTrimmedRecommendations(local, value, importantRecommendations, fileBasedRecommendations, configBasedRecommendations, others, []);
const recommendationsWithReason = this.tipsService.getAllRecommendationsWithReason();
/* __GDPR__
@@ -725,22 +727,30 @@ export class ExtensionsListView extends ViewPane {
// {{SQL CARBON EDIT}} - End
// Given all recommendations, trims and returns recommendations in the relevant order after filtering out installed extensions
private getTrimmedRecommendations(installedExtensions: IExtension[], value: string, fileBasedRecommendations: IExtensionRecommendation[], configBasedRecommendations: IExtensionRecommendation[], otherRecommendations: IExtensionRecommendation[], workspaceRecommendations: IExtensionRecommendation[]): string[] {
private getTrimmedRecommendations(installedExtensions: IExtension[], value: string, importantRecommendations: IExtensionRecommendation[], fileBasedRecommendations: IExtensionRecommendation[], configBasedRecommendations: IExtensionRecommendation[], otherRecommendations: IExtensionRecommendation[], workspaceRecommendations: IExtensionRecommendation[]): string[] {
const totalCount = 10;
workspaceRecommendations = workspaceRecommendations
.filter(recommendation => {
return !this.isRecommendationInstalled(recommendation, installedExtensions)
&& recommendation.extensionId.toLowerCase().indexOf(value) > -1;
});
configBasedRecommendations = configBasedRecommendations
importantRecommendations = importantRecommendations
.filter(recommendation => {
return !this.isRecommendationInstalled(recommendation, installedExtensions)
&& workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId)
&& recommendation.extensionId.toLowerCase().indexOf(value) > -1;
});
configBasedRecommendations = configBasedRecommendations
.filter(recommendation => {
return !this.isRecommendationInstalled(recommendation, installedExtensions)
&& workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId)
&& importantRecommendations.every(importantRecommendation => importantRecommendation.extensionId !== recommendation.extensionId)
&& recommendation.extensionId.toLowerCase().indexOf(value) > -1;
});
fileBasedRecommendations = fileBasedRecommendations.filter(recommendation => {
return !this.isRecommendationInstalled(recommendation, installedExtensions)
&& workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId)
&& importantRecommendations.every(importantRecommendation => importantRecommendation.extensionId !== recommendation.extensionId)
&& configBasedRecommendations.every(configBasedRecommendation => configBasedRecommendation.extensionId !== recommendation.extensionId)
&& recommendation.extensionId.toLowerCase().indexOf(value) > -1;
});
@@ -748,13 +758,14 @@ export class ExtensionsListView extends ViewPane {
return !this.isRecommendationInstalled(recommendation, installedExtensions)
&& fileBasedRecommendations.every(fileBasedRecommendation => fileBasedRecommendation.extensionId !== recommendation.extensionId)
&& workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId)
&& importantRecommendations.every(importantRecommendation => importantRecommendation.extensionId !== recommendation.extensionId)
&& configBasedRecommendations.every(configBasedRecommendation => configBasedRecommendation.extensionId !== recommendation.extensionId)
&& recommendation.extensionId.toLowerCase().indexOf(value) > -1;
});
const otherCount = Math.min(2, otherRecommendations.length);
const fileBasedCount = Math.min(fileBasedRecommendations.length, totalCount - workspaceRecommendations.length - configBasedRecommendations.length - otherCount);
const recommendations = [...workspaceRecommendations, ...configBasedRecommendations];
const fileBasedCount = Math.min(fileBasedRecommendations.length, totalCount - workspaceRecommendations.length - importantRecommendations.length - configBasedRecommendations.length - otherCount);
const recommendations = [...workspaceRecommendations, ...importantRecommendations, ...configBasedRecommendations];
recommendations.push(...fileBasedRecommendations.splice(0, fileBasedCount));
recommendations.push(...otherRecommendations.splice(0, otherCount));
@@ -1115,7 +1126,7 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView {
getActions(): IAction[] {
if (!this.installAllAction) {
this.installAllAction = this._register(this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, InstallWorkspaceRecommendedExtensionsAction.LABEL, []));
this.installAllAction = this._register(this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, []));
this.installAllAction.class = 'codicon codicon-cloud-download';
}

View File

@@ -1030,7 +1030,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
const id = extension.identifier.id.toLowerCase();
// first remove the extension completely from ignored extensions
let currentValue = [...this.configurationService.getValue<string[]>('sync.ignoredExtensions')].map(id => id.toLowerCase());
let currentValue = [...this.configurationService.getValue<string[]>('settingsSync.ignoredExtensions')].map(id => id.toLowerCase());
currentValue = currentValue.filter(v => v !== id && v !== `-${id}`);
// If ignored, then add only if it is ignored by default
@@ -1043,7 +1043,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
currentValue.push(id);
}
return this.configurationService.updateValue('sync.ignoredExtensions', currentValue.length ? currentValue : undefined, ConfigurationTarget.USER);
return this.configurationService.updateValue('settingsSync.ignoredExtensions', currentValue.length ? currentValue : undefined, ConfigurationTarget.USER);
}
private installWithProgress<T>(installTask: () => Promise<T>, extensionName?: string): Promise<T> {

View File

@@ -75,6 +75,14 @@ export class FileBasedRecommendations extends ExtensionRecommendations {
return recommendations;
}
get importantRecommendations(): ReadonlyArray<ExtensionRecommendation> {
return this.recommendations.filter(e => this.importantExtensionTips[e.extensionId]);
}
get otherRecommendations(): ReadonlyArray<ExtensionRecommendation> {
return this.recommendations.filter(e => !this.importantExtensionTips[e.extensionId]);
}
constructor(
isExtensionAllowedToBeRecommended: (extensionId: string) => boolean,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@@ -226,7 +234,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations {
message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", extensionName);
}
this.promptImportantExtensionInstallNotification(extensionId, message);
this.promptImportantExtensionsInstallNotification([extensionId], message);
return true;
}

View File

@@ -18,7 +18,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { localize } from 'vs/nls';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { InstallWorkspaceRecommendedExtensionsAction, ShowRecommendedExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ShowRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
@@ -120,7 +120,7 @@ export class WorkspaceRecommendations extends ExtensionRecommendations {
label: localize('installAll', "Install All"),
run: () => {
this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' });
const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, localize('installAll', "Install All"), recommendations.map(({ extensionId }) => extensionId));
const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, recommendations.map(({ extensionId }) => extensionId));
installAllAction.run();
installAllAction.dispose();
c(undefined);

View File

@@ -157,7 +157,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
this._extensionService.getExtensions().then((extensions) => {
// We only deal with extensions with source code!
this._extensionsDescriptions = extensions.filter((extension) => {
return !!extension.main;
return Boolean(extension.main) || Boolean(extension.browser);
});
this._updateExtensions();
});

View File

@@ -14,7 +14,7 @@ import {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test';
@@ -127,6 +127,9 @@ suite('ExtensionsListView Tests', () => {
{ extensionId: configBasedRecommendationA.identifier.id }
]);
},
getImportantRecommendations(): Promise<IExtensionRecommendation[]> {
return Promise.resolve([]);
},
getFileBasedRecommendations() {
return [
{ extensionId: fileBasedRecommendationA.identifier.id },