Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/preferences/browser/tocTree.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as DOM from '../../../../base/browser/dom.js';
7
import * as domStylesheetsJs from '../../../../base/browser/domStylesheets.js';
8
import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';
9
import { DefaultStyleController, IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js';
10
import { RenderIndentGuides } from '../../../../base/browser/ui/tree/abstractTree.js';
11
import { ITreeElement, ITreeNode, ITreeRenderer } from '../../../../base/browser/ui/tree/tree.js';
12
import { Iterable } from '../../../../base/common/iterator.js';
13
import { DisposableStore } from '../../../../base/common/lifecycle.js';
14
import { localize } from '../../../../nls.js';
15
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
16
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
17
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
18
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
19
import { IListService, IWorkbenchObjectTreeOptions, WorkbenchObjectTree } from '../../../../platform/list/browser/listService.js';
20
import { getListStyles } from '../../../../platform/theme/browser/defaultStyles.js';
21
import { editorBackground, focusBorder } from '../../../../platform/theme/common/colorRegistry.js';
22
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
23
import { settingsHeaderForeground, settingsHeaderHoverForeground } from '../common/settingsEditorColorRegistry.js';
24
import { SettingsTreeFilter } from './settingsTree.js';
25
import { ISettingsEditorViewState, SearchResultModel, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeSettingElement } from './settingsTreeModels.js';
26
27
const $ = DOM.$;
28
29
export class TOCTreeModel {
30
31
private _currentSearchModel: SearchResultModel | null = null;
32
private _settingsTreeRoot!: SettingsTreeGroupElement;
33
34
constructor(
35
private _viewState: ISettingsEditorViewState,
36
@IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService
37
) {
38
}
39
40
get settingsTreeRoot(): SettingsTreeGroupElement {
41
return this._settingsTreeRoot;
42
}
43
44
set settingsTreeRoot(value: SettingsTreeGroupElement) {
45
this._settingsTreeRoot = value;
46
this.update();
47
}
48
49
get currentSearchModel(): SearchResultModel | null {
50
return this._currentSearchModel;
51
}
52
53
set currentSearchModel(model: SearchResultModel | null) {
54
this._currentSearchModel = model;
55
this.update();
56
}
57
58
get children(): SettingsTreeElement[] {
59
return this._settingsTreeRoot.children;
60
}
61
62
update(): void {
63
if (this._settingsTreeRoot) {
64
this.updateGroupCount(this._settingsTreeRoot);
65
}
66
}
67
68
private updateGroupCount(group: SettingsTreeGroupElement): void {
69
group.children.forEach(child => {
70
if (child instanceof SettingsTreeGroupElement) {
71
this.updateGroupCount(child);
72
}
73
});
74
75
const childCount = group.children
76
.filter(child => child instanceof SettingsTreeGroupElement)
77
.reduce((acc, cur) => acc + (<SettingsTreeGroupElement>cur).count!, 0);
78
79
group.count = childCount + this.getGroupCount(group);
80
}
81
82
private getGroupCount(group: SettingsTreeGroupElement): number {
83
return group.children.filter(child => {
84
if (!(child instanceof SettingsTreeSettingElement)) {
85
return false;
86
}
87
88
if (this._currentSearchModel && !this._currentSearchModel.root.containsSetting(child.setting.key)) {
89
return false;
90
}
91
92
// Check everything that the SettingsFilter checks except whether it's filtered by a category
93
const isRemote = !!this.environmentService.remoteAuthority;
94
return child.matchesScope(this._viewState.settingsTarget, isRemote) &&
95
child.matchesAllTags(this._viewState.tagFilters) &&
96
child.matchesAnyFeature(this._viewState.featureFilters) &&
97
child.matchesAnyExtension(this._viewState.extensionFilters) &&
98
child.matchesAnyId(this._viewState.idFilters);
99
}).length;
100
}
101
}
102
103
const TOC_ENTRY_TEMPLATE_ID = 'settings.toc.entry';
104
105
interface ITOCEntryTemplate {
106
labelElement: HTMLElement;
107
countElement: HTMLElement;
108
elementDisposables: DisposableStore;
109
}
110
111
export class TOCRenderer implements ITreeRenderer<SettingsTreeGroupElement, never, ITOCEntryTemplate> {
112
113
templateId = TOC_ENTRY_TEMPLATE_ID;
114
115
constructor(private readonly _hoverService: IHoverService) {
116
}
117
118
renderTemplate(container: HTMLElement): ITOCEntryTemplate {
119
return {
120
labelElement: DOM.append(container, $('.settings-toc-entry')),
121
countElement: DOM.append(container, $('.settings-toc-count')),
122
elementDisposables: new DisposableStore()
123
};
124
}
125
126
renderElement(node: ITreeNode<SettingsTreeGroupElement>, index: number, template: ITOCEntryTemplate): void {
127
template.elementDisposables.clear();
128
129
const element = node.element;
130
const count = element.count;
131
const label = element.label;
132
133
template.labelElement.textContent = label;
134
template.elementDisposables.add(this._hoverService.setupDelayedHover(template.labelElement, { content: label }));
135
136
if (count) {
137
template.countElement.textContent = ` (${count})`;
138
} else {
139
template.countElement.textContent = '';
140
}
141
}
142
143
disposeTemplate(templateData: ITOCEntryTemplate): void {
144
templateData.elementDisposables.dispose();
145
}
146
}
147
148
class TOCTreeDelegate implements IListVirtualDelegate<SettingsTreeElement> {
149
getTemplateId(element: SettingsTreeElement): string {
150
return TOC_ENTRY_TEMPLATE_ID;
151
}
152
153
getHeight(element: SettingsTreeElement): number {
154
return 22;
155
}
156
}
157
158
export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement, tree: TOCTree): Iterable<ITreeElement<SettingsTreeGroupElement>> {
159
const groupChildren = <SettingsTreeGroupElement[]>model.children.filter(c => c instanceof SettingsTreeGroupElement);
160
161
return Iterable.map(groupChildren, g => {
162
const hasGroupChildren = g.children.some(c => c instanceof SettingsTreeGroupElement);
163
164
return {
165
element: g,
166
collapsed: undefined,
167
collapsible: hasGroupChildren,
168
children: g instanceof SettingsTreeGroupElement ?
169
createTOCIterator(g, tree) :
170
undefined
171
};
172
});
173
}
174
175
class SettingsAccessibilityProvider implements IListAccessibilityProvider<SettingsTreeGroupElement> {
176
getWidgetAriaLabel(): string {
177
return localize({
178
key: 'settingsTOC',
179
comment: ['A label for the table of contents for the full settings list']
180
},
181
"Settings Table of Contents");
182
}
183
184
getAriaLabel(element: SettingsTreeElement): string {
185
if (!element) {
186
return '';
187
}
188
189
if (element instanceof SettingsTreeGroupElement) {
190
return localize('groupRowAriaLabel', "{0}, group", element.label);
191
}
192
193
return '';
194
}
195
196
getAriaLevel(element: SettingsTreeGroupElement): number {
197
let i = 1;
198
while (element instanceof SettingsTreeGroupElement && element.parent) {
199
i++;
200
element = element.parent;
201
}
202
203
return i;
204
}
205
}
206
207
export class TOCTree extends WorkbenchObjectTree<SettingsTreeGroupElement> {
208
constructor(
209
container: HTMLElement,
210
viewState: ISettingsEditorViewState,
211
@IContextKeyService contextKeyService: IContextKeyService,
212
@IListService listService: IListService,
213
@IConfigurationService configurationService: IConfigurationService,
214
@IHoverService hoverService: IHoverService,
215
@IInstantiationService instantiationService: IInstantiationService,
216
) {
217
// test open mode
218
219
const filter = instantiationService.createInstance(SettingsTreeFilter, viewState);
220
const options: IWorkbenchObjectTreeOptions<SettingsTreeGroupElement, void> = {
221
filter,
222
multipleSelectionSupport: false,
223
identityProvider: {
224
getId(e) {
225
return e.id;
226
}
227
},
228
styleController: id => new DefaultStyleController(domStylesheetsJs.createStyleSheet(container), id),
229
accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider),
230
collapseByDefault: true,
231
horizontalScrolling: false,
232
hideTwistiesOfChildlessElements: true,
233
renderIndentGuides: RenderIndentGuides.None
234
};
235
236
super(
237
'SettingsTOC',
238
container,
239
new TOCTreeDelegate(),
240
[new TOCRenderer(hoverService)],
241
options,
242
instantiationService,
243
contextKeyService,
244
listService,
245
configurationService,
246
);
247
248
this.style(getListStyles({
249
listBackground: editorBackground,
250
listFocusOutline: focusBorder,
251
listActiveSelectionBackground: editorBackground,
252
listActiveSelectionForeground: settingsHeaderForeground,
253
listFocusAndSelectionBackground: editorBackground,
254
listFocusAndSelectionForeground: settingsHeaderForeground,
255
listFocusBackground: editorBackground,
256
listFocusForeground: settingsHeaderHoverForeground,
257
listHoverForeground: settingsHeaderHoverForeground,
258
listHoverBackground: editorBackground,
259
listInactiveSelectionBackground: editorBackground,
260
listInactiveSelectionForeground: settingsHeaderForeground,
261
listInactiveFocusBackground: editorBackground,
262
listInactiveFocusOutline: editorBackground,
263
treeIndentGuidesStroke: undefined,
264
treeInactiveIndentGuidesStroke: undefined
265
}));
266
}
267
}
268
269