mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
#136301 - support whenNotInstalled for config based and exe recommendations
This commit is contained in:
parent
a64e8e5673
commit
a0a9e5dbd3
|
@ -165,14 +165,14 @@ export interface IConfigBasedExtensionTip {
|
||||||
configPath: string;
|
configPath: string;
|
||||||
configName: string;
|
configName: string;
|
||||||
configScheme?: string;
|
configScheme?: string;
|
||||||
recommendations: IStringDictionary<{ name: string; remotes?: string[]; important?: boolean; isExtensionPack?: boolean }>;
|
recommendations: IStringDictionary<{ name: string; remotes?: string[]; important?: boolean; isExtensionPack?: boolean; whenNotInstalled?: string[] }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExeBasedExtensionTip {
|
export interface IExeBasedExtensionTip {
|
||||||
friendlyName: string;
|
friendlyName: string;
|
||||||
windowsPath?: string;
|
windowsPath?: string;
|
||||||
important?: boolean;
|
important?: boolean;
|
||||||
recommendations: IStringDictionary<{ name: string; important?: boolean; isExtensionPack?: boolean }>;
|
recommendations: IStringDictionary<{ name: string; important?: boolean; isExtensionPack?: boolean; whenNotInstalled?: string[] }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRemoteExtensionTip {
|
export interface IRemoteExtensionTip {
|
||||||
|
|
|
@ -465,6 +465,7 @@ export type IConfigBasedExtensionTip = {
|
||||||
readonly isExtensionPack: boolean;
|
readonly isExtensionPack: boolean;
|
||||||
readonly configName: string;
|
readonly configName: string;
|
||||||
readonly important: boolean;
|
readonly important: boolean;
|
||||||
|
readonly whenNotInstalled?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IExecutableBasedExtensionTip = {
|
export type IExecutableBasedExtensionTip = {
|
||||||
|
@ -474,6 +475,7 @@ export type IExecutableBasedExtensionTip = {
|
||||||
readonly exeName: string;
|
readonly exeName: string;
|
||||||
readonly exeFriendlyName: string;
|
readonly exeFriendlyName: string;
|
||||||
readonly windowsPath?: string;
|
readonly windowsPath?: string;
|
||||||
|
readonly whenNotInstalled?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IWorkspaceTips = { readonly remoteSet: string[]; readonly recommendations: string[] };
|
export type IWorkspaceTips = { readonly remoteSet: string[]; readonly recommendations: string[] };
|
||||||
|
|
|
@ -68,7 +68,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||||
extensionName: value.name,
|
extensionName: value.name,
|
||||||
configName: tip.configName,
|
configName: tip.configName,
|
||||||
important: !!value.important,
|
important: !!value.important,
|
||||||
isExtensionPack: !!value.isExtensionPack
|
isExtensionPack: !!value.isExtensionPack,
|
||||||
|
whenNotInstalled: value.whenNotInstalled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -77,7 +78,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||||
extensionName: value.name,
|
extensionName: value.name,
|
||||||
configName: tip.configName,
|
configName: tip.configName,
|
||||||
important: !!value.important,
|
important: !!value.important,
|
||||||
isExtensionPack: !!value.isExtensionPack
|
isExtensionPack: !!value.isExtensionPack,
|
||||||
|
whenNotInstalled: value.whenNotInstalled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,8 +14,10 @@ import { URI } from 'vs/base/common/uri';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
import { IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
import { IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||||
|
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||||
import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService';
|
import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService';
|
||||||
import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
|
import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
|
||||||
|
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||||
import { IFileService } from 'vs/platform/files/common/files';
|
import { IFileService } from 'vs/platform/files/common/files';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||||
|
@ -32,7 +34,7 @@ type ExeExtensionRecommendationsClassification = {
|
||||||
type IExeBasedExtensionTips = {
|
type IExeBasedExtensionTips = {
|
||||||
readonly exeFriendlyName: string;
|
readonly exeFriendlyName: string;
|
||||||
readonly windowsPath?: string;
|
readonly windowsPath?: string;
|
||||||
readonly recommendations: { extensionId: string; extensionName: string; isExtensionPack: boolean }[];
|
readonly recommendations: { extensionId: string; extensionName: string; isExtensionPack: boolean; whenNotInstalled?: string[] }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const promptedExecutableTipsStorageKey = 'extensionTips/promptedExecutableTips';
|
const promptedExecutableTipsStorageKey = 'extensionTips/promptedExecutableTips';
|
||||||
|
@ -242,8 +244,11 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private promptExeRecommendations(tips: IExecutableBasedExtensionTip[]): Promise<RecommendationsNotificationResult> {
|
private async promptExeRecommendations(tips: IExecutableBasedExtensionTip[]): Promise<RecommendationsNotificationResult> {
|
||||||
const extensionIds = tips.map(({ extensionId }) => extensionId.toLowerCase());
|
const installed = await this.extensionManagementService.getInstalled(ExtensionType.User);
|
||||||
|
const extensionIds = tips
|
||||||
|
.filter(tip => !tip.whenNotInstalled || tip.whenNotInstalled.every(id => installed.every(local => !areSameExtensions(local.identifier, { id }))))
|
||||||
|
.map(({ extensionId }) => extensionId.toLowerCase());
|
||||||
const message = localize({ key: 'exeRecommended', comment: ['Placeholder string is the name of the software that is installed.'] }, "You have {0} installed on your system. Do you want to install the recommended extensions for it?", tips[0].exeFriendlyName);
|
const message = localize({ key: 'exeRecommended', comment: ['Placeholder string is the name of the software that is installed.'] }, "You have {0} installed on your system. Do you want to install the recommended extensions for it?", tips[0].exeFriendlyName);
|
||||||
return this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`, RecommendationSource.EXE);
|
return this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`, RecommendationSource.EXE);
|
||||||
}
|
}
|
||||||
|
@ -316,7 +321,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
|
||||||
checkedExecutables.set(exePath, exists);
|
checkedExecutables.set(exePath, exists);
|
||||||
}
|
}
|
||||||
if (exists) {
|
if (exists) {
|
||||||
for (const { extensionId, extensionName, isExtensionPack } of extensionTip.recommendations) {
|
for (const { extensionId, extensionName, isExtensionPack, whenNotInstalled } of extensionTip.recommendations) {
|
||||||
result.push({
|
result.push({
|
||||||
extensionId,
|
extensionId,
|
||||||
extensionName,
|
extensionName,
|
||||||
|
@ -324,6 +329,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
|
||||||
exeName,
|
exeName,
|
||||||
exeFriendlyName: extensionTip.exeFriendlyName,
|
exeFriendlyName: extensionTip.exeFriendlyName,
|
||||||
windowsPath: extensionTip.windowsPath,
|
windowsPath: extensionTip.windowsPath,
|
||||||
|
whenNotInstalled: whenNotInstalled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRe
|
||||||
import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
|
import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
|
||||||
import { Emitter } from 'vs/base/common/event';
|
import { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
|
export type ConfigBasedExtensionRecommendation = ExtensionRecommendation & { whenNotInstalled: string[] | undefined };
|
||||||
|
|
||||||
export class ConfigBasedRecommendations extends ExtensionRecommendations {
|
export class ConfigBasedRecommendations extends ExtensionRecommendations {
|
||||||
|
|
||||||
private importantTips: IConfigBasedExtensionTip[] = [];
|
private importantTips: IConfigBasedExtensionTip[] = [];
|
||||||
|
@ -18,13 +20,13 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations {
|
||||||
private _onDidChangeRecommendations = this._register(new Emitter<void>());
|
private _onDidChangeRecommendations = this._register(new Emitter<void>());
|
||||||
readonly onDidChangeRecommendations = this._onDidChangeRecommendations.event;
|
readonly onDidChangeRecommendations = this._onDidChangeRecommendations.event;
|
||||||
|
|
||||||
private _otherRecommendations: ExtensionRecommendation[] = [];
|
private _otherRecommendations: ConfigBasedExtensionRecommendation[] = [];
|
||||||
get otherRecommendations(): ReadonlyArray<ExtensionRecommendation> { return this._otherRecommendations; }
|
get otherRecommendations(): ReadonlyArray<ConfigBasedExtensionRecommendation> { return this._otherRecommendations; }
|
||||||
|
|
||||||
private _importantRecommendations: ExtensionRecommendation[] = [];
|
private _importantRecommendations: ConfigBasedExtensionRecommendation[] = [];
|
||||||
get importantRecommendations(): ReadonlyArray<ExtensionRecommendation> { return this._importantRecommendations; }
|
get importantRecommendations(): ReadonlyArray<ConfigBasedExtensionRecommendation> { return this._importantRecommendations; }
|
||||||
|
|
||||||
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return [...this.importantRecommendations, ...this.otherRecommendations]; }
|
get recommendations(): ReadonlyArray<ConfigBasedExtensionRecommendation> { return [...this.importantRecommendations, ...this.otherRecommendations]; }
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
|
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
|
||||||
|
@ -69,13 +71,14 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private toExtensionRecommendation(tip: IConfigBasedExtensionTip): ExtensionRecommendation {
|
private toExtensionRecommendation(tip: IConfigBasedExtensionTip): ConfigBasedExtensionRecommendation {
|
||||||
return {
|
return {
|
||||||
extensionId: tip.extensionId,
|
extensionId: tip.extensionId,
|
||||||
reason: {
|
reason: {
|
||||||
reasonId: ExtensionRecommendationReason.WorkspaceConfig,
|
reasonId: ExtensionRecommendationReason.WorkspaceConfig,
|
||||||
reasonText: localize('exeBasedRecommendation', "This extension is recommended because of the current workspace configuration")
|
reasonText: localize('exeBasedRecommendation', "This extension is recommended because of the current workspace configuration")
|
||||||
}
|
},
|
||||||
|
whenNotInstalled: tip.whenNotInstalled
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@ import { IExtensionRecommendationNotificationService } from 'vs/platform/extensi
|
||||||
import { timeout } from 'vs/base/common/async';
|
import { timeout } from 'vs/base/common/async';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { WebRecommendations } from 'vs/workbench/contrib/extensions/browser/webRecommendations';
|
import { WebRecommendations } from 'vs/workbench/contrib/extensions/browser/webRecommendations';
|
||||||
|
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||||
|
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||||
|
|
||||||
type IgnoreRecommendationClassification = {
|
type IgnoreRecommendationClassification = {
|
||||||
recommendationReason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true };
|
recommendationReason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true };
|
||||||
|
@ -61,6 +63,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
|
||||||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||||
@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,
|
@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,
|
||||||
@IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService,
|
@IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService,
|
||||||
|
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -260,7 +263,12 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
|
||||||
}
|
}
|
||||||
|
|
||||||
private async promptWorkspaceRecommendations(): Promise<void> {
|
private async promptWorkspaceRecommendations(): Promise<void> {
|
||||||
const allowedRecommendations = [...this.workspaceRecommendations.recommendations, ...this.configBasedRecommendations.importantRecommendations]
|
const installed = await this.extensionsWorkbenchService.queryLocal();
|
||||||
|
const allowedRecommendations = [
|
||||||
|
...this.workspaceRecommendations.recommendations,
|
||||||
|
...this.configBasedRecommendations.importantRecommendations.filter(
|
||||||
|
recommendation => !recommendation.whenNotInstalled || recommendation.whenNotInstalled.every(id => installed.every(local => !areSameExtensions(local.identifier, { id }))))
|
||||||
|
]
|
||||||
.map(({ extensionId }) => extensionId)
|
.map(({ extensionId }) => extensionId)
|
||||||
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId));
|
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue