Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add dismiss button #221508

Merged
merged 1 commit into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,14 @@
}

.settings-editor > .settings-body .settings-tree-container .setting-item-extension-toggle .setting-item-extension-toggle-button {
display: block;
display: inline-block;
width: fit-content;
}

.settings-editor > .settings-body .settings-tree-container .setting-item-extension-toggle .setting-item-extension-dismiss-button {
display: inline-block;
width: fit-content;
margin-left: 8px;
}

.settings-editor.no-results > .settings-body .settings-toc-container,
Expand Down
120 changes: 71 additions & 49 deletions src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ export class SettingsEditor2 extends EditorPane {
private dimension!: DOM.Dimension;

private installedExtensionIds: string[] = [];
private dismissedExtensionSettings: string[] = [];

private readonly DISMISSED_EXTENSION_SETTINGS_STORAGE_KEY = 'settingsEditor2.dismissedExtensionSettings';

private readonly inputChangeListener: MutableDisposable<IDisposable>;

Expand Down Expand Up @@ -265,6 +268,8 @@ export class SettingsEditor2 extends EditorPane {

this.editorMemento = this.getEditorMemento<ISettingsEditor2State>(editorGroupService, textResourceConfigurationService, SETTINGS_EDITOR_STATE_KEY);

this.dismissedExtensionSettings = this.storageService.get(this.DISMISSED_EXTENSION_SETTINGS_STORAGE_KEY, StorageScope.PROFILE, '').split('\t');

this._register(configurationService.onDidChangeConfiguration(e => {
if (e.source !== ConfigurationTarget.DEFAULT) {
this.onConfigUpdate(e.affectedKeys);
Expand All @@ -286,6 +291,13 @@ export class SettingsEditor2 extends EditorPane {
}
}));

this._register(extensionManagementService.onDidInstallExtensions(() => {
this.refreshInstalledExtensionsList();
}));
this._register(extensionManagementService.onDidUninstallExtension(() => {
this.refreshInstalledExtensionsList();
}));

this.modelDisposables = this._register(new DisposableStore());

if (ENABLE_LANGUAGE_FILTER && !SettingsEditor2.SUGGESTIONS.includes(`@${LANGUAGE_SETTING_TAG}`)) {
Expand Down Expand Up @@ -399,7 +411,7 @@ export class SettingsEditor2 extends EditorPane {
private async refreshInstalledExtensionsList(): Promise<void> {
const installedExtensions = await this.extensionManagementService.getInstalled();
this.installedExtensionIds = installedExtensions
.filter(ext => ext.manifest && ext.manifest.contributes && ext.manifest.contributes.configuration)
.filter(ext => ext.manifest.contributes?.configuration)
.map(ext => ext.identifier.id);
}

Expand Down Expand Up @@ -679,6 +691,16 @@ export class SettingsEditor2 extends EditorPane {
this.onConfigUpdate(undefined, true);
}

private onDidDismissExtensionSetting(extensionId: string): void {
if (this.dismissedExtensionSettings.includes(extensionId)) {
return;
}

this.dismissedExtensionSettings.push(extensionId);
this.storageService.store(this.DISMISSED_EXTENSION_SETTINGS_STORAGE_KEY, this.dismissedExtensionSettings.join('\t'), StorageScope.PROFILE, StorageTarget.USER);
this.onConfigUpdate(undefined, true);
}

private onDidClickSetting(evt: ISettingLinkClickEvent, recursed?: boolean): void {
const targetElement = this.currentSettingsModel.getElementsByName(evt.targetKey)?.[0];
let revealFailed = false;
Expand Down Expand Up @@ -916,6 +938,7 @@ export class SettingsEditor2 extends EditorPane {
private createSettingsTree(container: HTMLElement): void {
this.settingRenderers = this._register(this.instantiationService.createInstance(SettingTreeRenderers));
this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type, e.manualReset, e.scope)));
this._register(this.settingRenderers.onDidDismissExtensionSetting((e) => this.onDidDismissExtensionSetting(e)));
this._register(this.settingRenderers.onDidOpenSettings(settingKey => {
this.openSettingsFile({ revealSetting: { key: settingKey, edit: true } });
}));
Expand Down Expand Up @@ -1210,43 +1233,6 @@ export class SettingsEditor2 extends EditorPane {
});
}

private addOrRemoveManageExtensionSetting(setting: ISetting, extension: IGalleryExtension, groups: ISettingsGroup[]): ISettingsGroup | undefined {
const matchingGroups = groups.filter(g => {
const lowerCaseId = g.extensionInfo?.id.toLowerCase();
return (lowerCaseId === setting.stableExtensionId!.toLowerCase() ||
lowerCaseId === setting.prereleaseExtensionId!.toLowerCase());
});

const extensionId = setting.displayExtensionId!;
const extensionInstalled = this.installedExtensionIds.includes(extensionId);
if (!matchingGroups.length && !extensionInstalled) {
// Only show the recommendation when the extension hasn't been installed.
const newGroup: ISettingsGroup = {
sections: [{
settings: [setting],
}],
id: extensionId,
title: setting.extensionGroupTitle!,
titleRange: nullRange,
range: nullRange,
extensionInfo: {
id: extensionId,
displayName: extension?.displayName,
}
};
groups.push(newGroup);
return newGroup;
} else if (matchingGroups.length >= 2 || extensionInstalled) {
// Remove the group with the manage extension setting.
const matchingGroupIndex = matchingGroups.findIndex(group =>
group.sections.length === 1 && group.sections[0].settings.length === 1 && group.sections[0].settings[0].displayExtensionId);
if (matchingGroupIndex !== -1) {
groups.splice(matchingGroupIndex, 1);
}
}
return undefined;
}

private createSettingsOrderByTocIndex(resolvedSettingsRoot: ITOCEntry<ISetting>): Map<string, number> {
const index = new Map<string, number>();
function indexSettings(resolvedSettingsRoot: ITOCEntry<ISetting>, counter = 0): number {
Expand Down Expand Up @@ -1301,11 +1287,37 @@ export class SettingsEditor2 extends EditorPane {
}

const additionalGroups: ISettingsGroup[] = [];
let setAdditionalGroups = false;
const toggleData = await getExperimentalExtensionToggleData(this.extensionGalleryService, this.productService);
if (toggleData && groups.filter(g => g.extensionInfo).length) {
for (const key in toggleData.settingsEditorRecommendedExtensions) {
const recommendationInfo = toggleData.settingsEditorRecommendedExtensions[key];
const extension = toggleData.recommendedExtensionsGalleryInfo[key];
const extension: IGalleryExtension = toggleData.recommendedExtensionsGalleryInfo[key];
if (!extension) {
continue;
}

const extensionId = extension.identifier.id;
const extensionInstalled = this.installedExtensionIds.includes(extensionId);

// Drill down to see whether the group and setting already exist
// and need to be removed.
const matchingGroupIndex = groups.findIndex(g =>
g.extensionInfo && g.extensionInfo!.id.toLowerCase() === extensionId.toLowerCase() &&
g.sections.length === 1 && g.sections[0].settings.length === 1 && g.sections[0].settings[0].displayExtensionId
);
if (extensionInstalled || this.dismissedExtensionSettings.includes(extensionId)) {
if (matchingGroupIndex !== -1) {
groups.splice(matchingGroupIndex, 1);
setAdditionalGroups = true;
}
continue;
}

if (matchingGroupIndex !== -1) {
continue;
}

// Create the entry. extensionInstalled is false in this case.
let manifest: IExtensionManifest | null = null;
try {
manifest = await this.extensionGalleryService.getManifest(extension, CancellationToken.None);
Expand All @@ -1323,7 +1335,8 @@ export class SettingsEditor2 extends EditorPane {
groupTitle = contributesConfiguration[0].title;
}

const extensionName = extension?.displayName ?? extension?.name ?? extension.identifier.id;
const recommendationInfo = toggleData.settingsEditorRecommendedExtensions[key];
const extensionName = extension.displayName ?? extension.name ?? extensionId;
const settingKey = `${key}.manageExtension`;
const setting: ISetting = {
range: nullRange,
Expand All @@ -1336,17 +1349,26 @@ export class SettingsEditor2 extends EditorPane {
descriptionRanges: [],
scope: ConfigurationScope.WINDOW,
type: 'null',
displayExtensionId: extension.identifier.id,
prereleaseExtensionId: key,
stableExtensionId: key,
displayExtensionId: extensionId,
extensionGroupTitle: groupTitle ?? extensionName,
categoryLabel: 'Extensions',
title: extensionName
};
const additionalGroup = this.addOrRemoveManageExtensionSetting(setting, extension, groups);
if (additionalGroup) {
additionalGroups.push(additionalGroup);
}
const additionalGroup: ISettingsGroup = {
sections: [{
settings: [setting],
}],
id: extensionId,
title: setting.extensionGroupTitle!,
titleRange: nullRange,
range: nullRange,
extensionInfo: {
id: extensionId,
displayName: extension.displayName,
}
};
additionalGroups.push(additionalGroup);
setAdditionalGroups = true;
}
}

Expand All @@ -1356,7 +1378,7 @@ export class SettingsEditor2 extends EditorPane {
const commonlyUsed = resolveSettingsTree(commonlyUsedDataToUse, groups, this.logService);
resolvedSettingsRoot.children!.unshift(commonlyUsed.tree);

if (toggleData) {
if (toggleData && setAdditionalGroups) {
// Add the additional groups to the model to help with searching.
this.defaultSettingsEditorModel.setAdditionalGroups(additionalGroups);
}
Expand Down
40 changes: 31 additions & 9 deletions src/vs/workbench/contrib/preferences/browser/settingsTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ interface ISettingBoolItemTemplate extends ISettingItemTemplate<boolean> {

interface ISettingExtensionToggleItemTemplate extends ISettingItemTemplate<undefined> {
actionButton: Button;
dismissButton: Button;
}

interface ISettingTextItemTemplate extends ISettingItemTemplate<string> {
Expand Down Expand Up @@ -1055,7 +1056,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre
}
}

export class SettingGroupRenderer implements ITreeRenderer<SettingsTreeGroupElement, never, IGroupTitleTemplate> {
class SettingGroupRenderer implements ITreeRenderer<SettingsTreeGroupElement, never, IGroupTitleTemplate> {
templateId = SETTINGS_ELEMENT_TEMPLATE_ID;

renderTemplate(container: HTMLElement): IGroupTitleTemplate {
Expand Down Expand Up @@ -1622,15 +1623,15 @@ abstract class SettingIncludeExcludeRenderer extends AbstractSettingRenderer imp
}
}

export class SettingExcludeRenderer extends SettingIncludeExcludeRenderer {
class SettingExcludeRenderer extends SettingIncludeExcludeRenderer {
templateId = SETTINGS_EXCLUDE_TEMPLATE_ID;

protected override isExclude(): boolean {
return true;
}
}

export class SettingIncludeRenderer extends SettingIncludeExcludeRenderer {
class SettingIncludeRenderer extends SettingIncludeExcludeRenderer {
templateId = SETTINGS_INCLUDE_TEMPLATE_ID;

protected override isExclude(): boolean {
Expand Down Expand Up @@ -1745,7 +1746,7 @@ class SettingMultilineTextRenderer extends AbstractSettingTextRenderer implement
}
}

export class SettingEnumRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingEnumItemTemplate> {
class SettingEnumRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingEnumItemTemplate> {
templateId = SETTINGS_ENUM_TEMPLATE_ID;

renderTemplate(container: HTMLElement): ISettingEnumItemTemplate {
Expand Down Expand Up @@ -1862,7 +1863,7 @@ const settingsNumberInputBoxStyles = getInputBoxStyle({
inputBorder: settingsNumberInputBorder
});

export class SettingNumberRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingNumberItemTemplate> {
class SettingNumberRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingNumberItemTemplate> {
templateId = SETTINGS_NUMBER_TEMPLATE_ID;

renderTemplate(_container: HTMLElement): ISettingNumberItemTemplate {
Expand Down Expand Up @@ -1916,7 +1917,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT
}
}

export class SettingBoolRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingBoolItemTemplate> {
class SettingBoolRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingBoolItemTemplate> {
templateId = SETTINGS_BOOL_TEMPLATE_ID;

renderTemplate(_container: HTMLElement): ISettingBoolItemTemplate {
Expand Down Expand Up @@ -2011,9 +2012,12 @@ type ManageExtensionClickTelemetryClassification = {
comment: 'Event used to gain insights into when users interact with an extension management setting';
};

export class SettingsExtensionToggleRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingExtensionToggleItemTemplate> {
class SettingsExtensionToggleRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingExtensionToggleItemTemplate> {
templateId = SETTINGS_EXTENSION_TOGGLE_TEMPLATE_ID;

private readonly _onDidDismissExtensionSetting = this._register(new Emitter<string>());
readonly onDidDismissExtensionSetting = this._onDidDismissExtensionSetting.event;

renderTemplate(_container: HTMLElement): ISettingExtensionToggleItemTemplate {
const common = super.renderCommonTemplate(null, _container, 'extension-toggle');

Expand All @@ -2024,9 +2028,18 @@ export class SettingsExtensionToggleRenderer extends AbstractSettingRenderer imp
actionButton.element.classList.add('setting-item-extension-toggle-button');
actionButton.label = localize('showExtension', "Show Extension");

const dismissButton = new Button(common.containerElement, {
title: false,
secondary: true,
...defaultButtonStyles
});
dismissButton.element.classList.add('setting-item-extension-dismiss-button');
dismissButton.label = localize('dismiss', "Dismiss");

const template: ISettingExtensionToggleItemTemplate = {
...common,
actionButton
actionButton,
dismissButton
};

this.addSettingElementFocusHandler(template);
Expand All @@ -2046,6 +2059,11 @@ export class SettingsExtensionToggleRenderer extends AbstractSettingRenderer imp
this._telemetryService.publicLog2<{ extensionId: String }, ManageExtensionClickTelemetryClassification>('ManageExtensionClick', { extensionId });
this._commandService.executeCommand('extension.open', extensionId);
}));

template.elementDisposables.add(template.dismissButton.onDidClick(async () => {
this._telemetryService.publicLog2<{ extensionId: String }, ManageExtensionClickTelemetryClassification>('DismissExtensionClick', { extensionId });
this._onDidDismissExtensionSetting.fire(extensionId);
}));
}
}

Expand All @@ -2055,6 +2073,8 @@ export class SettingTreeRenderers extends Disposable {
private readonly _onDidChangeSetting = this._register(new Emitter<ISettingChangeEvent>());
readonly onDidChangeSetting: Event<ISettingChangeEvent>;

readonly onDidDismissExtensionSetting: Event<string>;

readonly onDidOpenSettings: Event<string>;

readonly onDidClickSettingLink: Event<ISettingLinkClickEvent>;
Expand Down Expand Up @@ -2098,6 +2118,7 @@ export class SettingTreeRenderers extends Disposable {

const actionFactory = (setting: ISetting, settingTarget: SettingsTarget) => this.getActionsForSetting(setting, settingTarget);
const emptyActionFactory = (_: ISetting) => [];
const extensionRenderer = this._instantiationService.createInstance(SettingsExtensionToggleRenderer, [], emptyActionFactory);
const settingRenderers = [
this._instantiationService.createInstance(SettingBoolRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingNumberRenderer, this.settingActions, actionFactory),
Expand All @@ -2110,14 +2131,15 @@ export class SettingTreeRenderers extends Disposable {
this._instantiationService.createInstance(SettingEnumRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingObjectRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingBoolObjectRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingsExtensionToggleRenderer, [], emptyActionFactory)
extensionRenderer
];

this.onDidClickOverrideElement = Event.any(...settingRenderers.map(r => r.onDidClickOverrideElement));
this.onDidChangeSetting = Event.any(
...settingRenderers.map(r => r.onDidChangeSetting),
this._onDidChangeSetting.event
);
this.onDidDismissExtensionSetting = extensionRenderer.onDidDismissExtensionSetting;
this.onDidOpenSettings = Event.any(...settingRenderers.map(r => r.onDidOpenSettings));
this.onDidClickSettingLink = Event.any(...settingRenderers.map(r => r.onDidClickSettingLink));
this.onDidFocusSetting = Event.any(...settingRenderers.map(r => r.onDidFocusSetting));
Expand Down
2 changes: 0 additions & 2 deletions src/vs/workbench/services/preferences/common/preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ export interface ISetting {
// Internal properties
allKeysAreBoolean?: boolean;
displayExtensionId?: string;
stableExtensionId?: string;
prereleaseExtensionId?: string;
title?: string;
extensionGroupTitle?: string;
internalOrder?: number;
Expand Down
Loading
Loading