Change:
Issue Type: <bBug</bOpen extension recommendations Click on one In the editor, click the ignore recommendation button Note that the extension is removed from the recommended extension list as expected Click on the undo button in the editor Note that the recommended extension is not placed back in the list. VS Code version: Code - Insiders 1.25.0-insider (, 2018-06-26T05:18:23.085Z) OS version: WindowsNT x64 10.0. <details<summarySystem Info</summary </details<details<summaryExtensions (15)</summaryExtensionVersion ------ html-snippets0.2.1 jshint0.10.19 vscode-eslint1.4.12 tslint1.0.33 vscode-great-icons2.1.35 vscode-gutter-preview0.12.2 vscode-azureappservice0.8.1 vscode-cosmosdb0.7.2 azure-account0.4.1 csharp1.15.2 vs-keybindings0.2.0 team1.136.0 debugger-for-chrome4.7.0 addDocComments0.0.8 cordova-tools1.4.0 </details<!-- generated by issue reporter --! Testing:
Note: this only applies to non-workspace recommendations. Will look into
Closed with

Discussion:
return undefined; } for (const _key in obj) { <del> if (obj.hasOwnProperty(_key) && _key.toLowerCase() === key.toLowerCase()) { </del> <ins> if (Object.hasOwnProperty.call(obj, _key) && _key.toLowerCase() === key.toLowerCase()) { </ins> return obj[_key]; } }

private readonly _onRecommendationChange: Emitter<RecommendationChangeNotification> = new Emitter<RecommendationChangeNotification>(); onRecommendationChange: Event<RecommendationChangeNotification> = this._onRecommendationChange.event; <del> private _sessionIgnoredRecommendations: { [id: string]: { reasonId: ExtensionRecommendationReason } } = {}; private _sessionRestoredRecommendations: { [id: string]: { reasonId: ExtensionRecommendationReason } } = {}; </del> <ins> private _sessionIgnoredRecommendations: { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string, sources: ExtensionRecommendationSource[] } } = {}; private _sessionRestoredRecommendations: { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string, sources: ExtensionRecommendationSource[] } } = {}; </ins> constructor( @IExtensionGalleryService private readonly _galleryService: IExtensionGalleryService,

Object.keys(this._sessionRestoredRecommendations).forEach(x => output[x.toLowerCase()] = { reasonId: this._sessionRestoredRecommendations[x].reasonId, <del> reasonText: localize('restoredRecommendation', "You will receive recommendations for this extension in your future VS Code sessions.") </del> <ins> reasonText: this._sessionRestoredRecommendations[x].reasonText </ins> }); return output;

} getFileBasedRecommendations(): IExtensionRecommendation[] { <del> return Object.keys(this._fileBasedRecommendations) .sort((a, b) => { if (this._fileBasedRecommendations[a].recommendedTime === this._fileBasedRecommendations[b].recommendedTime) { if (!product.extensionImportantTips || caseInsensitiveGet(product.extensionImportantTips, a)) { return -1; } if (caseInsensitiveGet(product.extensionImportantTips, b)) { return 1; } </del> <ins> return [...this.getRestoredRecommendationsByReason(ExtensionRecommendationReason.File), ...Object.keys(this._fileBasedRecommendations).sort((a, b) => { if (this._fileBasedRecommendations[a].recommendedTime === this._fileBasedRecommendations[b].recommendedTime) { if (!product.extensionImportantTips || caseInsensitiveGet(product.extensionImportantTips, a)) { return -1; </ins> } <del> return this._fileBasedRecommendations[a].recommendedTime > this._fileBasedRecommendations[b].recommendedTime ? -1 : 1; }).map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: this._fileBasedRecommendations[extensionId].sources })); </del> <ins> if (caseInsensitiveGet(product.extensionImportantTips, b)) { return 1; } } return this._fileBasedRecommendations[a].recommendedTime > this._fileBasedRecommendations[b].recommendedTime ? -1 : 1; })] .map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: this._fileBasedRecommendations[extensionId] ? this._fileBasedRecommendations[extensionId].sources : this._sessionRestoredRecommendations[extensionId.toLowerCase()] ? this._sessionRestoredRecommendations[extensionId.toLowerCase()].sources : [] })); </ins> } getOtherRecommendations(): TPromise<IExtensionRecommendation[]> {

const others = distinct([ ...Object.keys(this._exeBasedRecommendations), ...this._dynamicWorkspaceRecommendations, <del> ...Object.keys(this._experimentalRecommendations)]); </del> <ins> ...Object.keys(this._experimentalRecommendations), ]); </ins> shuffle(others); <del> return others.map(extensionId => { const sources: ExtensionRecommendationSource[] = []; if (this._exeBasedRecommendations[extensionId]) { sources.push('executable'); } if (this._dynamicWorkspaceRecommendations[extensionId]) { sources.push('dynamic'); } return (<IExtensionRecommendation>{ extensionId, sources }); }); </del> <ins> return [ ...this.getRestoredRecommendationsByReason(ExtensionRecommendationReason.Executable), ...this.getRestoredRecommendationsByReason(ExtensionRecommendationReason.DynamicWorkspace), ...this.getRestoredRecommendationsByReason(ExtensionRecommendationReason.Experimental), ...others] .map(extensionId => { const sources: ExtensionRecommendationSource[] = []; if (this._exeBasedRecommendations[extensionId]) { sources.push('executable'); } if (this._dynamicWorkspaceRecommendations[extensionId]) { sources.push('dynamic'); } if (this._sessionRestoredRecommendations[extensionId.toLowerCase()]) { sources.push(...this._sessionRestoredRecommendations[extensionId.toLowerCase()].sources); } return (<IExtensionRecommendation>{ extensionId, sources }); }); </ins> }); } <ins> private getRestoredRecommendationsByReason(reason: ExtensionRecommendationReason): string[] { return Object.keys(this._sessionRestoredRecommendations).filter(key => this._sessionRestoredRecommendations[key].reasonId === reason); } </ins> getKeymapRecommendations(): IExtensionRecommendation[] { return (product.keymapExtensionTips || []).map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] })); }

*/ this.telemetryService.publicLog('extensionsRecommendations:ignoreRecommendation', { id: extensionId, recommendationReason: reason.reasonId }); } <del> this._sessionIgnoredRecommendations[lowerId] = reason; </del> <ins> this._sessionIgnoredRecommendations[lowerId] = { ...reason, sources: coalesce([ <'executable' | null>(caseInsensitiveGet(this._exeBasedRecommendations, lowerId) ? 'executable' : null), ...(() => { let a = caseInsensitiveGet(this._fileBasedRecommendations, lowerId); return a ? a.sources : null; })(), <'dynamic' | null>(this._dynamicWorkspaceRecommendations.filter(x => x.toLowerCase() === lowerId).length > 0 ? 'dynamic' : null), ]) }; </ins> delete this._sessionRestoredRecommendations[lowerId]; this._globallyIgnoredRecommendations = distinct([...this._globallyIgnoredRecommendations, lowerId].map(id => id.toLowerCase())); } else {