diff --git a/src/vscode/impl/recommendationServiceImpl.ts b/src/vscode/impl/recommendationServiceImpl.ts index 4600069..90d25b3 100644 --- a/src/vscode/impl/recommendationServiceImpl.ts +++ b/src/vscode/impl/recommendationServiceImpl.ts @@ -8,7 +8,7 @@ import { Level, Recommendation, RecommendationModel, RecommendationsTelemetrySer import { IRecommendationService } from "../recommendationService"; import { IStorageService } from "../storageService"; import { StorageServiceImpl } from "./storageServiceImpl"; -import { getInstalledExtensionName, isExtensionInstalled, promptUserUtil, installExtensionUtil } from "./vscodeUtil"; +import { getInstalledExtensionName, isExtensionInstalled, promptUserUtil, installExtensionUtil, isUnwantedRecommendation } from "./vscodeUtil"; export const filterUnique = (value: any, index: number, self: any[]): boolean => self.indexOf(value) === index; @@ -114,7 +114,7 @@ export class RecommendationServiceImpl implements IRecommendationService { // Show a single recommendation immediately, if certain conditions are met // Specifically, if the recommender is installed, and the recommended is not installed, // and the recommended has not been timelocked in this session or ignored by user previously - if( this.ignoreRecommendations()) { + if( this.ignoreRecommendations() || await isUnwantedRecommendation(toExtension)) { return; } @@ -154,6 +154,7 @@ export class RecommendationServiceImpl implements IRecommendationService { const recommendedExtension: string[] = model.recommendations .map((x) => x.extensionId) .filter(filterUnique) + .filter((x) => !isUnwantedRecommendation(x)) .filter((x) => !isExtensionInstalled(x)); for( let i = 0; i < recommendedExtension.length; i++ ) { this.showStartupRecommendationsForSingleExtension(model, recommendedExtension[i]); diff --git a/src/vscode/impl/vscodeUtil.ts b/src/vscode/impl/vscodeUtil.ts index 824ea97..4ccf6c6 100644 --- a/src/vscode/impl/vscodeUtil.ts +++ b/src/vscode/impl/vscodeUtil.ts @@ -2,8 +2,11 @@ * Copyright (c) Red Hat, Inc. All rights reserved. * Licensed under the EPL v2.0 License. See LICENSE file in the project root for license information. *-----------------------------------------------------------------------------------------------*/ -import {commands, Disposable, extensions, window} from 'vscode'; +import {commands, Disposable, extensions, window, workspace} from 'vscode'; import { Level, UserChoice } from '../recommendationModel'; +import path from 'path'; +import { existsSync } from 'fs'; +import { readFile } from './util/fsUtil'; export const promptUserUtil = async (message: string, level: Level, hideNever: boolean): Promise => { const actions: Array = Object.keys(UserChoice).filter((x) => x !== UserChoice.Never || !hideNever); @@ -55,4 +58,41 @@ export const getInstalledExtensionName = (id: string): string | undefined => { }).finally(() => { installListenerDisposable.dispose(); }); + } + + export const getExtensionConfigurationFile = (): string | undefined => { + if (workspace.workspaceFolders !== undefined) { + if (workspace.workspaceFolders.length == 1) { + const file = path.resolve(workspace.workspaceFolders[0].uri.path, '.vscode', 'extensions.json'); + if (existsSync(file)) { + return file; + } + } else { + const file = workspace.workspaceFile?.path; + if (file !== undefined && existsSync(file)) { + return file; + } + } + } + return undefined; + } + + export const isUnwantedRecommendation = async (toExtension: string): Promise => { + const extensionConfigFile = getExtensionConfigurationFile(); + if (extensionConfigFile !== undefined) { + try { + const jsonData = await readFile(extensionConfigFile); + if (jsonData) { + let json = JSON.parse(jsonData); + if (workspace.workspaceFile?.path !== undefined) { + json = json['extensions']; + } + const unwantedRecommendations = json['unwantedRecommendations']; + return !!unwantedRecommendations && unwantedRecommendations.length > 0 && unwantedRecommendations.includes(toExtension); + } + } catch (err) { + // continue + } + } + return false; }