@theia/preferences 1.50.1 → 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 (41) 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 +1 -0
  5. package/lib/browser/preference-tree-model.d.ts.map +1 -1
  6. package/lib/browser/preference-tree-model.js +10 -3
  7. package/lib/browser/preference-tree-model.js.map +1 -1
  8. package/lib/browser/util/preference-layout.d.ts +23 -0
  9. package/lib/browser/util/preference-layout.d.ts.map +1 -0
  10. package/lib/browser/util/preference-layout.js +365 -0
  11. package/lib/browser/util/preference-layout.js.map +1 -0
  12. package/lib/browser/util/preference-tree-generator.d.ts +4 -7
  13. package/lib/browser/util/preference-tree-generator.d.ts.map +1 -1
  14. package/lib/browser/util/preference-tree-generator.js +29 -62
  15. package/lib/browser/util/preference-tree-generator.js.map +1 -1
  16. package/lib/browser/util/preference-tree-label-provider.d.ts +2 -2
  17. package/lib/browser/util/preference-tree-label-provider.d.ts.map +1 -1
  18. package/lib/browser/util/preference-tree-label-provider.js +17 -6
  19. package/lib/browser/util/preference-tree-label-provider.js.map +1 -1
  20. package/lib/browser/util/preference-tree-label-provider.spec.js +2 -0
  21. package/lib/browser/util/preference-tree-label-provider.spec.js.map +1 -1
  22. package/lib/browser/util/preference-types.d.ts +6 -2
  23. package/lib/browser/util/preference-types.d.ts.map +1 -1
  24. package/lib/browser/util/preference-types.js +4 -0
  25. package/lib/browser/util/preference-types.js.map +1 -1
  26. package/lib/browser/views/preference-editor-widget.d.ts.map +1 -1
  27. package/lib/browser/views/preference-editor-widget.js +2 -2
  28. package/lib/browser/views/preference-editor-widget.js.map +1 -1
  29. package/lib/browser/views/preference-widget.d.ts.map +1 -1
  30. package/lib/browser/views/preference-widget.js +1 -0
  31. package/lib/browser/views/preference-widget.js.map +1 -1
  32. package/package.json +9 -9
  33. package/src/browser/preference-frontend-module.ts +2 -0
  34. package/src/browser/preference-tree-model.ts +10 -3
  35. package/src/browser/util/preference-layout.ts +377 -0
  36. package/src/browser/util/preference-tree-generator.ts +27 -63
  37. package/src/browser/util/preference-tree-label-provider.spec.ts +2 -0
  38. package/src/browser/util/preference-tree-label-provider.ts +18 -4
  39. package/src/browser/util/preference-types.ts +8 -2
  40. package/src/browser/views/preference-editor-widget.ts +1 -1
  41. package/src/browser/views/preference-widget.tsx +1 -0
@@ -30,11 +30,12 @@ import {
30
30
  } from '@theia/core/lib/browser';
31
31
  import { Emitter } from '@theia/core';
32
32
  import { PreferencesSearchbarWidget } from './views/preference-searchbar-widget';
33
- import { PreferenceTreeGenerator, COMMONLY_USED_SECTION_PREFIX } from './util/preference-tree-generator';
33
+ import { PreferenceTreeGenerator } from './util/preference-tree-generator';
34
34
  import * as fuzzy from '@theia/core/shared/fuzzy';
35
35
  import { PreferencesScopeTabBar } from './views/preference-scope-tabbar-widget';
36
36
  import { Preference } from './util/preference-types';
37
37
  import { Event } from '@theia/core/lib/common';
38
+ import { COMMONLY_USED_SECTION_PREFIX } from './util/preference-layout';
38
39
 
39
40
  export interface PreferenceTreeNodeProps extends NodeProps {
40
41
  visibleChildren: number;
@@ -67,6 +68,7 @@ export class PreferenceTreeModel extends TreeModelImpl {
67
68
 
68
69
  protected lastSearchedFuzzy: string = '';
69
70
  protected lastSearchedLiteral: string = '';
71
+ protected lastSearchedTags: string[] = [];
70
72
  protected _currentScope: number = Number(Preference.DEFAULT_SCOPE.scope);
71
73
  protected _isFiltered: boolean = false;
72
74
  protected _currentRows: Map<string, PreferenceTreeNodeRow> = new Map();
@@ -110,8 +112,10 @@ export class PreferenceTreeModel extends TreeModelImpl {
110
112
  this.updateFilteredRows(PreferenceFilterChangeSource.Scope);
111
113
  }),
112
114
  this.filterInput.onFilterChanged(newSearchTerm => {
113
- this.lastSearchedLiteral = newSearchTerm;
114
- this.lastSearchedFuzzy = newSearchTerm.replace(/\s/g, '');
115
+ this.lastSearchedTags = Array.from(newSearchTerm.matchAll(/@tag:([^\s]+)/g)).map(match => match[0].slice(5));
116
+ const newSearchTermWithoutTags = newSearchTerm.replace(/@tag:[^\s]+/g, '');
117
+ this.lastSearchedLiteral = newSearchTermWithoutTags;
118
+ this.lastSearchedFuzzy = newSearchTermWithoutTags.replace(/\s/g, '');
115
119
  this._isFiltered = newSearchTerm.length > 2;
116
120
  if (this.isFiltered) {
117
121
  this.expandAll();
@@ -183,6 +187,9 @@ export class PreferenceTreeModel extends TreeModelImpl {
183
187
  if (node.id.startsWith(COMMONLY_USED_SECTION_PREFIX)) {
184
188
  return false;
185
189
  }
190
+ if (!this.lastSearchedTags.every(tag => node.preference.data.tags?.includes(tag))) {
191
+ return false;
192
+ }
186
193
  return fuzzy.test(this.lastSearchedFuzzy, prefID) // search matches preference name.
187
194
  // search matches description. Fuzzy isn't ideal here because the score depends on the order of discovery.
188
195
  || (node.preference.data.description ?? '').includes(this.lastSearchedLiteral);
@@ -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,57 +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
- ['toolbar', 'features'],
71
- ['webview', 'features'],
72
- ['workspace', 'application'],
73
- ]);
74
36
  protected readonly defaultTopLevelCategory = 'extensions';
75
37
 
76
38
  get root(): CompositeTreeNode {
@@ -94,11 +56,13 @@ export class PreferenceTreeGenerator {
94
56
  const groups = new Map<string, Preference.CompositeTreeNode>();
95
57
  const root = this.createRootNode();
96
58
 
97
- for (const id of this.topLevelCategories.keys()) {
98
- 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);
99
64
  }
100
- const commonlyUsed = this.getOrCreatePreferencesGroup(COMMONLY_USED_SECTION_PREFIX, COMMONLY_USED_SECTION_PREFIX, root, groups);
101
- for (const preference of this.commonlyUsedPreferences) {
65
+ for (const preference of commonlyUsedLayout.settings ?? []) {
102
66
  if (preference in preferencesSchema.properties) {
103
67
  this.createLeafNode(preference, commonlyUsed, preferencesSchema.properties[preference]);
104
68
  }
@@ -106,12 +70,15 @@ export class PreferenceTreeGenerator {
106
70
  for (const propertyName of propertyNames) {
107
71
  const property = preferencesSchema.properties[propertyName];
108
72
  if (!this.preferenceConfigs.isSectionName(propertyName) && !OVERRIDE_PROPERTY_PATTERN.test(propertyName) && !property.deprecationMessage) {
109
- const labels = propertyName.split('.');
110
- const groupID = this.getGroupName(labels);
111
- 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);
112
79
  const subgroupID = [groupID, subgroupName].join('.');
113
80
  const toplevelParent = this.getOrCreatePreferencesGroup(groupID, groupID, root, groups);
114
- const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups);
81
+ const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups, property.title ?? layoutItem?.label);
115
82
  this.createLeafNode(propertyName, immediateParent || toplevelParent, property);
116
83
  }
117
84
  }
@@ -144,21 +111,19 @@ export class PreferenceTreeGenerator {
144
111
 
145
112
  protected getGroupName(labels: string[]): string {
146
113
  const defaultGroup = labels[0];
147
- if (this.topLevelCategories.has(defaultGroup)) {
114
+ if (this.layoutProvider.hasCategory(defaultGroup)) {
148
115
  return defaultGroup;
149
116
  }
150
- const assignedGroup = this.sectionAssignments.get(defaultGroup);
151
- if (assignedGroup) {
152
- return assignedGroup;
153
- }
154
117
  return this.defaultTopLevelCategory;
155
118
  }
156
119
 
157
120
  protected getSubgroupName(labels: string[], computedGroupName: string): string | undefined {
158
121
  if (computedGroupName !== labels[0]) {
159
122
  return labels[0];
160
- } else if (labels.length > 2) {
123
+ } else if (labels.length > 1) {
161
124
  return labels[1];
125
+ } else {
126
+ return undefined;
162
127
  }
163
128
  }
164
129
 
@@ -181,7 +146,7 @@ export class PreferenceTreeGenerator {
181
146
 
182
147
  protected createLeafNode(property: string, preferencesGroup: Preference.CompositeTreeNode, data: PreferenceDataProperty): Preference.LeafNode {
183
148
  const { group } = Preference.TreeNode.getGroupAndIdFromNodeId(preferencesGroup.id);
184
- const newNode = {
149
+ const newNode: Preference.LeafNode = {
185
150
  id: `${group}@${property}`,
186
151
  preferenceId: property,
187
152
  parent: preferencesGroup,
@@ -192,8 +157,8 @@ export class PreferenceTreeGenerator {
192
157
  return newNode;
193
158
  }
194
159
 
195
- protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode): Preference.CompositeTreeNode {
196
- const newNode = {
160
+ protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode, label?: string): Preference.CompositeTreeNode {
161
+ const newNode: Preference.CompositeTreeNode = {
197
162
  id: `${group}@${id}`,
198
163
  visible: true,
199
164
  parent: root,
@@ -201,6 +166,7 @@ export class PreferenceTreeGenerator {
201
166
  expanded: false,
202
167
  selected: false,
203
168
  depth: 0,
169
+ label
204
170
  };
205
171
  const isTopLevel = Preference.TreeNode.isTopLevel(newNode);
206
172
  if (!isTopLevel) {
@@ -212,14 +178,12 @@ export class PreferenceTreeGenerator {
212
178
  return newNode;
213
179
  }
214
180
 
215
- getCustomLabelFor(id: string): string | undefined {
216
- return this.topLevelCategories.get(id);
217
- }
218
-
219
- 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 {
220
184
  const existingGroup = groups.get(id);
221
185
  if (existingGroup) { return existingGroup; }
222
- const newNode = this.createPreferencesGroup(id, group, root);
186
+ const newNode = this.createPreferencesGroup(id, group, root, label);
223
187
  groups.set(id, newNode);
224
188
  return newNode;
225
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 {