@theia/preferences 1.53.0-next.5 → 1.53.0-next.55

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 (74) hide show
  1. package/README.md +81 -81
  2. package/lib/browser/preference-tree-model.d.ts.map +1 -1
  3. package/lib/browser/preference-tree-model.js +5 -2
  4. package/lib/browser/preference-tree-model.js.map +1 -1
  5. package/lib/browser/util/preference-layout.d.ts.map +1 -1
  6. package/lib/browser/util/preference-layout.js +4 -0
  7. package/lib/browser/util/preference-layout.js.map +1 -1
  8. package/lib/browser/util/preference-tree-generator.d.ts +14 -2
  9. package/lib/browser/util/preference-tree-generator.d.ts.map +1 -1
  10. package/lib/browser/util/preference-tree-generator.js +89 -26
  11. package/lib/browser/util/preference-tree-generator.js.map +1 -1
  12. package/lib/browser/util/preference-tree-label-provider.d.ts.map +1 -1
  13. package/lib/browser/util/preference-tree-label-provider.js +4 -11
  14. package/lib/browser/util/preference-tree-label-provider.js.map +1 -1
  15. package/lib/browser/util/preference-types.d.ts +4 -2
  16. package/lib/browser/util/preference-types.d.ts.map +1 -1
  17. package/lib/browser/util/preference-types.js.map +1 -1
  18. package/lib/browser/views/preference-tree-widget.d.ts +2 -0
  19. package/lib/browser/views/preference-tree-widget.d.ts.map +1 -1
  20. package/lib/browser/views/preference-tree-widget.js +10 -1
  21. package/lib/browser/views/preference-tree-widget.js.map +1 -1
  22. package/package.json +9 -9
  23. package/src/browser/abstract-resource-preference-provider.spec.ts +95 -95
  24. package/src/browser/abstract-resource-preference-provider.ts +232 -232
  25. package/src/browser/folder-preference-provider.ts +58 -58
  26. package/src/browser/folders-preferences-provider.ts +244 -244
  27. package/src/browser/index.ts +23 -23
  28. package/src/browser/monaco-jsonc-editor.ts +67 -67
  29. package/src/browser/package.spec.ts +28 -28
  30. package/src/browser/preference-bindings.ts +65 -65
  31. package/src/browser/preference-frontend-contribution.ts +38 -38
  32. package/src/browser/preference-frontend-module.ts +66 -66
  33. package/src/browser/preference-open-handler.ts +53 -53
  34. package/src/browser/preference-transaction-manager.ts +287 -287
  35. package/src/browser/preference-tree-model.ts +260 -257
  36. package/src/browser/preferences-contribution.ts +263 -263
  37. package/src/browser/preferences-json-schema-contribution.ts +86 -86
  38. package/src/browser/preferences-monaco-contribution.ts +27 -27
  39. package/src/browser/section-preference-provider.ts +83 -83
  40. package/src/browser/style/index.css +506 -506
  41. package/src/browser/style/preference-array.css +94 -94
  42. package/src/browser/style/preference-context-menu.css +74 -74
  43. package/src/browser/style/preference-file.css +31 -31
  44. package/src/browser/style/preference-object.css +49 -49
  45. package/src/browser/style/search-input.css +66 -66
  46. package/src/browser/user-configs-preference-provider.ts +127 -127
  47. package/src/browser/user-preference-provider.ts +35 -35
  48. package/src/browser/util/preference-layout.ts +381 -377
  49. package/src/browser/util/preference-scope-command-manager.ts +75 -75
  50. package/src/browser/util/preference-tree-generator.ts +260 -190
  51. package/src/browser/util/preference-tree-label-provider.spec.ts +110 -110
  52. package/src/browser/util/preference-tree-label-provider.ts +72 -78
  53. package/src/browser/util/preference-types.ts +177 -175
  54. package/src/browser/views/components/preference-array-input.ts +174 -174
  55. package/src/browser/views/components/preference-boolean-input.ts +69 -69
  56. package/src/browser/views/components/preference-file-input.ts +104 -104
  57. package/src/browser/views/components/preference-json-input.ts +78 -78
  58. package/src/browser/views/components/preference-markdown-renderer.ts +68 -68
  59. package/src/browser/views/components/preference-node-renderer-creator.ts +141 -141
  60. package/src/browser/views/components/preference-node-renderer.ts +477 -477
  61. package/src/browser/views/components/preference-number-input.ts +147 -147
  62. package/src/browser/views/components/preference-select-input.ts +131 -131
  63. package/src/browser/views/components/preference-string-input.ts +76 -76
  64. package/src/browser/views/preference-editor-widget.ts +349 -349
  65. package/src/browser/views/preference-scope-tabbar-widget.tsx +344 -344
  66. package/src/browser/views/preference-searchbar-widget.tsx +183 -183
  67. package/src/browser/views/preference-tree-widget.tsx +102 -93
  68. package/src/browser/views/preference-widget-bindings.ts +102 -102
  69. package/src/browser/views/preference-widget.tsx +118 -118
  70. package/src/browser/workspace-file-preference-provider.ts +100 -100
  71. package/src/browser/workspace-preference-provider.ts +134 -134
  72. package/src/common/cli-preferences.ts +22 -22
  73. package/src/node/preference-backend-module.ts +33 -33
  74. package/src/node/preference-cli-contribution.ts +48 -48
@@ -1,75 +1,75 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2018 Red Hat, Inc. 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 { injectable, inject } from '@theia/core/shared/inversify';
18
- import { PreferenceScope, LabelProvider, codicon } from '@theia/core/lib/browser';
19
- import { FileStat } from '@theia/filesystem/lib/common/files';
20
- import { CommandRegistry, MenuModelRegistry, Command } from '@theia/core/lib/common';
21
- import { Preference, PreferenceMenus } from './preference-types';
22
-
23
- /**
24
- * @deprecated since 1.17.0 moved to PreferenceMenus namespace.
25
- */
26
- export const FOLDER_SCOPE_MENU_PATH = PreferenceMenus.FOLDER_SCOPE_MENU_PATH;
27
-
28
- /**
29
- * @deprecated since 1.17.0. This work is now done in the PreferenceScopeTabbarWidget.
30
- */
31
- @injectable()
32
- export class PreferenceScopeCommandManager {
33
- @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
34
- @inject(MenuModelRegistry) protected readonly menuModelRegistry: MenuModelRegistry;
35
- @inject(LabelProvider) protected readonly labelProvider: LabelProvider;
36
-
37
- protected foldersAsCommands: Command[] = [];
38
-
39
- createFolderWorkspacesMenu(
40
- folderWorkspaces: FileStat[],
41
- currentFolderURI?: string,
42
- ): void {
43
- this.foldersAsCommands.forEach(folderCommand => {
44
- this.menuModelRegistry.unregisterMenuAction(folderCommand, FOLDER_SCOPE_MENU_PATH);
45
- this.commandRegistry.unregisterCommand(folderCommand);
46
- });
47
- this.foldersAsCommands.length = 0;
48
-
49
- folderWorkspaces.forEach(folderWorkspace => {
50
- const folderLabel = this.labelProvider.getName(folderWorkspace.resource);
51
-
52
- const iconClass = currentFolderURI === folderWorkspace.resource.toString() ? codicon('pass') : '';
53
- const newFolderAsCommand = {
54
- id: `preferenceScopeCommand:${folderWorkspace.resource.toString()}`,
55
- label: folderLabel,
56
- iconClass: iconClass
57
- };
58
-
59
- this.foldersAsCommands.push(newFolderAsCommand);
60
-
61
- this.commandRegistry.registerCommand(newFolderAsCommand, {
62
- isVisible: (callback, check) => check === 'from-tabbar',
63
- isEnabled: (callback, check) => check === 'from-tabbar',
64
- execute: (callback: (scopeDetails: Preference.SelectedScopeDetails) => void) => {
65
- callback({ scope: PreferenceScope.Folder, uri: folderWorkspace.resource.toString(), activeScopeIsFolder: true });
66
- }
67
- });
68
-
69
- this.menuModelRegistry.registerMenuAction(FOLDER_SCOPE_MENU_PATH, {
70
- commandId: newFolderAsCommand.id,
71
- label: newFolderAsCommand.label
72
- });
73
- });
74
- }
75
- }
1
+ // *****************************************************************************
2
+ // Copyright (C) 2018 Red Hat, Inc. 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 { injectable, inject } from '@theia/core/shared/inversify';
18
+ import { PreferenceScope, LabelProvider, codicon } from '@theia/core/lib/browser';
19
+ import { FileStat } from '@theia/filesystem/lib/common/files';
20
+ import { CommandRegistry, MenuModelRegistry, Command } from '@theia/core/lib/common';
21
+ import { Preference, PreferenceMenus } from './preference-types';
22
+
23
+ /**
24
+ * @deprecated since 1.17.0 moved to PreferenceMenus namespace.
25
+ */
26
+ export const FOLDER_SCOPE_MENU_PATH = PreferenceMenus.FOLDER_SCOPE_MENU_PATH;
27
+
28
+ /**
29
+ * @deprecated since 1.17.0. This work is now done in the PreferenceScopeTabbarWidget.
30
+ */
31
+ @injectable()
32
+ export class PreferenceScopeCommandManager {
33
+ @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
34
+ @inject(MenuModelRegistry) protected readonly menuModelRegistry: MenuModelRegistry;
35
+ @inject(LabelProvider) protected readonly labelProvider: LabelProvider;
36
+
37
+ protected foldersAsCommands: Command[] = [];
38
+
39
+ createFolderWorkspacesMenu(
40
+ folderWorkspaces: FileStat[],
41
+ currentFolderURI?: string,
42
+ ): void {
43
+ this.foldersAsCommands.forEach(folderCommand => {
44
+ this.menuModelRegistry.unregisterMenuAction(folderCommand, FOLDER_SCOPE_MENU_PATH);
45
+ this.commandRegistry.unregisterCommand(folderCommand);
46
+ });
47
+ this.foldersAsCommands.length = 0;
48
+
49
+ folderWorkspaces.forEach(folderWorkspace => {
50
+ const folderLabel = this.labelProvider.getName(folderWorkspace.resource);
51
+
52
+ const iconClass = currentFolderURI === folderWorkspace.resource.toString() ? codicon('pass') : '';
53
+ const newFolderAsCommand = {
54
+ id: `preferenceScopeCommand:${folderWorkspace.resource.toString()}`,
55
+ label: folderLabel,
56
+ iconClass: iconClass
57
+ };
58
+
59
+ this.foldersAsCommands.push(newFolderAsCommand);
60
+
61
+ this.commandRegistry.registerCommand(newFolderAsCommand, {
62
+ isVisible: (callback, check) => check === 'from-tabbar',
63
+ isEnabled: (callback, check) => check === 'from-tabbar',
64
+ execute: (callback: (scopeDetails: Preference.SelectedScopeDetails) => void) => {
65
+ callback({ scope: PreferenceScope.Folder, uri: folderWorkspace.resource.toString(), activeScopeIsFolder: true });
66
+ }
67
+ });
68
+
69
+ this.menuModelRegistry.registerMenuAction(FOLDER_SCOPE_MENU_PATH, {
70
+ commandId: newFolderAsCommand.id,
71
+ label: newFolderAsCommand.label
72
+ });
73
+ });
74
+ }
75
+ }
@@ -1,190 +1,260 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2020 Ericsson 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 { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
18
- import { CompositeTreeNode, PreferenceSchemaProvider, OVERRIDE_PROPERTY_PATTERN, PreferenceDataProperty } from '@theia/core/lib/browser';
19
- import { PreferenceConfigurations } from '@theia/core/lib/browser/preferences/preference-configurations';
20
- import { Emitter } from '@theia/core';
21
- import debounce = require('@theia/core/shared/lodash.debounce');
22
- import { Preference } from './preference-types';
23
- import { COMMONLY_USED_SECTION_PREFIX, PreferenceLayoutProvider } from './preference-layout';
24
-
25
- @injectable()
26
- export class PreferenceTreeGenerator {
27
-
28
- @inject(PreferenceSchemaProvider) protected readonly schemaProvider: PreferenceSchemaProvider;
29
- @inject(PreferenceConfigurations) protected readonly preferenceConfigs: PreferenceConfigurations;
30
- @inject(PreferenceLayoutProvider) protected readonly layoutProvider: PreferenceLayoutProvider;
31
-
32
- protected _root: CompositeTreeNode;
33
-
34
- protected readonly onSchemaChangedEmitter = new Emitter<CompositeTreeNode>();
35
- readonly onSchemaChanged = this.onSchemaChangedEmitter.event;
36
- protected readonly defaultTopLevelCategory = 'extensions';
37
-
38
- get root(): CompositeTreeNode {
39
- return this._root ?? this.generateTree();
40
- }
41
-
42
- @postConstruct()
43
- protected init(): void {
44
- this.doInit();
45
- }
46
-
47
- protected async doInit(): Promise<void> {
48
- await this.schemaProvider.ready;
49
- this.schemaProvider.onDidPreferenceSchemaChanged(() => this.handleChangedSchema());
50
- this.handleChangedSchema();
51
- }
52
-
53
- generateTree(): CompositeTreeNode {
54
- const preferencesSchema = this.schemaProvider.getCombinedSchema();
55
- const propertyNames = Object.keys(preferencesSchema.properties);
56
- const groups = new Map<string, Preference.CompositeTreeNode>();
57
- const root = this.createRootNode();
58
-
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);
64
- }
65
- for (const preference of commonlyUsedLayout.settings ?? []) {
66
- if (preference in preferencesSchema.properties) {
67
- this.createLeafNode(preference, commonlyUsed, preferencesSchema.properties[preference]);
68
- }
69
- }
70
- for (const propertyName of propertyNames) {
71
- const property = preferencesSchema.properties[propertyName];
72
- if (!this.preferenceConfigs.isSectionName(propertyName) && !OVERRIDE_PROPERTY_PATTERN.test(propertyName) && !property.deprecationMessage) {
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);
79
- const subgroupID = [groupID, subgroupName].join('.');
80
- const toplevelParent = this.getOrCreatePreferencesGroup(groupID, groupID, root, groups);
81
- const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups, property.title ?? layoutItem?.label);
82
- this.createLeafNode(propertyName, immediateParent || toplevelParent, property);
83
- }
84
- }
85
-
86
- for (const group of groups.values()) {
87
- if (group.id !== `${COMMONLY_USED_SECTION_PREFIX}@${COMMONLY_USED_SECTION_PREFIX}`) {
88
- (group.children as Preference.TreeNode[]).sort((a, b) => {
89
- const aIsComposite = CompositeTreeNode.is(a);
90
- const bIsComposite = CompositeTreeNode.is(b);
91
- if (aIsComposite && !bIsComposite) {
92
- return 1;
93
- }
94
- if (bIsComposite && !aIsComposite) {
95
- return -1;
96
- }
97
- return a.id.localeCompare(b.id);
98
- });
99
- }
100
- }
101
-
102
- this._root = root;
103
- return root;
104
- };
105
-
106
- getNodeId(preferenceId: string): string {
107
- const expectedGroup = this.getGroupName(preferenceId.split('.'));
108
- const expectedId = `${expectedGroup}@${preferenceId}`;
109
- return expectedId;
110
- }
111
-
112
- protected getGroupName(labels: string[]): string {
113
- const defaultGroup = labels[0];
114
- if (this.layoutProvider.hasCategory(defaultGroup)) {
115
- return defaultGroup;
116
- }
117
- return this.defaultTopLevelCategory;
118
- }
119
-
120
- protected getSubgroupName(labels: string[], computedGroupName: string): string | undefined {
121
- if (computedGroupName !== labels[0]) {
122
- return labels[0];
123
- } else if (labels.length > 1) {
124
- return labels[1];
125
- } else {
126
- return undefined;
127
- }
128
- }
129
-
130
- doHandleChangedSchema(): void {
131
- const newTree = this.generateTree();
132
- this.onSchemaChangedEmitter.fire(newTree);
133
- }
134
-
135
- handleChangedSchema = debounce(this.doHandleChangedSchema, 200);
136
-
137
- protected createRootNode(): CompositeTreeNode {
138
- return {
139
- id: 'root-node-id',
140
- name: '',
141
- parent: undefined,
142
- visible: true,
143
- children: []
144
- };
145
- }
146
-
147
- protected createLeafNode(property: string, preferencesGroup: Preference.CompositeTreeNode, data: PreferenceDataProperty): Preference.LeafNode {
148
- const { group } = Preference.TreeNode.getGroupAndIdFromNodeId(preferencesGroup.id);
149
- const newNode: Preference.LeafNode = {
150
- id: `${group}@${property}`,
151
- preferenceId: property,
152
- parent: preferencesGroup,
153
- preference: { data },
154
- depth: Preference.TreeNode.isTopLevel(preferencesGroup) ? 1 : 2,
155
- };
156
- CompositeTreeNode.addChild(preferencesGroup, newNode);
157
- return newNode;
158
- }
159
-
160
- protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode, label?: string): Preference.CompositeTreeNode {
161
- const newNode: Preference.CompositeTreeNode = {
162
- id: `${group}@${id}`,
163
- visible: true,
164
- parent: root,
165
- children: [],
166
- expanded: false,
167
- selected: false,
168
- depth: 0,
169
- label
170
- };
171
- const isTopLevel = Preference.TreeNode.isTopLevel(newNode);
172
- if (!isTopLevel) {
173
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
- delete (newNode as any).expanded;
175
- }
176
- newNode.depth = isTopLevel ? 0 : 1;
177
- CompositeTreeNode.addChild(root, newNode);
178
- return newNode;
179
- }
180
-
181
- protected getOrCreatePreferencesGroup(
182
- id: string, group: string, root: CompositeTreeNode, groups: Map<string, Preference.CompositeTreeNode>, label?: string
183
- ): Preference.CompositeTreeNode {
184
- const existingGroup = groups.get(id);
185
- if (existingGroup) { return existingGroup; }
186
- const newNode = this.createPreferencesGroup(id, group, root, label);
187
- groups.set(id, newNode);
188
- return newNode;
189
- };
190
- }
1
+ // *****************************************************************************
2
+ // Copyright (C) 2020 Ericsson 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 { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
18
+ import { CompositeTreeNode, PreferenceSchemaProvider, OVERRIDE_PROPERTY_PATTERN, PreferenceDataProperty } from '@theia/core/lib/browser';
19
+ import { PreferenceConfigurations } from '@theia/core/lib/browser/preferences/preference-configurations';
20
+ import { Emitter } from '@theia/core';
21
+ import debounce = require('@theia/core/shared/lodash.debounce');
22
+ import { Preference } from './preference-types';
23
+ import { COMMONLY_USED_SECTION_PREFIX, PreferenceLayoutProvider } from './preference-layout';
24
+
25
+ export interface CreatePreferencesGroupOptions {
26
+ id: string,
27
+ group: string,
28
+ root: CompositeTreeNode,
29
+ expanded?: boolean,
30
+ depth?: number,
31
+ label?: string
32
+ }
33
+
34
+ @injectable()
35
+ export class PreferenceTreeGenerator {
36
+
37
+ @inject(PreferenceSchemaProvider) protected readonly schemaProvider: PreferenceSchemaProvider;
38
+ @inject(PreferenceConfigurations) protected readonly preferenceConfigs: PreferenceConfigurations;
39
+ @inject(PreferenceLayoutProvider) protected readonly layoutProvider: PreferenceLayoutProvider;
40
+
41
+ protected _root: CompositeTreeNode;
42
+
43
+ protected readonly onSchemaChangedEmitter = new Emitter<CompositeTreeNode>();
44
+ readonly onSchemaChanged = this.onSchemaChangedEmitter.event;
45
+ protected readonly defaultTopLevelCategory = 'extensions';
46
+
47
+ get root(): CompositeTreeNode {
48
+ return this._root ?? this.generateTree();
49
+ }
50
+
51
+ @postConstruct()
52
+ protected init(): void {
53
+ this.doInit();
54
+ }
55
+
56
+ protected async doInit(): Promise<void> {
57
+ await this.schemaProvider.ready;
58
+ this.schemaProvider.onDidPreferenceSchemaChanged(() => this.handleChangedSchema());
59
+ this.handleChangedSchema();
60
+ }
61
+
62
+ generateTree(): CompositeTreeNode {
63
+ const preferencesSchema = this.schemaProvider.getCombinedSchema();
64
+ const propertyNames = Object.keys(preferencesSchema.properties);
65
+ const groups = new Map<string, Preference.CompositeTreeNode>();
66
+ const root = this.createRootNode();
67
+
68
+ const commonlyUsedLayout = this.layoutProvider.getCommonlyUsedLayout();
69
+ const commonlyUsed = this.getOrCreatePreferencesGroup({
70
+ id: commonlyUsedLayout.id,
71
+ group: commonlyUsedLayout.id,
72
+ root,
73
+ groups,
74
+ label: commonlyUsedLayout.label
75
+ });
76
+
77
+ for (const layout of this.layoutProvider.getLayout()) {
78
+ this.getOrCreatePreferencesGroup({
79
+ id: layout.id,
80
+ group: layout.id,
81
+ root,
82
+ groups,
83
+ label: layout.label
84
+ });
85
+ }
86
+ for (const preference of commonlyUsedLayout.settings ?? []) {
87
+ if (preference in preferencesSchema.properties) {
88
+ this.createLeafNode(preference, commonlyUsed, preferencesSchema.properties[preference]);
89
+ }
90
+ }
91
+ for (const propertyName of propertyNames) {
92
+ const property = preferencesSchema.properties[propertyName];
93
+ if (!this.preferenceConfigs.isSectionName(propertyName) && !OVERRIDE_PROPERTY_PATTERN.test(propertyName) && !property.deprecationMessage) {
94
+ if (property.owner) {
95
+ this.createPluginLeafNode(propertyName, property, root, groups);
96
+ } else {
97
+ this.createBuiltinLeafNode(propertyName, property, root, groups);
98
+ }
99
+ }
100
+ }
101
+
102
+ for (const group of groups.values()) {
103
+ if (group.id !== `${COMMONLY_USED_SECTION_PREFIX}@${COMMONLY_USED_SECTION_PREFIX}`) {
104
+ (group.children as Preference.TreeNode[]).sort((a, b) => {
105
+ const aIsComposite = CompositeTreeNode.is(a);
106
+ const bIsComposite = CompositeTreeNode.is(b);
107
+ if (aIsComposite && !bIsComposite) {
108
+ return 1;
109
+ }
110
+ if (bIsComposite && !aIsComposite) {
111
+ return -1;
112
+ }
113
+ return a.id.localeCompare(b.id);
114
+ });
115
+ }
116
+ }
117
+
118
+ this._root = root;
119
+ return root;
120
+ };
121
+
122
+ protected createBuiltinLeafNode(name: string, property: PreferenceDataProperty, root: CompositeTreeNode, groups: Map<string, Preference.CompositeTreeNode>): void {
123
+ const layoutItem = this.layoutProvider.getLayoutForPreference(name);
124
+ const labels = layoutItem ? layoutItem.id.split('.') : name.split('.');
125
+ const groupID = this.getGroupName(labels);
126
+ const subgroupName = this.getSubgroupName(labels, groupID);
127
+ const subgroupID = [groupID, subgroupName].join('.');
128
+ const toplevelParent = this.getOrCreatePreferencesGroup({
129
+ id: groupID,
130
+ group: groupID,
131
+ root,
132
+ groups
133
+ });
134
+ const immediateParent = subgroupName ? this.getOrCreatePreferencesGroup({
135
+ id: subgroupID,
136
+ group: groupID,
137
+ root: toplevelParent,
138
+ groups,
139
+ label: layoutItem?.label
140
+ }) : undefined;
141
+ this.createLeafNode(name, immediateParent || toplevelParent, property);
142
+ }
143
+
144
+ protected createPluginLeafNode(name: string, property: PreferenceDataProperty, root: CompositeTreeNode, groups: Map<string, Preference.CompositeTreeNode>): void {
145
+ if (!property.owner) {
146
+ return;
147
+ }
148
+ const groupID = this.defaultTopLevelCategory;
149
+ const subgroupName = property.owner;
150
+ const subsubgroupName = property.group;
151
+ const hasGroup = Boolean(subsubgroupName);
152
+ const toplevelParent = this.getOrCreatePreferencesGroup({
153
+ id: groupID,
154
+ group: groupID,
155
+ root,
156
+ groups
157
+ });
158
+ const subgroupID = [groupID, subgroupName].join('.');
159
+ const subgroupParent = this.getOrCreatePreferencesGroup({
160
+ id: subgroupID,
161
+ group: groupID,
162
+ root: toplevelParent,
163
+ groups,
164
+ expanded: hasGroup,
165
+ label: subgroupName
166
+ });
167
+ const subsubgroupID = [groupID, subgroupName, subsubgroupName].join('.');
168
+ const subsubgroupParent = hasGroup ? this.getOrCreatePreferencesGroup({
169
+ id: subsubgroupID,
170
+ group: subgroupID,
171
+ root: subgroupParent,
172
+ groups,
173
+ depth: 2,
174
+ label: subsubgroupName
175
+ }) : undefined;
176
+ this.createLeafNode(name, subsubgroupParent || subgroupParent, property);
177
+ }
178
+
179
+ getNodeId(preferenceId: string): string {
180
+ const expectedGroup = this.getGroupName(preferenceId.split('.'));
181
+ const expectedId = `${expectedGroup}@${preferenceId}`;
182
+ return expectedId;
183
+ }
184
+
185
+ protected getGroupName(labels: string[]): string {
186
+ const defaultGroup = labels[0];
187
+ if (this.layoutProvider.hasCategory(defaultGroup)) {
188
+ return defaultGroup;
189
+ }
190
+ return this.defaultTopLevelCategory;
191
+ }
192
+
193
+ protected getSubgroupName(labels: string[], computedGroupName: string): string | undefined {
194
+ if (computedGroupName !== labels[0]) {
195
+ return labels[0];
196
+ } else if (labels.length > 1) {
197
+ return labels[1];
198
+ } else {
199
+ return undefined;
200
+ }
201
+ }
202
+
203
+ doHandleChangedSchema(): void {
204
+ const newTree = this.generateTree();
205
+ this.onSchemaChangedEmitter.fire(newTree);
206
+ }
207
+
208
+ handleChangedSchema = debounce(this.doHandleChangedSchema, 200);
209
+
210
+ protected createRootNode(): CompositeTreeNode {
211
+ return {
212
+ id: 'root-node-id',
213
+ name: '',
214
+ parent: undefined,
215
+ visible: true,
216
+ children: []
217
+ };
218
+ }
219
+
220
+ protected createLeafNode(property: string, preferencesGroup: Preference.CompositeTreeNode, data: PreferenceDataProperty): Preference.LeafNode {
221
+ const { group } = Preference.TreeNode.getGroupAndIdFromNodeId(preferencesGroup.id);
222
+ const newNode: Preference.LeafNode = {
223
+ id: `${group}@${property}`,
224
+ preferenceId: property,
225
+ parent: preferencesGroup,
226
+ preference: { data },
227
+ depth: Preference.TreeNode.isTopLevel(preferencesGroup) ? 1 : 2
228
+ };
229
+ CompositeTreeNode.addChild(preferencesGroup, newNode);
230
+ return newNode;
231
+ }
232
+
233
+ protected createPreferencesGroup(options: CreatePreferencesGroupOptions): Preference.CompositeTreeNode {
234
+ const newNode: Preference.CompositeTreeNode = {
235
+ id: `${options.group}@${options.id}`,
236
+ visible: true,
237
+ parent: options.root,
238
+ children: [],
239
+ expanded: false,
240
+ selected: false,
241
+ depth: 0,
242
+ label: options.label
243
+ };
244
+ const isTopLevel = Preference.TreeNode.isTopLevel(newNode);
245
+ if (!(options.expanded ?? isTopLevel)) {
246
+ delete newNode.expanded;
247
+ }
248
+ newNode.depth = options.depth ?? (isTopLevel ? 0 : 1);
249
+ CompositeTreeNode.addChild(options.root, newNode);
250
+ return newNode;
251
+ }
252
+
253
+ protected getOrCreatePreferencesGroup(options: CreatePreferencesGroupOptions & { groups: Map<string, Preference.CompositeTreeNode> }): Preference.CompositeTreeNode {
254
+ const existingGroup = options.groups.get(options.id);
255
+ if (existingGroup) { return existingGroup; }
256
+ const newNode = this.createPreferencesGroup(options);
257
+ options.groups.set(options.id, newNode);
258
+ return newNode;
259
+ };
260
+ }