提交 4d8eb83c 编写于 作者: R Ramya Achutha Rao

Setting to disable eager recommendations fixes #43471

上级 7420a04c
......@@ -342,7 +342,7 @@ export interface IExtensionTipsService {
_serviceBrand: any;
getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; };
getFileBasedRecommendations(): string[];
getOtherRecommendations(): string[];
getOtherRecommendations(): TPromise<string[]>;
getWorkspaceRecommendations(): TPromise<string[]>;
getKeymapRecommendations(): string[];
getKeywordsForExtension(extension: string): string[];
......
......@@ -86,8 +86,10 @@ export interface IExtensionsWorkbenchService {
export const ConfigurationKey = 'extensions';
export const AutoUpdateConfigurationKey = 'extensions.autoUpdate';
export const DisableEagerRecommendationsKey = 'extensions.disableEagerRecommendations';
export interface IExtensionsConfiguration {
autoUpdate: boolean;
ignoreRecommendations: boolean;
disableEagerRecommendations: boolean;
}
......@@ -22,7 +22,7 @@ import Severity from 'vs/base/common/severity';
import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { Schemas } from 'vs/base/common/network';
import { IFileService } from 'vs/platform/files/common/files';
import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
import { IExtensionsConfiguration, ConfigurationKey, DisableEagerRecommendationsKey } from 'vs/workbench/parts/extensions/common/extensions';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as pfs from 'vs/base/node/pfs';
......@@ -63,6 +63,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
private _extensionsRecommendationsUrl: string;
private _disposables: IDisposable[] = [];
public promptWorkspaceRecommendationsPromise: TPromise<any>;
private proactiveRecommendationsFetched: boolean = false;
constructor(
@IExtensionGalleryService private _galleryService: IExtensionGalleryService,
......@@ -89,15 +90,39 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
if (product.extensionsGallery && product.extensionsGallery.recommendationsUrl) {
this._extensionsRecommendationsUrl = product.extensionsGallery.recommendationsUrl;
}
this.getDynamicWorkspaceRecommendations();
this._suggestFileBasedRecommendations();
this.getCachedDynamicWorkspaceRecommendations();
this._suggestFileBasedRecommendations();
this.promptWorkspaceRecommendationsPromise = this._suggestWorkspaceRecommendations();
// Executable based recommendations carry out a lot of file stats, so run them after 10 secs
// So that the startup is not affected
setTimeout(() => this._suggestBasedOnExecutables(this._exeBasedRecommendations), 10000);
if (!this.configurationService.getValue<boolean>(DisableEagerRecommendationsKey)) {
this.fetchProactiveRecommendations(true);
}
this._register(this.contextService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e)));
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (!this.proactiveRecommendationsFetched && !this.configurationService.getValue<boolean>(DisableEagerRecommendationsKey)) {
this.fetchProactiveRecommendations();
}
}));
}
private fetchProactiveRecommendations(calledDuringStartup?: boolean): TPromise<void> {
let fetchPromise = TPromise.as(null);
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
fetchPromise = new TPromise((c, e) => {
setTimeout(() => {
TPromise.join([this._suggestBasedOnExecutables(), this.getDynamicWorkspaceRecommendations()]).then(() => c(null));
}, calledDuringStartup ? 10000 : 0);
});
}
return fetchPromise;
}
private isEnabled(): boolean {
......@@ -107,6 +132,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } {
let output: { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } = Object.create(null);
if (!this.proactiveRecommendationsFetched) {
return output;
}
if (this.contextService.getWorkspace().folders && this.contextService.getWorkspace().folders.length === 1) {
const currentRepo = this.contextService.getWorkspace().folders[0].name;
this._dynamicWorkspaceRecommendations.forEach(x => output[x.toLowerCase()] = {
......@@ -238,10 +267,12 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return fileBased;
}
getOtherRecommendations(): string[] {
const others = distinct([...Object.keys(this._exeBasedRecommendations), ...this._dynamicWorkspaceRecommendations]);
shuffle(others);
return others;
getOtherRecommendations(): TPromise<string[]> {
return this.fetchProactiveRecommendations().then(() => {
const others = distinct([...Object.keys(this._exeBasedRecommendations), ...this._dynamicWorkspaceRecommendations]);
shuffle(others);
return others;
});
}
getKeymapRecommendations(): string[] {
......@@ -342,7 +373,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
);
const config = this.configurationService.getValue<IExtensionsConfiguration>(ConfigurationKey);
if (config.ignoreRecommendations) {
if (config.ignoreRecommendations || config.disableEagerRecommendations) {
return;
}
......@@ -521,7 +552,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
const config = this.configurationService.getValue<IExtensionsConfiguration>(ConfigurationKey);
return this.getWorkspaceRecommendations().then(allRecommendations => {
if (!allRecommendations.length || config.ignoreRecommendations || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) {
if (!allRecommendations.length || config.ignoreRecommendations || config.disableEagerRecommendations || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) {
return;
}
......@@ -609,7 +640,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
});
}
private _suggestBasedOnExecutables(recommendations: { [id: string]: string; }): void {
private _suggestBasedOnExecutables(): TPromise<any> {
const homeDir = os.homedir();
let foundExecutables: Set<string> = new Set<string>();
......@@ -620,13 +651,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
(product.exeBasedExtensionTips[exeName]['recommendations'] || [])
.forEach(x => {
if (product.exeBasedExtensionTips[exeName]['friendlyName']) {
recommendations[x] = product.exeBasedExtensionTips[exeName]['friendlyName'];
this._exeBasedRecommendations[x] = product.exeBasedExtensionTips[exeName]['friendlyName'];
}
});
}
});
};
let promises: TPromise<any>[] = [];
// Loop through recommended extensions
forEach(product.exeBasedExtensionTips, entry => {
if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) {
......@@ -643,12 +675,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
.replace('%ProgramFiles(x86)%', process.env['ProgramFiles(x86)'])
.replace('%ProgramFiles%', process.env['ProgramFiles'])
.replace('%APPDATA%', process.env['APPDATA']);
findExecutable(exeName, windowsPath);
promises.push(findExecutable(exeName, windowsPath));
} else {
findExecutable(exeName, paths.join('/usr/local/bin', exeName));
findExecutable(exeName, paths.join(homeDir, exeName));
promises.push(findExecutable(exeName, paths.join('/usr/local/bin', exeName)));
promises.push(findExecutable(exeName, paths.join(homeDir, exeName)));
}
});
return TPromise.join(promises);
}
private setIgnoreRecommendationsConfig(configVal: boolean) {
......@@ -659,9 +693,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
}
private getDynamicWorkspaceRecommendations(): TPromise<void> {
private getCachedDynamicWorkspaceRecommendations() {
if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER) {
return TPromise.as(null);
return;
}
const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations';
......@@ -684,13 +718,17 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
*/
this.telemetryService.publicLog('dynamicWorkspaceRecommendations', { count: this._dynamicWorkspaceRecommendations.length, cache: 1 });
return TPromise.as(null);
}
}
if (!this._extensionsRecommendationsUrl) {
private getDynamicWorkspaceRecommendations(): TPromise<void> {
if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER
|| this._dynamicWorkspaceRecommendations.length
|| !this._extensionsRecommendationsUrl) {
return TPromise.as(null);
}
const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations';
const workspaceUri = this.contextService.getWorkspace().folders[0].uri;
return TPromise.join([getHashedRemotesFromUri(workspaceUri, this.fileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => {
const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []);
......@@ -698,43 +736,37 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return null;
}
return new TPromise((c, e) => {
setTimeout(() => {
this.requestService.request({ type: 'GET', url: this._extensionsRecommendationsUrl }).then(context => {
if (context.res.statusCode !== 200) {
return c(null);
}
return asJson(context).then((result) => {
const allRecommendations: IDynamicWorkspaceRecommendations[] = Array.isArray(result['workspaceRecommendations']) ? result['workspaceRecommendations'] : [];
if (!allRecommendations.length) {
return c(null);
}
return this.requestService.request({ type: 'GET', url: this._extensionsRecommendationsUrl }).then(context => {
if (context.res.statusCode !== 200) {
return TPromise.as(null);
}
return asJson(context).then((result) => {
const allRecommendations: IDynamicWorkspaceRecommendations[] = Array.isArray(result['workspaceRecommendations']) ? result['workspaceRecommendations'] : [];
if (!allRecommendations.length) {
return;
}
let foundRemote = false;
for (let i = 0; i < hashedRemotes.length && !foundRemote; i++) {
for (let j = 0; j < allRecommendations.length && !foundRemote; j++) {
if (Array.isArray(allRecommendations[j].remoteSet) && allRecommendations[j].remoteSet.indexOf(hashedRemotes[i]) > -1) {
foundRemote = true;
this._dynamicWorkspaceRecommendations = allRecommendations[j].recommendations || [];
this.storageService.store(storageKey, JSON.stringify({
recommendations: this._dynamicWorkspaceRecommendations,
timestamp: Date.now()
}), StorageScope.WORKSPACE);
/* __GDPR__
"dynamicWorkspaceRecommendations" : {
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"cache" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('dynamicWorkspaceRecommendations', { count: this._dynamicWorkspaceRecommendations.length, cache: 0 });
let foundRemote = false;
for (let i = 0; i < hashedRemotes.length && !foundRemote; i++) {
for (let j = 0; j < allRecommendations.length && !foundRemote; j++) {
if (Array.isArray(allRecommendations[j].remoteSet) && allRecommendations[j].remoteSet.indexOf(hashedRemotes[i]) > -1) {
foundRemote = true;
this._dynamicWorkspaceRecommendations = allRecommendations[j].recommendations || [];
this.storageService.store(storageKey, JSON.stringify({
recommendations: this._dynamicWorkspaceRecommendations,
timestamp: Date.now()
}), StorageScope.WORKSPACE);
/* __GDPR__
"dynamicWorkspaceRecommendations" : {
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"cache" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
}
*/
this.telemetryService.publicLog('dynamicWorkspaceRecommendations', { count: this._dynamicWorkspaceRecommendations.length, cache: 0 });
}
return c(null);
});
});
}, 10000);
}
}
});
});
});
}
......
......@@ -208,6 +208,11 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
type: 'boolean',
description: localize('extensionsIgnoreRecommendations', "If set to true, the notifications for extension recommendations will stop showing up."),
default: false
},
'extensions.disableEagerRecommendations': {
type: 'boolean',
description: localize('extensionsDisableEagerRecommendations', "If set to true, no recommendation is fetched or shown unless specifically requested by the user."),
default: false
}
}
});
......
......@@ -25,7 +25,7 @@ import { append, $, addStandardDisposableListener, EventType, addClass, removeCl
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState, AutoUpdateConfigurationKey } from '../common/extensions';
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState, AutoUpdateConfigurationKey, DisableEagerRecommendationsKey } from '../common/extensions';
import {
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction,
ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
......@@ -64,6 +64,7 @@ const NonEmptyWorkspaceContext = new RawContextKey<boolean>('nonEmptyWorkspace',
const SearchExtensionsContext = new RawContextKey<boolean>('searchExtensions', false);
const SearchInstalledExtensionsContext = new RawContextKey<boolean>('searchInstalledExtensions', false);
const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false);
const DefaultRecommendedExtensionsContext = new RawContextKey<boolean>('defaultRecommendedExtensions', false);
export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtensionsViewlet {
......@@ -72,6 +73,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
private searchExtensionsContextKey: IContextKey<boolean>;
private searchInstalledExtensionsContextKey: IContextKey<boolean>;
private recommendedExtensionsContextKey: IContextKey<boolean>;
private defaultRecommendedExtensionsContextKey: IContextKey<boolean>;
private searchDelayer: ThrottledDelayer<any>;
private root: HTMLElement;
......@@ -107,7 +109,8 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
this.searchExtensionsContextKey = SearchExtensionsContext.bindTo(contextKeyService);
this.searchInstalledExtensionsContextKey = SearchInstalledExtensionsContext.bindTo(contextKeyService);
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
this.defaultRecommendedExtensionsContextKey = DefaultRecommendedExtensionsContext.bindTo(contextKeyService);
this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(DisableEagerRecommendationsKey));
this.disposables.push(this.viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables));
this.configurationService.onDidChangeConfiguration(e => {
......@@ -115,6 +118,9 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
this.secondaryActions = null;
this.updateTitleArea();
}
if (e.affectedKeys.indexOf(DisableEagerRecommendationsKey) > -1) {
this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(DisableEagerRecommendationsKey));
}
}, this, this.disposables);
}
......@@ -168,7 +174,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
name: localize('recommendedExtensions', "Recommended"),
location: ViewLocation.Extensions,
ctor: RecommendedExtensionsView,
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions')),
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')),
weight: 70,
canToggleVisibility: true
};
......
......@@ -278,10 +278,11 @@ export class ExtensionsListView extends ViewsViewletPanel {
.then(local => {
const installedExtensions = local.map(x => `${x.publisher}.${x.name}`);
let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations();
let others = this.tipsService.getOtherRecommendations();
const othersPromise = this.tipsService.getOtherRecommendations();
const workspacePromise = this.tipsService.getWorkspaceRecommendations();
return this.tipsService.getWorkspaceRecommendations()
.then(workspaceRecommendations => {
return TPromise.join([othersPromise, workspacePromise])
.then(([others, workspaceRecommendations]) => {
const names = this.getTrimmedRecommendations(installedExtensions, value, fileBasedRecommendations, others, workspaceRecommendations);
/* __GDPR__
......@@ -311,10 +312,11 @@ export class ExtensionsListView extends ViewsViewletPanel {
.then(local => {
const installedExtensions = local.map(x => `${x.publisher}.${x.name}`);
let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations();
let others = this.tipsService.getOtherRecommendations();
const othersPromise = this.tipsService.getOtherRecommendations();
const workspacePromise = this.tipsService.getWorkspaceRecommendations();
return this.tipsService.getWorkspaceRecommendations()
.then(workspaceRecommendations => {
return TPromise.join([othersPromise, workspacePromise])
.then(([others, workspaceRecommendations]) => {
workspaceRecommendations = workspaceRecommendations.map(x => x.toLowerCase());
fileBasedRecommendations = fileBasedRecommendations.filter(x => workspaceRecommendations.indexOf(x.toLowerCase()) === -1);
others = others.filter(x => workspaceRecommendations.indexOf(x.toLowerCase()) === -1);
......
......@@ -227,7 +227,7 @@ suite('ExtensionsTipsService Test', () => {
}
});
testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: false });
testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: false, disableEagerRecommendations: false });
instantiationService.stub(IStorageService, { get: (a, b, c) => c, getBoolean: (a, b, c) => c, store: () => { } });
instantiationService.stub(IModelService, <IModelService>{
getModels(): any { return []; },
......@@ -333,6 +333,17 @@ suite('ExtensionsTipsService Test', () => {
return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions);
});
test('ExtensionTipsService: No Prompt for valid workspace recommendations if disableEagerRecommendations is set', () => {
testConfigurationService.setUserConfiguration(ConfigurationKey, { disableEagerRecommendations: true });
return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => {
testObject = instantiationService.createInstance(ExtensionTipsService);
return testObject.promptWorkspaceRecommendationsPromise.then(() => {
assert.equal(Object.keys(testObject.getAllRecommendationsWithReason()).length, 0);
assert.ok(!prompted);
});
});
});
test('ExtensionTipsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set for current workspace', () => {
instantiationService.stub(IStorageService, { get: (a, b, c) => c, getBoolean: (a, b, c) => a === 'extensionsAssistant/workspaceRecommendationsIgnore' || c });
return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册