@theia/preferences 1.51.0 → 1.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/lib/browser/preference-frontend-module.d.ts.map +1 -1
  2. package/lib/browser/preference-frontend-module.js +2 -0
  3. package/lib/browser/preference-frontend-module.js.map +1 -1
  4. package/lib/browser/preference-tree-model.d.ts.map +1 -1
  5. package/lib/browser/preference-tree-model.js +2 -1
  6. package/lib/browser/preference-tree-model.js.map +1 -1
  7. package/lib/browser/util/preference-layout.d.ts +23 -0
  8. package/lib/browser/util/preference-layout.d.ts.map +1 -0
  9. package/lib/browser/util/preference-layout.js +365 -0
  10. package/lib/browser/util/preference-layout.js.map +1 -0
  11. package/lib/browser/util/preference-tree-generator.d.ts +4 -7
  12. package/lib/browser/util/preference-tree-generator.d.ts.map +1 -1
  13. package/lib/browser/util/preference-tree-generator.js +29 -63
  14. package/lib/browser/util/preference-tree-generator.js.map +1 -1
  15. package/lib/browser/util/preference-tree-label-provider.d.ts +2 -2
  16. package/lib/browser/util/preference-tree-label-provider.d.ts.map +1 -1
  17. package/lib/browser/util/preference-tree-label-provider.js +17 -6
  18. package/lib/browser/util/preference-tree-label-provider.js.map +1 -1
  19. package/lib/browser/util/preference-tree-label-provider.spec.js +2 -0
  20. package/lib/browser/util/preference-tree-label-provider.spec.js.map +1 -1
  21. package/lib/browser/util/preference-types.d.ts +6 -2
  22. package/lib/browser/util/preference-types.d.ts.map +1 -1
  23. package/lib/browser/util/preference-types.js +4 -0
  24. package/lib/browser/util/preference-types.js.map +1 -1
  25. package/lib/browser/views/preference-editor-widget.d.ts.map +1 -1
  26. package/lib/browser/views/preference-editor-widget.js +2 -2
  27. package/lib/browser/views/preference-editor-widget.js.map +1 -1
  28. package/lib/browser/views/preference-widget.d.ts.map +1 -1
  29. package/lib/browser/views/preference-widget.js +1 -0
  30. package/lib/browser/views/preference-widget.js.map +1 -1
  31. package/package.json +9 -9
  32. package/src/browser/preference-frontend-module.ts +2 -0
  33. package/src/browser/preference-tree-model.ts +2 -1
  34. package/src/browser/util/preference-layout.ts +377 -0
  35. package/src/browser/util/preference-tree-generator.ts +27 -64
  36. package/src/browser/util/preference-tree-label-provider.spec.ts +2 -0
  37. package/src/browser/util/preference-tree-label-provider.ts +18 -4
  38. package/src/browser/util/preference-types.ts +8 -2
  39. package/src/browser/views/preference-editor-widget.ts +1 -1
  40. package/src/browser/views/preference-widget.tsx +1 -0
@@ -0,0 +1,377 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { nls } from '@theia/core';
18
+ import { injectable } from '@theia/core/shared/inversify';
19
+
20
+ export interface PreferenceLayout {
21
+ id: string;
22
+ label: string;
23
+ children?: PreferenceLayout[];
24
+ settings?: string[];
25
+ }
26
+
27
+ export const COMMONLY_USED_SECTION_PREFIX = 'commonly-used';
28
+
29
+ export const COMMONLY_USED_LAYOUT = {
30
+ id: COMMONLY_USED_SECTION_PREFIX,
31
+ label: nls.localizeByDefault('Commonly Used'),
32
+ settings: [
33
+ 'files.autoSave',
34
+ 'editor.fontSize',
35
+ 'editor.fontFamily',
36
+ 'editor.tabSize',
37
+ 'editor.renderWhitespace',
38
+ 'editor.cursorStyle',
39
+ 'editor.multiCursorModifier',
40
+ 'editor.insertSpaces',
41
+ 'editor.wordWrap',
42
+ 'files.exclude',
43
+ 'files.associations'
44
+ ]
45
+ };
46
+
47
+ export const DEFAULT_LAYOUT: PreferenceLayout[] = [
48
+ {
49
+ id: 'editor',
50
+ label: nls.localizeByDefault('Text Editor'),
51
+ settings: ['editor.*'],
52
+ children: [
53
+ {
54
+ id: 'editor.cursor',
55
+ label: nls.localizeByDefault('Cursor'),
56
+ settings: ['editor.cursor*']
57
+ },
58
+ {
59
+ id: 'editor.find',
60
+ label: nls.localizeByDefault('Find'),
61
+ settings: ['editor.find.*']
62
+ },
63
+ {
64
+ id: 'editor.font',
65
+ label: nls.localizeByDefault('Font'),
66
+ settings: ['editor.font*']
67
+ },
68
+ {
69
+ id: 'editor.format',
70
+ label: nls.localizeByDefault('Formatting'),
71
+ settings: ['editor.format*']
72
+ },
73
+ {
74
+ id: 'editor.diffEditor',
75
+ label: nls.localizeByDefault('Diff Editor'),
76
+ settings: ['diffEditor.*']
77
+ },
78
+ {
79
+ id: 'editor.multiDiffEditor',
80
+ label: nls.localizeByDefault('Multi-File Diff Editor'),
81
+ settings: ['multiDiffEditor.*']
82
+ },
83
+ {
84
+ id: 'editor.minimap',
85
+ label: nls.localizeByDefault('Minimap'),
86
+ settings: ['editor.minimap.*']
87
+ },
88
+ {
89
+ id: 'editor.suggestions',
90
+ label: nls.localizeByDefault('Suggestions'),
91
+ settings: ['editor.*suggest*']
92
+ },
93
+ {
94
+ id: 'editor.files',
95
+ label: nls.localizeByDefault('Files'),
96
+ settings: ['files.*']
97
+ }
98
+ ]
99
+ },
100
+ {
101
+ id: 'workbench',
102
+ label: nls.localizeByDefault('Workbench'),
103
+ settings: ['workbench.*', 'workspace.*'],
104
+ children: [
105
+ {
106
+ id: 'workbench.appearance',
107
+ label: nls.localizeByDefault('Appearance'),
108
+ settings: [
109
+ 'workbench.activityBar.*', 'workbench.*color*', 'workbench.fontAliasing', 'workbench.iconTheme', 'workbench.sidebar.location',
110
+ 'workbench.*.visible', 'workbench.tips.enabled', 'workbench.tree.*', 'workbench.view.*'
111
+ ]
112
+ },
113
+ {
114
+ id: 'workbench.breadcrumbs',
115
+ label: nls.localizeByDefault('Breadcrumbs'),
116
+ settings: ['breadcrumbs.*']
117
+ },
118
+ {
119
+ id: 'workbench.editor',
120
+ label: nls.localizeByDefault('Editor Management'),
121
+ settings: ['workbench.editor.*']
122
+ },
123
+ {
124
+ id: 'workbench.settings',
125
+ label: nls.localizeByDefault('Settings Editor'),
126
+ settings: ['workbench.settings.*']
127
+ },
128
+ {
129
+ id: 'workbench.zenmode',
130
+ label: nls.localizeByDefault('Zen Mode'),
131
+ settings: ['zenmode.*']
132
+ },
133
+ {
134
+ id: 'workbench.screencastmode',
135
+ label: nls.localizeByDefault('Screencast Mode'),
136
+ settings: ['screencastMode.*']
137
+ }
138
+ ]
139
+ },
140
+ {
141
+ id: 'window',
142
+ label: nls.localizeByDefault('Window'),
143
+ settings: ['window.*'],
144
+ children: [
145
+ {
146
+ id: 'window.newWindow',
147
+ label: nls.localizeByDefault('New Window'),
148
+ settings: ['window.*newwindow*']
149
+ }
150
+ ]
151
+ },
152
+ {
153
+ id: 'features',
154
+ label: nls.localizeByDefault('Features'),
155
+ children: [
156
+ {
157
+ id: 'features.accessibilitySignals',
158
+ label: nls.localizeByDefault('Accessibility Signals'),
159
+ settings: ['accessibility.signal*']
160
+ },
161
+ {
162
+ id: 'features.accessibility',
163
+ label: nls.localizeByDefault('Accessibility'),
164
+ settings: ['accessibility.*']
165
+ },
166
+ {
167
+ id: 'features.explorer',
168
+ label: nls.localizeByDefault('Explorer'),
169
+ settings: ['explorer.*', 'outline.*']
170
+ },
171
+ {
172
+ id: 'features.search',
173
+ label: nls.localizeByDefault('Search'),
174
+ settings: ['search.*']
175
+ },
176
+ {
177
+ id: 'features.debug',
178
+ label: nls.localizeByDefault('Debug'),
179
+ settings: ['debug.*', 'launch']
180
+ },
181
+ {
182
+ id: 'features.testing',
183
+ label: nls.localizeByDefault('Testing'),
184
+ settings: ['testing.*']
185
+ },
186
+ {
187
+ id: 'features.scm',
188
+ label: nls.localizeByDefault('Source Control'),
189
+ settings: ['scm.*']
190
+ },
191
+ {
192
+ id: 'features.extensions',
193
+ label: nls.localizeByDefault('Extensions'),
194
+ settings: ['extensions.*']
195
+ },
196
+ {
197
+ id: 'features.terminal',
198
+ label: nls.localizeByDefault('Terminal'),
199
+ settings: ['terminal.*']
200
+ },
201
+ {
202
+ id: 'features.task',
203
+ label: nls.localizeByDefault('Task'),
204
+ settings: ['task.*']
205
+ },
206
+ {
207
+ id: 'features.problems',
208
+ label: nls.localizeByDefault('Problems'),
209
+ settings: ['problems.*']
210
+ },
211
+ {
212
+ id: 'features.output',
213
+ label: nls.localizeByDefault('Output'),
214
+ settings: ['output.*']
215
+ },
216
+ {
217
+ id: 'features.comments',
218
+ label: nls.localizeByDefault('Comments'),
219
+ settings: ['comments.*']
220
+ },
221
+ {
222
+ id: 'features.remote',
223
+ label: nls.localizeByDefault('Remote'),
224
+ settings: ['remote.*']
225
+ },
226
+ {
227
+ id: 'features.timeline',
228
+ label: nls.localizeByDefault('Timeline'),
229
+ settings: ['timeline.*']
230
+ },
231
+ {
232
+ id: 'features.toolbar',
233
+ label: nls.localize('theia/preferences/toolbar', 'Toolbar'),
234
+ settings: ['toolbar.*']
235
+ },
236
+ {
237
+ id: 'features.notebook',
238
+ label: nls.localizeByDefault('Notebook'),
239
+ settings: ['notebook.*', 'interactiveWindow.*']
240
+ },
241
+ {
242
+ id: 'features.mergeEditor',
243
+ label: nls.localizeByDefault('Merge Editor'),
244
+ settings: ['mergeEditor.*']
245
+ },
246
+ {
247
+ id: 'features.chat',
248
+ label: nls.localizeByDefault('Chat'),
249
+ settings: ['chat.*', 'inlineChat.*']
250
+ }
251
+ ]
252
+ },
253
+ {
254
+ id: 'application',
255
+ label: nls.localizeByDefault('Application'),
256
+ children: [
257
+ {
258
+ id: 'application.http',
259
+ label: nls.localizeByDefault('HTTP'),
260
+ settings: ['http.*']
261
+ },
262
+ {
263
+ id: 'application.keyboard',
264
+ label: nls.localizeByDefault('Keyboard'),
265
+ settings: ['keyboard.*']
266
+ },
267
+ {
268
+ id: 'application.update',
269
+ label: nls.localizeByDefault('Update'),
270
+ settings: ['update.*']
271
+ },
272
+ {
273
+ id: 'application.telemetry',
274
+ label: nls.localizeByDefault('Telemetry'),
275
+ settings: ['telemetry.*']
276
+ },
277
+ {
278
+ id: 'application.settingsSync',
279
+ label: nls.localizeByDefault('Settings Sync'),
280
+ settings: ['settingsSync.*']
281
+ },
282
+ {
283
+ id: 'application.experimental',
284
+ label: nls.localizeByDefault('Experimental'),
285
+ settings: ['application.experimental.*']
286
+ },
287
+ {
288
+ id: 'application.other',
289
+ label: nls.localizeByDefault('Other'),
290
+ settings: ['application.*']
291
+ }
292
+ ]
293
+ },
294
+ {
295
+ id: 'security',
296
+ label: nls.localizeByDefault('Security'),
297
+ settings: ['security.*'],
298
+ children: [
299
+ {
300
+ id: 'security.workspace',
301
+ label: nls.localizeByDefault('Workspace'),
302
+ settings: ['security.workspace.*']
303
+ }
304
+ ]
305
+ },
306
+ {
307
+ id: 'extensions',
308
+ label: nls.localizeByDefault('Extensions'),
309
+ children: [
310
+ {
311
+ id: 'extensions.hosted-plugin',
312
+ label: nls.localize('theia/preferences/hostedPlugin', 'Hosted Plugin'),
313
+ settings: ['hosted-plugin.*']
314
+ }
315
+ ]
316
+ }
317
+ ];
318
+
319
+ @injectable()
320
+ export class PreferenceLayoutProvider {
321
+
322
+ getLayout(): PreferenceLayout[] {
323
+ return DEFAULT_LAYOUT;
324
+ }
325
+
326
+ getCommonlyUsedLayout(): PreferenceLayout {
327
+ return COMMONLY_USED_LAYOUT;
328
+ }
329
+
330
+ hasCategory(id: string): boolean {
331
+ return [...this.getLayout(), this.getCommonlyUsedLayout()].some(e => e.id === id);
332
+ }
333
+
334
+ getLayoutForPreference(preferenceId: string): PreferenceLayout | undefined {
335
+ const layout = this.getLayout();
336
+ for (const section of layout) {
337
+ const item = this.findItemInSection(section, preferenceId);
338
+ if (item) {
339
+ return item;
340
+ }
341
+ }
342
+ return undefined;
343
+ }
344
+
345
+ protected findItemInSection(section: PreferenceLayout, preferenceId: string): PreferenceLayout | undefined {
346
+ // First check whether any of its children match the preferenceId.
347
+ if (section.children) {
348
+ for (const child of section.children) {
349
+ const item = this.findItemInSection(child, preferenceId);
350
+ if (item) {
351
+ return item;
352
+ }
353
+ }
354
+ }
355
+ // Then check whether the section itself matches the preferenceId.
356
+ if (section.settings) {
357
+ for (const setting of section.settings) {
358
+ if (this.matchesSetting(preferenceId, setting)) {
359
+ return section;
360
+ }
361
+ }
362
+ }
363
+ return undefined;
364
+ }
365
+
366
+ protected matchesSetting(preferenceId: string, setting: string): boolean {
367
+ if (setting.includes('*')) {
368
+ return this.createRegExp(setting).test(preferenceId);
369
+ }
370
+ return preferenceId === setting;
371
+ }
372
+
373
+ protected createRegExp(setting: string): RegExp {
374
+ return new RegExp(`^${setting.replace(/\./g, '\\.').replace(/\*/g, '.*')}$`);
375
+ }
376
+
377
+ }
@@ -20,58 +20,19 @@ import { PreferenceConfigurations } from '@theia/core/lib/browser/preferences/pr
20
20
  import { Emitter } from '@theia/core';
21
21
  import debounce = require('@theia/core/shared/lodash.debounce');
22
22
  import { Preference } from './preference-types';
23
+ import { COMMONLY_USED_SECTION_PREFIX, PreferenceLayoutProvider } from './preference-layout';
23
24
 
24
- export const COMMONLY_USED_SECTION_PREFIX = 'commonly-used';
25
25
  @injectable()
26
26
  export class PreferenceTreeGenerator {
27
27
 
28
28
  @inject(PreferenceSchemaProvider) protected readonly schemaProvider: PreferenceSchemaProvider;
29
29
  @inject(PreferenceConfigurations) protected readonly preferenceConfigs: PreferenceConfigurations;
30
+ @inject(PreferenceLayoutProvider) protected readonly layoutProvider: PreferenceLayoutProvider;
30
31
 
31
32
  protected _root: CompositeTreeNode;
32
33
 
33
34
  protected readonly onSchemaChangedEmitter = new Emitter<CompositeTreeNode>();
34
35
  readonly onSchemaChanged = this.onSchemaChangedEmitter.event;
35
- protected readonly commonlyUsedPreferences = [
36
- 'files.autoSave', 'files.autoSaveDelay', 'editor.fontSize',
37
- 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace',
38
- 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces',
39
- 'editor.wordWrap', 'files.exclude', 'files.associations'
40
- ];
41
- protected readonly topLevelCategories = new Map([
42
- [COMMONLY_USED_SECTION_PREFIX, 'Commonly Used'],
43
- ['editor', 'Text Editor'],
44
- ['workbench', 'Workbench'],
45
- ['window', 'Window'],
46
- ['features', 'Features'],
47
- ['application', 'Application'],
48
- ['security', 'Security'],
49
- ['extensions', 'Extensions']
50
- ]);
51
- protected readonly sectionAssignments = new Map([
52
- ['breadcrumbs', 'workbench'],
53
- ['comments', 'features'],
54
- ['debug', 'features'],
55
- ['diffEditor', 'editor'],
56
- ['explorer', 'features'],
57
- ['extensions', 'features'],
58
- ['files', 'editor'],
59
- ['hosted-plugin', 'features'],
60
- ['http', 'application'],
61
- ['keyboard', 'application'],
62
- ['notification', 'workbench'],
63
- ['output', 'features'],
64
- ['preview', 'features'],
65
- ['problems', 'features'],
66
- ['scm', 'features'],
67
- ['search', 'features'],
68
- ['task', 'features'],
69
- ['terminal', 'features'],
70
- ['testing', 'features'],
71
- ['toolbar', 'features'],
72
- ['webview', 'features'],
73
- ['workspace', 'application'],
74
- ]);
75
36
  protected readonly defaultTopLevelCategory = 'extensions';
76
37
 
77
38
  get root(): CompositeTreeNode {
@@ -95,11 +56,13 @@ export class PreferenceTreeGenerator {
95
56
  const groups = new Map<string, Preference.CompositeTreeNode>();
96
57
  const root = this.createRootNode();
97
58
 
98
- for (const id of this.topLevelCategories.keys()) {
99
- this.getOrCreatePreferencesGroup(id, id, root, groups);
59
+ const commonlyUsedLayout = this.layoutProvider.getCommonlyUsedLayout();
60
+ const commonlyUsed = this.getOrCreatePreferencesGroup(commonlyUsedLayout.id, commonlyUsedLayout.id, root, groups, commonlyUsedLayout.label);
61
+
62
+ for (const layout of this.layoutProvider.getLayout()) {
63
+ this.getOrCreatePreferencesGroup(layout.id, layout.id, root, groups, layout.label);
100
64
  }
101
- const commonlyUsed = this.getOrCreatePreferencesGroup(COMMONLY_USED_SECTION_PREFIX, COMMONLY_USED_SECTION_PREFIX, root, groups);
102
- for (const preference of this.commonlyUsedPreferences) {
65
+ for (const preference of commonlyUsedLayout.settings ?? []) {
103
66
  if (preference in preferencesSchema.properties) {
104
67
  this.createLeafNode(preference, commonlyUsed, preferencesSchema.properties[preference]);
105
68
  }
@@ -107,12 +70,15 @@ export class PreferenceTreeGenerator {
107
70
  for (const propertyName of propertyNames) {
108
71
  const property = preferencesSchema.properties[propertyName];
109
72
  if (!this.preferenceConfigs.isSectionName(propertyName) && !OVERRIDE_PROPERTY_PATTERN.test(propertyName) && !property.deprecationMessage) {
110
- const labels = propertyName.split('.');
111
- const groupID = this.getGroupName(labels);
112
- const subgroupName = this.getSubgroupName(labels, groupID);
73
+ const layoutItem = this.layoutProvider.getLayoutForPreference(propertyName);
74
+ const labels = layoutItem ? layoutItem.id.split('.') : propertyName.split('.');
75
+ // If a title is set, this property belongs to the 'extensions' category
76
+ const groupID = property.title ? this.defaultTopLevelCategory : this.getGroupName(labels);
77
+ // Automatically assign all properties with the same title to the same subgroup
78
+ const subgroupName = property.title ?? this.getSubgroupName(labels, groupID);
113
79
  const subgroupID = [groupID, subgroupName].join('.');
114
80
  const toplevelParent = this.getOrCreatePreferencesGroup(groupID, groupID, root, groups);
115
- const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups);
81
+ const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups, property.title ?? layoutItem?.label);
116
82
  this.createLeafNode(propertyName, immediateParent || toplevelParent, property);
117
83
  }
118
84
  }
@@ -145,21 +111,19 @@ export class PreferenceTreeGenerator {
145
111
 
146
112
  protected getGroupName(labels: string[]): string {
147
113
  const defaultGroup = labels[0];
148
- if (this.topLevelCategories.has(defaultGroup)) {
114
+ if (this.layoutProvider.hasCategory(defaultGroup)) {
149
115
  return defaultGroup;
150
116
  }
151
- const assignedGroup = this.sectionAssignments.get(defaultGroup);
152
- if (assignedGroup) {
153
- return assignedGroup;
154
- }
155
117
  return this.defaultTopLevelCategory;
156
118
  }
157
119
 
158
120
  protected getSubgroupName(labels: string[], computedGroupName: string): string | undefined {
159
121
  if (computedGroupName !== labels[0]) {
160
122
  return labels[0];
161
- } else if (labels.length > 2) {
123
+ } else if (labels.length > 1) {
162
124
  return labels[1];
125
+ } else {
126
+ return undefined;
163
127
  }
164
128
  }
165
129
 
@@ -182,7 +146,7 @@ export class PreferenceTreeGenerator {
182
146
 
183
147
  protected createLeafNode(property: string, preferencesGroup: Preference.CompositeTreeNode, data: PreferenceDataProperty): Preference.LeafNode {
184
148
  const { group } = Preference.TreeNode.getGroupAndIdFromNodeId(preferencesGroup.id);
185
- const newNode = {
149
+ const newNode: Preference.LeafNode = {
186
150
  id: `${group}@${property}`,
187
151
  preferenceId: property,
188
152
  parent: preferencesGroup,
@@ -193,8 +157,8 @@ export class PreferenceTreeGenerator {
193
157
  return newNode;
194
158
  }
195
159
 
196
- protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode): Preference.CompositeTreeNode {
197
- const newNode = {
160
+ protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode, label?: string): Preference.CompositeTreeNode {
161
+ const newNode: Preference.CompositeTreeNode = {
198
162
  id: `${group}@${id}`,
199
163
  visible: true,
200
164
  parent: root,
@@ -202,6 +166,7 @@ export class PreferenceTreeGenerator {
202
166
  expanded: false,
203
167
  selected: false,
204
168
  depth: 0,
169
+ label
205
170
  };
206
171
  const isTopLevel = Preference.TreeNode.isTopLevel(newNode);
207
172
  if (!isTopLevel) {
@@ -213,14 +178,12 @@ export class PreferenceTreeGenerator {
213
178
  return newNode;
214
179
  }
215
180
 
216
- getCustomLabelFor(id: string): string | undefined {
217
- return this.topLevelCategories.get(id);
218
- }
219
-
220
- protected getOrCreatePreferencesGroup(id: string, group: string, root: CompositeTreeNode, groups: Map<string, Preference.CompositeTreeNode>): Preference.CompositeTreeNode {
181
+ protected getOrCreatePreferencesGroup(
182
+ id: string, group: string, root: CompositeTreeNode, groups: Map<string, Preference.CompositeTreeNode>, label?: string
183
+ ): Preference.CompositeTreeNode {
221
184
  const existingGroup = groups.get(id);
222
185
  if (existingGroup) { return existingGroup; }
223
- const newNode = this.createPreferencesGroup(id, group, root);
186
+ const newNode = this.createPreferencesGroup(id, group, root, label);
224
187
  groups.set(id, newNode);
225
188
  return newNode;
226
189
  };
@@ -28,6 +28,7 @@ import { PreferenceTreeGenerator } from './preference-tree-generator';
28
28
  import { PreferenceTreeLabelProvider } from './preference-tree-label-provider';
29
29
  import { Preference } from './preference-types';
30
30
  import { SelectableTreeNode } from '@theia/core/lib/browser';
31
+ import { PreferenceLayoutProvider } from './preference-layout';
31
32
 
32
33
  disableJSDOM();
33
34
 
@@ -37,6 +38,7 @@ describe('preference-tree-label-provider', () => {
37
38
 
38
39
  beforeEach(() => {
39
40
  const container = new Container();
41
+ container.bind(PreferenceLayoutProvider).toSelf().inSingletonScope();
40
42
  container.bind<any>(PreferenceTreeGenerator).toConstantValue({ getCustomLabelFor: () => { } });
41
43
  preferenceTreeLabelProvider = container.resolve(PreferenceTreeLabelProvider);
42
44
  });
@@ -14,21 +14,35 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { injectable, inject } from '@theia/core/shared/inversify';
17
+ import { inject, injectable } from '@theia/core/shared/inversify';
18
18
  import { LabelProviderContribution, TreeNode } from '@theia/core/lib/browser';
19
19
  import { Preference } from './preference-types';
20
- import { PreferenceTreeGenerator } from './preference-tree-generator';
20
+ import { PreferenceLayoutProvider } from './preference-layout';
21
+
21
22
  @injectable()
22
23
  export class PreferenceTreeLabelProvider implements LabelProviderContribution {
23
- @inject(PreferenceTreeGenerator) protected readonly treeGenerator: PreferenceTreeGenerator;
24
+
25
+ @inject(PreferenceLayoutProvider)
26
+ protected readonly layoutProvider: PreferenceLayoutProvider;
24
27
 
25
28
  canHandle(element: object): number {
26
29
  return TreeNode.is(element) && Preference.TreeNode.is(element) ? 150 : 0;
27
30
  }
28
31
 
29
32
  getName(node: Preference.TreeNode): string {
33
+ if (Preference.CompositeTreeNode.is(node) && node.label) {
34
+ return node.label;
35
+ }
30
36
  const { id } = Preference.TreeNode.getGroupAndIdFromNodeId(node.id);
31
- return this.formatString(this.treeGenerator.getCustomLabelFor(id) ?? id.split('.').pop()!);
37
+ const layouts = this.layoutProvider.getLayout();
38
+ const layout = layouts.find(e => e.id === id);
39
+ if (layout) {
40
+ return layout.label;
41
+ } else {
42
+ const labels = id.split('.');
43
+ const groupName = labels[labels.length - 1];
44
+ return this.formatString(groupName);
45
+ }
32
46
  }
33
47
 
34
48
  getPrefix(node: Preference.TreeNode, fullPath = false): string | undefined {
@@ -18,7 +18,8 @@ import {
18
18
  PreferenceDataProperty,
19
19
  PreferenceScope,
20
20
  TreeNode as BaseTreeNode,
21
- CompositeTreeNode as BaseCompositeTreeNode,
21
+ ExpandableTreeNode,
22
+ SelectableTreeNode,
22
23
  PreferenceInspection,
23
24
  CommonCommands,
24
25
  } from '@theia/core/lib/browser';
@@ -58,8 +59,13 @@ export namespace Preference {
58
59
  };
59
60
  }
60
61
 
61
- export interface CompositeTreeNode extends BaseCompositeTreeNode {
62
+ export interface CompositeTreeNode extends ExpandableTreeNode, SelectableTreeNode {
62
63
  depth: number;
64
+ label?: string;
65
+ }
66
+
67
+ export namespace CompositeTreeNode {
68
+ export const is = (node: TreeNode): node is CompositeTreeNode => !LeafNode.is(node);
63
69
  }
64
70
 
65
71
  export interface LeafNode extends BaseTreeNode {
@@ -33,9 +33,9 @@ import { BaseWidget, DEFAULT_SCROLL_OPTIONS } from '@theia/core/lib/browser/widg
33
33
  import { PreferenceTreeModel, PreferenceFilterChangeEvent, PreferenceFilterChangeSource } from '../preference-tree-model';
34
34
  import { PreferenceNodeRendererFactory, GeneralPreferenceNodeRenderer } from './components/preference-node-renderer';
35
35
  import { Preference } from '../util/preference-types';
36
- import { COMMONLY_USED_SECTION_PREFIX } from '../util/preference-tree-generator';
37
36
  import { PreferencesScopeTabBar } from './preference-scope-tabbar-widget';
38
37
  import { PreferenceNodeRendererCreatorRegistry } from './components/preference-node-renderer-creator';
38
+ import { COMMONLY_USED_SECTION_PREFIX } from '../util/preference-layout';
39
39
 
40
40
  export interface PreferencesEditorState {
41
41
  firstVisibleChildID: string,
@@ -78,6 +78,7 @@ export class PreferencesWidget extends Panel implements StatefulWidget {
78
78
  protected init(): void {
79
79
  this.id = PreferencesWidget.ID;
80
80
  this.title.label = PreferencesWidget.LABEL;
81
+ this.title.caption = PreferencesWidget.LABEL;
81
82
  this.title.closable = true;
82
83
  this.addClass('theia-settings-container');
83
84
  this.title.iconClass = codicon('settings');