From 5a8d3cf2e00f948a17398aab7bfbd03f2a228c25 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 3 Jul 2024 12:52:02 +0200 Subject: [PATCH] Refactor preference tree layouting (#13819) --- .../common/preferences/preference-schema.ts | 5 + .../src/hosted/node/scanners/scanner-theia.ts | 1 + .../src/browser/preference-frontend-module.ts | 2 + .../src/browser/preference-tree-model.ts | 3 +- .../src/browser/util/preference-layout.ts | 377 ++++++++++++++++++ .../browser/util/preference-tree-generator.ts | 91 ++--- .../preference-tree-label-provider.spec.ts | 2 + .../util/preference-tree-label-provider.ts | 22 +- .../src/browser/util/preference-types.ts | 10 +- .../browser/views/preference-editor-widget.ts | 2 +- 10 files changed, 443 insertions(+), 72 deletions(-) create mode 100644 packages/preferences/src/browser/util/preference-layout.ts diff --git a/packages/core/src/common/preferences/preference-schema.ts b/packages/core/src/common/preferences/preference-schema.ts index b77c01a1891f9..436a9f82a1d38 100644 --- a/packages/core/src/common/preferences/preference-schema.ts +++ b/packages/core/src/common/preferences/preference-schema.ts @@ -25,6 +25,11 @@ export interface PreferenceSchema { [name: string]: any, scope?: 'application' | 'window' | 'resource' | PreferenceScope, overridable?: boolean; + /** + * The title of the preference schema. + * It is used in the preference UI to associate a localized group of preferences. + */ + title?: string; properties: PreferenceSchemaProperties } export namespace PreferenceSchema { diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 834aaee8bc35a..1d309445f0bfb 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -196,6 +196,7 @@ export class TheiaPluginScanner extends AbstractPluginScanner { for (const c of configurations) { const config = this.readConfiguration(c, rawPlugin.packagePath); if (config) { + Object.values(config.properties).forEach(property => property.title = config.title); contributions.configuration.push(config); } } diff --git a/packages/preferences/src/browser/preference-frontend-module.ts b/packages/preferences/src/browser/preference-frontend-module.ts index a66fbace9102e..ef7d553bcee84 100644 --- a/packages/preferences/src/browser/preference-frontend-module.ts +++ b/packages/preferences/src/browser/preference-frontend-module.ts @@ -32,12 +32,14 @@ import { PreferenceOpenHandler } from './preference-open-handler'; import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences'; import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; import { PreferenceFrontendContribution } from './preference-frontend-contribution'; +import { PreferenceLayoutProvider } from './util/preference-layout'; export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind): void { bindPreferenceProviders(bind, unbind); bindPreferencesWidgets(bind); bind(PreferenceTreeGenerator).toSelf().inSingletonScope(); + bind(PreferenceLayoutProvider).toSelf().inSingletonScope(); bindViewContribution(bind, PreferencesContribution); diff --git a/packages/preferences/src/browser/preference-tree-model.ts b/packages/preferences/src/browser/preference-tree-model.ts index 689e186641fba..c65e88166ea77 100644 --- a/packages/preferences/src/browser/preference-tree-model.ts +++ b/packages/preferences/src/browser/preference-tree-model.ts @@ -30,11 +30,12 @@ import { } from '@theia/core/lib/browser'; import { Emitter } from '@theia/core'; import { PreferencesSearchbarWidget } from './views/preference-searchbar-widget'; -import { PreferenceTreeGenerator, COMMONLY_USED_SECTION_PREFIX } from './util/preference-tree-generator'; +import { PreferenceTreeGenerator } from './util/preference-tree-generator'; import * as fuzzy from '@theia/core/shared/fuzzy'; import { PreferencesScopeTabBar } from './views/preference-scope-tabbar-widget'; import { Preference } from './util/preference-types'; import { Event } from '@theia/core/lib/common'; +import { COMMONLY_USED_SECTION_PREFIX } from './util/preference-layout'; export interface PreferenceTreeNodeProps extends NodeProps { visibleChildren: number; diff --git a/packages/preferences/src/browser/util/preference-layout.ts b/packages/preferences/src/browser/util/preference-layout.ts new file mode 100644 index 0000000000000..43ba032e0ca7f --- /dev/null +++ b/packages/preferences/src/browser/util/preference-layout.ts @@ -0,0 +1,377 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { nls } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; + +export interface PreferenceLayout { + id: string; + label: string; + children?: PreferenceLayout[]; + settings?: string[]; +} + +export const COMMONLY_USED_SECTION_PREFIX = 'commonly-used'; + +export const COMMONLY_USED_LAYOUT = { + id: COMMONLY_USED_SECTION_PREFIX, + label: nls.localizeByDefault('Commonly Used'), + settings: [ + 'files.autoSave', + 'editor.fontSize', + 'editor.fontFamily', + 'editor.tabSize', + 'editor.renderWhitespace', + 'editor.cursorStyle', + 'editor.multiCursorModifier', + 'editor.insertSpaces', + 'editor.wordWrap', + 'files.exclude', + 'files.associations' + ] +}; + +export const DEFAULT_LAYOUT: PreferenceLayout[] = [ + { + id: 'editor', + label: nls.localizeByDefault('Text Editor'), + settings: ['editor.*'], + children: [ + { + id: 'editor.cursor', + label: nls.localizeByDefault('Cursor'), + settings: ['editor.cursor*'] + }, + { + id: 'editor.find', + label: nls.localizeByDefault('Find'), + settings: ['editor.find.*'] + }, + { + id: 'editor.font', + label: nls.localizeByDefault('Font'), + settings: ['editor.font*'] + }, + { + id: 'editor.format', + label: nls.localizeByDefault('Formatting'), + settings: ['editor.format*'] + }, + { + id: 'editor.diffEditor', + label: nls.localizeByDefault('Diff Editor'), + settings: ['diffEditor.*'] + }, + { + id: 'editor.multiDiffEditor', + label: nls.localizeByDefault('Multi-File Diff Editor'), + settings: ['multiDiffEditor.*'] + }, + { + id: 'editor.minimap', + label: nls.localizeByDefault('Minimap'), + settings: ['editor.minimap.*'] + }, + { + id: 'editor.suggestions', + label: nls.localizeByDefault('Suggestions'), + settings: ['editor.*suggest*'] + }, + { + id: 'editor.files', + label: nls.localizeByDefault('Files'), + settings: ['files.*'] + } + ] + }, + { + id: 'workbench', + label: nls.localizeByDefault('Workbench'), + settings: ['workbench.*', 'workspace.*'], + children: [ + { + id: 'workbench.appearance', + label: nls.localizeByDefault('Appearance'), + settings: [ + 'workbench.activityBar.*', 'workbench.*color*', 'workbench.fontAliasing', 'workbench.iconTheme', 'workbench.sidebar.location', + 'workbench.*.visible', 'workbench.tips.enabled', 'workbench.tree.*', 'workbench.view.*' + ] + }, + { + id: 'workbench.breadcrumbs', + label: nls.localizeByDefault('Breadcrumbs'), + settings: ['breadcrumbs.*'] + }, + { + id: 'workbench.editor', + label: nls.localizeByDefault('Editor Management'), + settings: ['workbench.editor.*'] + }, + { + id: 'workbench.settings', + label: nls.localizeByDefault('Settings Editor'), + settings: ['workbench.settings.*'] + }, + { + id: 'workbench.zenmode', + label: nls.localizeByDefault('Zen Mode'), + settings: ['zenmode.*'] + }, + { + id: 'workbench.screencastmode', + label: nls.localizeByDefault('Screencast Mode'), + settings: ['screencastMode.*'] + } + ] + }, + { + id: 'window', + label: nls.localizeByDefault('Window'), + settings: ['window.*'], + children: [ + { + id: 'window.newWindow', + label: nls.localizeByDefault('New Window'), + settings: ['window.*newwindow*'] + } + ] + }, + { + id: 'features', + label: nls.localizeByDefault('Features'), + children: [ + { + id: 'features.accessibilitySignals', + label: nls.localizeByDefault('Accessibility Signals'), + settings: ['accessibility.signal*'] + }, + { + id: 'features.accessibility', + label: nls.localizeByDefault('Accessibility'), + settings: ['accessibility.*'] + }, + { + id: 'features.explorer', + label: nls.localizeByDefault('Explorer'), + settings: ['explorer.*', 'outline.*'] + }, + { + id: 'features.search', + label: nls.localizeByDefault('Search'), + settings: ['search.*'] + }, + { + id: 'features.debug', + label: nls.localizeByDefault('Debug'), + settings: ['debug.*', 'launch'] + }, + { + id: 'features.testing', + label: nls.localizeByDefault('Testing'), + settings: ['testing.*'] + }, + { + id: 'features.scm', + label: nls.localizeByDefault('Source Control'), + settings: ['scm.*'] + }, + { + id: 'features.extensions', + label: nls.localizeByDefault('Extensions'), + settings: ['extensions.*'] + }, + { + id: 'features.terminal', + label: nls.localizeByDefault('Terminal'), + settings: ['terminal.*'] + }, + { + id: 'features.task', + label: nls.localizeByDefault('Task'), + settings: ['task.*'] + }, + { + id: 'features.problems', + label: nls.localizeByDefault('Problems'), + settings: ['problems.*'] + }, + { + id: 'features.output', + label: nls.localizeByDefault('Output'), + settings: ['output.*'] + }, + { + id: 'features.comments', + label: nls.localizeByDefault('Comments'), + settings: ['comments.*'] + }, + { + id: 'features.remote', + label: nls.localizeByDefault('Remote'), + settings: ['remote.*'] + }, + { + id: 'features.timeline', + label: nls.localizeByDefault('Timeline'), + settings: ['timeline.*'] + }, + { + id: 'features.toolbar', + label: nls.localize('theia/preferences/toolbar', 'Toolbar'), + settings: ['toolbar.*'] + }, + { + id: 'features.notebook', + label: nls.localizeByDefault('Notebook'), + settings: ['notebook.*', 'interactiveWindow.*'] + }, + { + id: 'features.mergeEditor', + label: nls.localizeByDefault('Merge Editor'), + settings: ['mergeEditor.*'] + }, + { + id: 'features.chat', + label: nls.localizeByDefault('Chat'), + settings: ['chat.*', 'inlineChat.*'] + } + ] + }, + { + id: 'application', + label: nls.localizeByDefault('Application'), + children: [ + { + id: 'application.http', + label: nls.localizeByDefault('HTTP'), + settings: ['http.*'] + }, + { + id: 'application.keyboard', + label: nls.localizeByDefault('Keyboard'), + settings: ['keyboard.*'] + }, + { + id: 'application.update', + label: nls.localizeByDefault('Update'), + settings: ['update.*'] + }, + { + id: 'application.telemetry', + label: nls.localizeByDefault('Telemetry'), + settings: ['telemetry.*'] + }, + { + id: 'application.settingsSync', + label: nls.localizeByDefault('Settings Sync'), + settings: ['settingsSync.*'] + }, + { + id: 'application.experimental', + label: nls.localizeByDefault('Experimental'), + settings: ['application.experimental.*'] + }, + { + id: 'application.other', + label: nls.localizeByDefault('Other'), + settings: ['application.*'] + } + ] + }, + { + id: 'security', + label: nls.localizeByDefault('Security'), + settings: ['security.*'], + children: [ + { + id: 'security.workspace', + label: nls.localizeByDefault('Workspace'), + settings: ['security.workspace.*'] + } + ] + }, + { + id: 'extensions', + label: nls.localizeByDefault('Extensions'), + children: [ + { + id: 'extensions.hosted-plugin', + label: nls.localize('theia/preferences/hostedPlugin', 'Hosted Plugin'), + settings: ['hosted-plugin.*'] + } + ] + } +]; + +@injectable() +export class PreferenceLayoutProvider { + + getLayout(): PreferenceLayout[] { + return DEFAULT_LAYOUT; + } + + getCommonlyUsedLayout(): PreferenceLayout { + return COMMONLY_USED_LAYOUT; + } + + hasCategory(id: string): boolean { + return [...this.getLayout(), this.getCommonlyUsedLayout()].some(e => e.id === id); + } + + getLayoutForPreference(preferenceId: string): PreferenceLayout | undefined { + const layout = this.getLayout(); + for (const section of layout) { + const item = this.findItemInSection(section, preferenceId); + if (item) { + return item; + } + } + return undefined; + } + + protected findItemInSection(section: PreferenceLayout, preferenceId: string): PreferenceLayout | undefined { + // First check whether any of its children match the preferenceId. + if (section.children) { + for (const child of section.children) { + const item = this.findItemInSection(child, preferenceId); + if (item) { + return item; + } + } + } + // Then check whether the section itself matches the preferenceId. + if (section.settings) { + for (const setting of section.settings) { + if (this.matchesSetting(preferenceId, setting)) { + return section; + } + } + } + return undefined; + } + + protected matchesSetting(preferenceId: string, setting: string): boolean { + if (setting.includes('*')) { + return this.createRegExp(setting).test(preferenceId); + } + return preferenceId === setting; + } + + protected createRegExp(setting: string): RegExp { + return new RegExp(`^${setting.replace(/\./g, '\\.').replace(/\*/g, '.*')}$`); + } + +} diff --git a/packages/preferences/src/browser/util/preference-tree-generator.ts b/packages/preferences/src/browser/util/preference-tree-generator.ts index 1c6e8b392767f..05648a634b5b2 100644 --- a/packages/preferences/src/browser/util/preference-tree-generator.ts +++ b/packages/preferences/src/browser/util/preference-tree-generator.ts @@ -20,58 +20,19 @@ import { PreferenceConfigurations } from '@theia/core/lib/browser/preferences/pr import { Emitter } from '@theia/core'; import debounce = require('@theia/core/shared/lodash.debounce'); import { Preference } from './preference-types'; +import { COMMONLY_USED_SECTION_PREFIX, PreferenceLayoutProvider } from './preference-layout'; -export const COMMONLY_USED_SECTION_PREFIX = 'commonly-used'; @injectable() export class PreferenceTreeGenerator { @inject(PreferenceSchemaProvider) protected readonly schemaProvider: PreferenceSchemaProvider; @inject(PreferenceConfigurations) protected readonly preferenceConfigs: PreferenceConfigurations; + @inject(PreferenceLayoutProvider) protected readonly layoutProvider: PreferenceLayoutProvider; protected _root: CompositeTreeNode; protected readonly onSchemaChangedEmitter = new Emitter(); readonly onSchemaChanged = this.onSchemaChangedEmitter.event; - protected readonly commonlyUsedPreferences = [ - 'files.autoSave', 'files.autoSaveDelay', 'editor.fontSize', - 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', - 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', - 'editor.wordWrap', 'files.exclude', 'files.associations' - ]; - protected readonly topLevelCategories = new Map([ - [COMMONLY_USED_SECTION_PREFIX, 'Commonly Used'], - ['editor', 'Text Editor'], - ['workbench', 'Workbench'], - ['window', 'Window'], - ['features', 'Features'], - ['application', 'Application'], - ['security', 'Security'], - ['extensions', 'Extensions'] - ]); - protected readonly sectionAssignments = new Map([ - ['breadcrumbs', 'workbench'], - ['comments', 'features'], - ['debug', 'features'], - ['diffEditor', 'editor'], - ['explorer', 'features'], - ['extensions', 'features'], - ['files', 'editor'], - ['hosted-plugin', 'features'], - ['http', 'application'], - ['keyboard', 'application'], - ['notification', 'workbench'], - ['output', 'features'], - ['preview', 'features'], - ['problems', 'features'], - ['scm', 'features'], - ['search', 'features'], - ['task', 'features'], - ['terminal', 'features'], - ['testing', 'features'], - ['toolbar', 'features'], - ['webview', 'features'], - ['workspace', 'application'], - ]); protected readonly defaultTopLevelCategory = 'extensions'; get root(): CompositeTreeNode { @@ -95,11 +56,13 @@ export class PreferenceTreeGenerator { const groups = new Map(); const root = this.createRootNode(); - for (const id of this.topLevelCategories.keys()) { - this.getOrCreatePreferencesGroup(id, id, root, groups); + const commonlyUsedLayout = this.layoutProvider.getCommonlyUsedLayout(); + const commonlyUsed = this.getOrCreatePreferencesGroup(commonlyUsedLayout.id, commonlyUsedLayout.id, root, groups, commonlyUsedLayout.label); + + for (const layout of this.layoutProvider.getLayout()) { + this.getOrCreatePreferencesGroup(layout.id, layout.id, root, groups, layout.label); } - const commonlyUsed = this.getOrCreatePreferencesGroup(COMMONLY_USED_SECTION_PREFIX, COMMONLY_USED_SECTION_PREFIX, root, groups); - for (const preference of this.commonlyUsedPreferences) { + for (const preference of commonlyUsedLayout.settings ?? []) { if (preference in preferencesSchema.properties) { this.createLeafNode(preference, commonlyUsed, preferencesSchema.properties[preference]); } @@ -107,12 +70,15 @@ export class PreferenceTreeGenerator { for (const propertyName of propertyNames) { const property = preferencesSchema.properties[propertyName]; if (!this.preferenceConfigs.isSectionName(propertyName) && !OVERRIDE_PROPERTY_PATTERN.test(propertyName) && !property.deprecationMessage) { - const labels = propertyName.split('.'); - const groupID = this.getGroupName(labels); - const subgroupName = this.getSubgroupName(labels, groupID); + const layoutItem = this.layoutProvider.getLayoutForPreference(propertyName); + const labels = layoutItem ? layoutItem.id.split('.') : propertyName.split('.'); + // If a title is set, this property belongs to the 'extensions' category + const groupID = property.title ? this.defaultTopLevelCategory : this.getGroupName(labels); + // Automatically assign all properties with the same title to the same subgroup + const subgroupName = property.title ?? this.getSubgroupName(labels, groupID); const subgroupID = [groupID, subgroupName].join('.'); const toplevelParent = this.getOrCreatePreferencesGroup(groupID, groupID, root, groups); - const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups); + const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups, property.title ?? layoutItem?.label); this.createLeafNode(propertyName, immediateParent || toplevelParent, property); } } @@ -145,21 +111,19 @@ export class PreferenceTreeGenerator { protected getGroupName(labels: string[]): string { const defaultGroup = labels[0]; - if (this.topLevelCategories.has(defaultGroup)) { + if (this.layoutProvider.hasCategory(defaultGroup)) { return defaultGroup; } - const assignedGroup = this.sectionAssignments.get(defaultGroup); - if (assignedGroup) { - return assignedGroup; - } return this.defaultTopLevelCategory; } protected getSubgroupName(labels: string[], computedGroupName: string): string | undefined { if (computedGroupName !== labels[0]) { return labels[0]; - } else if (labels.length > 2) { + } else if (labels.length > 1) { return labels[1]; + } else { + return undefined; } } @@ -182,7 +146,7 @@ export class PreferenceTreeGenerator { protected createLeafNode(property: string, preferencesGroup: Preference.CompositeTreeNode, data: PreferenceDataProperty): Preference.LeafNode { const { group } = Preference.TreeNode.getGroupAndIdFromNodeId(preferencesGroup.id); - const newNode = { + const newNode: Preference.LeafNode = { id: `${group}@${property}`, preferenceId: property, parent: preferencesGroup, @@ -193,8 +157,8 @@ export class PreferenceTreeGenerator { return newNode; } - protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode): Preference.CompositeTreeNode { - const newNode = { + protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode, label?: string): Preference.CompositeTreeNode { + const newNode: Preference.CompositeTreeNode = { id: `${group}@${id}`, visible: true, parent: root, @@ -202,6 +166,7 @@ export class PreferenceTreeGenerator { expanded: false, selected: false, depth: 0, + label }; const isTopLevel = Preference.TreeNode.isTopLevel(newNode); if (!isTopLevel) { @@ -213,14 +178,12 @@ export class PreferenceTreeGenerator { return newNode; } - getCustomLabelFor(id: string): string | undefined { - return this.topLevelCategories.get(id); - } - - protected getOrCreatePreferencesGroup(id: string, group: string, root: CompositeTreeNode, groups: Map): Preference.CompositeTreeNode { + protected getOrCreatePreferencesGroup( + id: string, group: string, root: CompositeTreeNode, groups: Map, label?: string + ): Preference.CompositeTreeNode { const existingGroup = groups.get(id); if (existingGroup) { return existingGroup; } - const newNode = this.createPreferencesGroup(id, group, root); + const newNode = this.createPreferencesGroup(id, group, root, label); groups.set(id, newNode); return newNode; }; diff --git a/packages/preferences/src/browser/util/preference-tree-label-provider.spec.ts b/packages/preferences/src/browser/util/preference-tree-label-provider.spec.ts index 06b764580b449..d50b22baef6c3 100644 --- a/packages/preferences/src/browser/util/preference-tree-label-provider.spec.ts +++ b/packages/preferences/src/browser/util/preference-tree-label-provider.spec.ts @@ -28,6 +28,7 @@ import { PreferenceTreeGenerator } from './preference-tree-generator'; import { PreferenceTreeLabelProvider } from './preference-tree-label-provider'; import { Preference } from './preference-types'; import { SelectableTreeNode } from '@theia/core/lib/browser'; +import { PreferenceLayoutProvider } from './preference-layout'; disableJSDOM(); @@ -37,6 +38,7 @@ describe('preference-tree-label-provider', () => { beforeEach(() => { const container = new Container(); + container.bind(PreferenceLayoutProvider).toSelf().inSingletonScope(); container.bind(PreferenceTreeGenerator).toConstantValue({ getCustomLabelFor: () => { } }); preferenceTreeLabelProvider = container.resolve(PreferenceTreeLabelProvider); }); diff --git a/packages/preferences/src/browser/util/preference-tree-label-provider.ts b/packages/preferences/src/browser/util/preference-tree-label-provider.ts index 671bed7be7565..a40f1fbd272af 100644 --- a/packages/preferences/src/browser/util/preference-tree-label-provider.ts +++ b/packages/preferences/src/browser/util/preference-tree-label-provider.ts @@ -14,21 +14,35 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject } from '@theia/core/shared/inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { LabelProviderContribution, TreeNode } from '@theia/core/lib/browser'; import { Preference } from './preference-types'; -import { PreferenceTreeGenerator } from './preference-tree-generator'; +import { PreferenceLayoutProvider } from './preference-layout'; + @injectable() export class PreferenceTreeLabelProvider implements LabelProviderContribution { - @inject(PreferenceTreeGenerator) protected readonly treeGenerator: PreferenceTreeGenerator; + + @inject(PreferenceLayoutProvider) + protected readonly layoutProvider: PreferenceLayoutProvider; canHandle(element: object): number { return TreeNode.is(element) && Preference.TreeNode.is(element) ? 150 : 0; } getName(node: Preference.TreeNode): string { + if (Preference.CompositeTreeNode.is(node) && node.label) { + return node.label; + } const { id } = Preference.TreeNode.getGroupAndIdFromNodeId(node.id); - return this.formatString(this.treeGenerator.getCustomLabelFor(id) ?? id.split('.').pop()!); + const layouts = this.layoutProvider.getLayout(); + const layout = layouts.find(e => e.id === id); + if (layout) { + return layout.label; + } else { + const labels = id.split('.'); + const groupName = labels[labels.length - 1]; + return this.formatString(groupName); + } } getPrefix(node: Preference.TreeNode, fullPath = false): string | undefined { diff --git a/packages/preferences/src/browser/util/preference-types.ts b/packages/preferences/src/browser/util/preference-types.ts index c901c87a3ae85..29ee2bf3f67c9 100644 --- a/packages/preferences/src/browser/util/preference-types.ts +++ b/packages/preferences/src/browser/util/preference-types.ts @@ -18,7 +18,8 @@ import { PreferenceDataProperty, PreferenceScope, TreeNode as BaseTreeNode, - CompositeTreeNode as BaseCompositeTreeNode, + ExpandableTreeNode, + SelectableTreeNode, PreferenceInspection, CommonCommands, } from '@theia/core/lib/browser'; @@ -58,8 +59,13 @@ export namespace Preference { }; } - export interface CompositeTreeNode extends BaseCompositeTreeNode { + export interface CompositeTreeNode extends ExpandableTreeNode, SelectableTreeNode { depth: number; + label?: string; + } + + export namespace CompositeTreeNode { + export const is = (node: TreeNode): node is CompositeTreeNode => !LeafNode.is(node); } export interface LeafNode extends BaseTreeNode { diff --git a/packages/preferences/src/browser/views/preference-editor-widget.ts b/packages/preferences/src/browser/views/preference-editor-widget.ts index 4d2e06ea70568..d8405759ad312 100644 --- a/packages/preferences/src/browser/views/preference-editor-widget.ts +++ b/packages/preferences/src/browser/views/preference-editor-widget.ts @@ -33,9 +33,9 @@ import { BaseWidget, DEFAULT_SCROLL_OPTIONS } from '@theia/core/lib/browser/widg import { PreferenceTreeModel, PreferenceFilterChangeEvent, PreferenceFilterChangeSource } from '../preference-tree-model'; import { PreferenceNodeRendererFactory, GeneralPreferenceNodeRenderer } from './components/preference-node-renderer'; import { Preference } from '../util/preference-types'; -import { COMMONLY_USED_SECTION_PREFIX } from '../util/preference-tree-generator'; import { PreferencesScopeTabBar } from './preference-scope-tabbar-widget'; import { PreferenceNodeRendererCreatorRegistry } from './components/preference-node-renderer-creator'; +import { COMMONLY_USED_SECTION_PREFIX } from '../util/preference-layout'; export interface PreferencesEditorState { firstVisibleChildID: string,