Settings editor - implement scroll on right based on left

This commit is contained in:
Rob Lourens
2018-06-18 15:56:24 -07:00
parent efef790376
commit 0abbd7e4e1
4 changed files with 413 additions and 412 deletions
@@ -11,7 +11,6 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { Color } from 'vs/base/common/color';
import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors';
import { KeyCode } from 'vs/base/common/keyCodes';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree';
import { DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults';
@@ -31,10 +30,10 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions, IEditor } from 'vs/workbench/common/editor';
import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout';
import { ISettingsEditorViewState, SearchResultIdx, SearchResultModel, SettingsAccessibilityProvider, SettingsDataSource, SettingsRenderer, SettingsTreeController, SettingsTreeFilter, TreeElement, isTOCLeaf } from 'vs/workbench/parts/preferences/browser/settingsTree';
import { TOCDataSource, TOCRenderer } from 'vs/workbench/parts/preferences/browser/tocTree';
import { ISettingsEditorViewState, SearchResultIdx, SearchResultModel, SettingsAccessibilityProvider, SettingsDataSource, SettingsRenderer, SettingsTreeController, SettingsTreeElement, SettingsTreeFilter, SettingsTreeModel } from 'vs/workbench/parts/preferences/browser/settingsTree';
import { getTOCElement, TOCDataSource, TOCRenderer } from 'vs/workbench/parts/preferences/browser/tocTree';
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences';
import { IPreferencesService, ISearchResult, ISetting, ISettingsEditorModel } from 'vs/workbench/services/preferences/common/preferences';
import { IPreferencesService, ISearchResult, ISettingsEditorModel } from 'vs/workbench/services/preferences/common/preferences';
import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
import { DefaultSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
@@ -56,10 +55,10 @@ export class SettingsEditor2 extends BaseEditor {
private settingsTreeContainer: HTMLElement;
private settingsTree: WorkbenchTree;
private treeDataSource: SettingsDataSource;
private settingsTreeModel: SettingsTreeModel;
private tocTreeContainer: HTMLElement;
private tocTree: WorkbenchTree;
private resolvedTocData: IResolvedTOCEntry;
private delayedFilterLogging: Delayer<void>;
private localSearchDelayer: Delayer<void>;
@@ -69,7 +68,7 @@ export class SettingsEditor2 extends BaseEditor {
private settingUpdateDelayer: Delayer<void>;
private pendingSettingUpdate: { key: string, value: any };
private selectedElement: TreeElement;
private selectedElement: SettingsTreeElement;
private viewState: ISettingsEditorViewState;
private searchResultModel: SearchResultModel;
@@ -240,7 +239,8 @@ export class SettingsEditor2 extends BaseEditor {
});
this._register(this.tocTree.onDidChangeSelection(e => {
this.settingsTree.reveal(e.selection[0], .1);
const element = this.settingsTreeModel.getElementById(e.selection[0] && e.selection[0].id);
this.settingsTree.reveal(element, 0);
}));
}
@@ -445,12 +445,12 @@ export class SettingsEditor2 extends BaseEditor {
this.defaultSettingsEditorModel = model;
// if (!this.settingsTree.getInput()) {
this.resolvedTocData = resolveSettingsTree(tocData, this.defaultSettingsEditorModel);
this.tocTree.setInput(this.resolvedTocData);
this.settingsTree.setInput(this.resolvedTocData);
this.tocTree.setInput(getTOCElement(tocData));
this.expandAll(this.settingsTree);
this.expandAll(this.tocTree);
this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState, tocData, this.defaultSettingsEditorModel.settingsGroups.slice(1));
this.settingsTree.setInput(this.settingsTreeModel.root);
this.expandAll(this.settingsTree);
this.expandAll(this.tocTree);
// }
});
}
@@ -505,7 +505,7 @@ export class SettingsEditor2 extends BaseEditor {
}
this.searchResultModel = null;
this.settingsTree.setInput(this.resolvedTocData);
this.settingsTree.setInput(this.settingsTreeModel.root);
this.expandAll(this.settingsTree);
this.expandAll(this.tocTree);
@@ -634,75 +634,3 @@ export class SettingsEditor2 extends BaseEditor {
this.tocTree.layout(listHeight, 200);
}
}
export interface ITOCEntry<T> {
id: string;
label: string;
}
export interface ITOCGroupEntry<T> extends ITOCEntry<T> {
children?: ITOCEntry<T>[];
}
export interface ITOCLeafEntry<T> extends ITOCEntry<T> {
settings?: T[];
}
export type IRawTOCEntry = ITOCGroupEntry<string> | ITOCLeafEntry<string>;
export type IResolvedTOCEntry = ITOCGroupEntry<ISetting> | ITOCLeafEntry<ISetting>;
function resolveSettingsTree(tocData: IRawTOCEntry, defaultSettings: DefaultSettingsEditorModel): IResolvedTOCEntry {
return _resolveSettingsTree(tocData, getAllSettings(defaultSettings));
}
function _resolveSettingsTree(tocData: IRawTOCEntry, allSettings: Set<ISetting>): IResolvedTOCEntry {
if (isTOCLeaf(tocData)) {
return <IResolvedTOCEntry>{
id: tocData.id,
label: tocData.label,
settings: arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern)))
};
} else {
return <IResolvedTOCEntry>{
id: tocData.id,
label: tocData.label,
children: tocData.children.map(child => _resolveSettingsTree(child, allSettings))
};
}
}
function getMatchingSettings(allSettings: Set<ISetting>, pattern: string): ISetting[] {
const result: ISetting[] = [];
allSettings.forEach(s => {
if (settingMatches(s, pattern)) {
result.push(s);
allSettings.delete(s);
}
});
return result.sort((a, b) => a.key.localeCompare(b.key));
}
function settingMatches(s: ISetting, pattern: string): boolean {
pattern = escapeRegExpCharacters(pattern)
.replace(/\\\*/g, '.*');
const regexp = new RegExp(`^${pattern}`, 'i');
return regexp.test(s.key);
}
function getAllSettings(defaultSettings: DefaultSettingsEditorModel) {
const result: Set<ISetting> = new Set();
for (let group of defaultSettings.settingsGroups.slice(1)) {
for (let section of group.sections) {
for (let s of section.settings) {
result.add(s);
}
}
}
return result;
}
@@ -1,179 +1,179 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IRawTOCEntry } from 'vs/workbench/parts/preferences/browser/settingsEditor2';
import { ITOCEntry } from 'vs/workbench/parts/preferences/browser/tocTree';
export const tocData: IRawTOCEntry = {
id: 'root',
label: 'root',
children: [
export const tocData: ITOCEntry = {
id: 'root',
label: 'root',
children: [
{
id: 'editor',
label: 'Text Editor',
children: [
{
id: 'editor.cursor',
label: 'Cursor',
settings: ['editor.cursor*']
},
{
id: 'editor.find',
label: 'Find',
settings: ['editor.find.*']
},
{
id: 'editor.font',
label: 'Font',
settings: ['editor.font*']
},
{
id: 'editor.format',
label: 'Format',
settings: ['editor.format*']
},
{
id: 'editor.diff',
label: 'Diff Editor',
settings: ['diffEditor.*']
},
{
id: 'editor.minimap',
label: 'Minimap',
settings: ['editor.minimap.*']
},
{
id: 'editor.suggestions',
label: 'Suggestions',
settings: ['editor.*suggestion*']
},
{
id: 'editor.files',
label: 'Files',
settings: ['files.*']
},
{
id: 'editor.editor',
label: 'Editor',
settings: ['editor.*']
}
]
},
{
id: 'workbench',
label: 'Workbench',
children: [
id: 'editor',
label: 'Text Editor',
children: [
{
id: 'workbench.appearance',
label: 'Appearance',
settings: ['workbench.activityBar.*', 'workbench.*color*', 'workbench.fontAliasing', 'workbench.iconTheme', 'workbench.sidebar.location', 'workbench.*.visible', 'workbench.tips.enabled', 'workbench.tree.*', 'workbench.view.*']
},
{
id: 'workbench.editor',
label: 'Editor Management',
settings: ['workbench.editor.*']
},
{
id: 'workbench.zenmode',
label: 'Zen Mode',
settings: ['zenmode.*']
},
{
id: 'workbench.workbench',
label: 'Workbench',
settings: ['workbench.*']
}
]
},
{
id: 'window',
label: 'Window',
children: [
{
id: 'window.newWindow',
label: 'New Window',
settings: ['window.*newwindow*']
},
{
id: 'window.window',
label: 'Window',
settings: ['window.*']
}
]
},
{
id: 'features',
label: 'Features',
children: [
{
id: 'features.explorer',
label: 'File Explorer',
settings: ['explorer.*', 'outline.*']
},
{
id: 'features.search',
label: 'Search',
settings: ['search.*']
}
,
{
id: 'features.debug',
label: 'Debug',
settings: ['debug.*', 'launch']
},
{
id: 'features.scm',
label: 'Source Control Management',
settings: ['scm.*']
},
{
id: 'features.extensions',
label: 'Extension Viewlet',
settings: ['extensions.*']
},
{
id: 'features.terminal',
label: 'Terminal',
settings: ['terminal.*']
},
{
id: 'features.problems',
label: 'Problems',
settings: ['problems.*']
}
]
},
{
id: 'application',
label: 'Application',
children: [
{
id: 'application.http',
label: 'Proxy',
settings: ['http.*']
},
{
id: 'application.keyboard',
label: 'Keyboard',
settings: ['keyboard.*']
},
{
id: 'application.update',
label: 'Update',
settings: ['update.*']
},
{
id: 'application.telemetry',
label: 'Telemetry',
settings: ['telemetry.*']
}
]
},
{
id: 'extensions',
label: 'Extensions',
settings: ['*']
}
]
id: 'editor/cursor',
label: 'Cursor',
settings: ['editor.cursor*']
},
{
id: 'editor/find',
label: 'Find',
settings: ['editor.find.*']
},
{
id: 'editor/font',
label: 'Font',
settings: ['editor.font*']
},
{
id: 'editor/format',
label: 'Format',
settings: ['editor.format*']
},
{
id: 'editor/diff',
label: 'Diff Editor',
settings: ['diffEditor.*']
},
{
id: 'editor/minimap',
label: 'Minimap',
settings: ['editor.minimap.*']
},
{
id: 'editor/suggestions',
label: 'Suggestions',
settings: ['editor.*suggestion*']
},
{
id: 'editor/files',
label: 'Files',
settings: ['files.*']
},
{
id: 'editor/editor',
label: 'Editor',
settings: ['editor.*']
}
]
},
{
id: 'workbench',
label: 'Workbench',
children: [
{
id: 'workbench/appearance',
label: 'Appearance',
settings: ['workbench.activityBar.*', 'workbench.*color*', 'workbench.fontAliasing', 'workbench.iconTheme', 'workbench.sidebar.location', 'workbench.*.visible', 'workbench.tips.enabled', 'workbench.tree.*', 'workbench.view.*']
},
{
id: 'workbench/editor',
label: 'Editor Management',
settings: ['workbench.editor.*']
},
{
id: 'workbench/zenmode',
label: 'Zen Mode',
settings: ['zenmode.*']
},
{
id: 'workbench/workbench',
label: 'Workbench',
settings: ['workbench.*']
}
]
},
{
id: 'window',
label: 'Window',
children: [
{
id: 'window/newWindow',
label: 'New Window',
settings: ['window.*newwindow*']
},
{
id: 'window/window',
label: 'Window',
settings: ['window.*']
}
]
},
{
id: 'features',
label: 'Features',
children: [
{
id: 'features/explorer',
label: 'File Explorer',
settings: ['explorer.*', 'outline.*']
},
{
id: 'features/search',
label: 'Search',
settings: ['search.*']
}
,
{
id: 'features/debug',
label: 'Debug',
settings: ['debug.*', 'launch']
},
{
id: 'features/scm',
label: 'Source Control Management',
settings: ['scm.*']
},
{
id: 'features/extensions',
label: 'Extension Viewlet',
settings: ['extensions.*']
},
{
id: 'features/terminal',
label: 'Terminal',
settings: ['terminal.*']
},
{
id: 'features/problems',
label: 'Problems',
settings: ['problems.*']
}
]
},
{
id: 'application',
label: 'Application',
children: [
{
id: 'application/http',
label: 'Proxy',
settings: ['http.*']
},
{
id: 'application/keyboard',
label: 'Keyboard',
settings: ['keyboard.*']
},
{
id: 'application/update',
label: 'Update',
settings: ['update.*']
},
{
id: 'application/telemetry',
label: 'Telemetry',
settings: ['telemetry.*']
}
]
},
{
id: 'extensions',
label: 'Extensions',
settings: ['*']
}
]
};
@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as arrays from 'vs/base/common/arrays';
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
@@ -25,8 +26,9 @@ import { editorActiveLinkForeground, registerColor } from 'vs/platform/theme/com
import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { IResolvedTOCEntry, ITOCGroupEntry, ITOCLeafEntry, ITOCEntry } from 'vs/workbench/parts/preferences/browser/settingsEditor2';
import { ISearchResult, ISetting } from 'vs/workbench/services/preferences/common/preferences';
import { ISearchResult, ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences';
import { ITOCEntry } from 'vs/workbench/parts/preferences/browser/tocTree';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
const $ = DOM.$;
@@ -43,20 +45,20 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
}
});
export interface ITreeItem {
export abstract class SettingsTreeElement {
id: string;
parent: SettingsTreeElement;
}
export enum TreeItemType {
setting,
groupTitle
export class SettingsTreeGroupElement extends SettingsTreeElement {
children: (SettingsTreeGroupElement | SettingsTreeSettingElement)[];
label: string;
}
export interface ISettingElement extends ITreeItem {
type: TreeItemType.setting;
export class SettingsTreeSettingElement extends SettingsTreeElement {
setting: ISetting;
parent: IGroupElement | SearchResultModel;
isExpanded: boolean;
displayCategory: string;
displayLabel: string;
value: any;
@@ -67,44 +69,49 @@ export interface ISettingElement extends ITreeItem {
enum?: string[];
}
export interface IGroupElement extends ITreeItem {
type: TreeItemType.groupTitle;
parent: IGroupElement | IResolvedTOCEntry;
group: IResolvedTOCEntry;
index: number;
}
export class SettingsTreeModel {
private _root: SettingsTreeElement;
private _treeElementsById = new Map<string, SettingsTreeElement>();
export type TreeElement = ISettingElement | IGroupElement;
export type TreeElementOrRoot = TreeElement | IResolvedTOCEntry | SearchResultModel;
function inspectSetting(key: string, target: SettingsTarget, configurationService: IConfigurationService): { isConfigured: boolean, inspected: any, targetSelector: string } {
const inspectOverrides = URI.isUri(target) ? { resource: target } : undefined;
const inspected = configurationService.inspect(key, inspectOverrides);
const targetSelector = target === ConfigurationTarget.USER ? 'user' :
target === ConfigurationTarget.WORKSPACE ? 'workspace' :
'workspaceFolder';
const isConfigured = typeof inspected[targetSelector] !== 'undefined';
return { isConfigured, inspected, targetSelector };
}
export class SettingsDataSource implements IDataSource {
constructor(
private viewState: ISettingsEditorViewState,
tocRoot: ITOCEntry,
allSettings: ISettingsGroup[],
@IConfigurationService private configurationService: IConfigurationService
) { }
getGroupElement(group: IResolvedTOCEntry, parent: IGroupElement | IResolvedTOCEntry, index: number): IGroupElement {
return <IGroupElement>{
type: TreeItemType.groupTitle,
group,
parent,
id: sanitizeElementId(group.id),
index
};
) {
const resolvedTOC = resolveSettingsTree(tocRoot, allSettings);
this._root = this.createSettingsTreeGroupElement(resolvedTOC);
}
getSettingElement(setting: ISetting, parent: IGroupElement | SearchResultModel, category: string): ISettingElement {
get root(): SettingsTreeElement {
return this._root;
}
getElementById(id: string): SettingsTreeElement {
return this._treeElementsById.get(id);
}
private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement {
const element = new SettingsTreeGroupElement();
element.id = tocEntry.id;
element.label = tocEntry.label;
element.parent = parent;
if (tocEntry.children) {
element.children = tocEntry.children.map(child => this.createSettingsTreeGroupElement(child, element));
} else if (tocEntry.settings) {
element.children = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(<ISetting>s, element));
}
this._treeElementsById.set(element.id, element);
return element;
}
private createSettingsTreeSettingElement(setting: ISetting, parent: SettingsTreeGroupElement): SettingsTreeSettingElement {
const element = new SettingsTreeSettingElement();
element.id = setting.key;
element.parent = parent;
const { isConfigured, inspected, targetSelector } = inspectSetting(setting.key, this.viewState.settingsTarget, this.configurationService);
const displayValue = isConfigured ? inspected[targetSelector] : inspected.default;
@@ -117,96 +124,143 @@ export class SettingsDataSource implements IDataSource {
overriddenScopeList.push(localize('user', "User"));
}
const displayKeyFormat = settingKeyToDisplayFormat(setting.key, category);
return <ISettingElement>{
type: TreeItemType.setting,
parent,
id: sanitizeElementId(setting.key),
setting,
const displayKeyFormat = settingKeyToDisplayFormat(setting.key, parent.id);
element.setting = setting;
element.displayLabel = displayKeyFormat.label;
element.displayCategory = displayKeyFormat.category;
element.isExpanded = false;
displayLabel: displayKeyFormat.label,
displayCategory: displayKeyFormat.category,
isExpanded: false,
element.value = displayValue;
element.isConfigured = isConfigured;
element.overriddenScopeList = overriddenScopeList;
element.description = setting.description.join('\n');
element.enum = setting.enum;
element.valueType = setting.type;
value: displayValue,
isConfigured,
overriddenScopeList,
description: setting.description.join('\n'),
enum: setting.enum,
valueType: setting.type
this._treeElementsById.set(element.id, element);
return element;
}
}
function inspectSetting(key: string, target: SettingsTarget, configurationService: IConfigurationService): { isConfigured: boolean, inspected: any, targetSelector: string } {
const inspectOverrides = URI.isUri(target) ? { resource: target } : undefined;
const inspected = configurationService.inspect(key, inspectOverrides);
const targetSelector = target === ConfigurationTarget.USER ? 'user' :
target === ConfigurationTarget.WORKSPACE ? 'workspace' :
'workspaceFolder';
const isConfigured = typeof inspected[targetSelector] !== 'undefined';
return { isConfigured, inspected, targetSelector };
}
function resolveSettingsTree(tocData: ITOCEntry, settingsGroups: ISettingsGroup[]): ITOCEntry {
return _resolveSettingsTree(tocData, getFlatSettings(settingsGroups));
}
function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set<ISetting>): ITOCEntry {
if (tocData.settings) {
return <ITOCEntry>{
id: tocData.id,
label: tocData.label,
settings: arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, <string>pattern)))
};
} else if (tocData.children) {
return <ITOCEntry>{
id: tocData.id,
label: tocData.label,
children: tocData.children.map(child => _resolveSettingsTree(child, allSettings))
};
}
getId(tree: ITree, element: TreeElementOrRoot): string {
return null;
}
function getMatchingSettings(allSettings: Set<ISetting>, pattern: string): ISetting[] {
const result: ISetting[] = [];
allSettings.forEach(s => {
if (settingMatches(s, pattern)) {
result.push(s);
allSettings.delete(s);
}
});
return result.sort((a, b) => a.key.localeCompare(b.key));
}
function settingMatches(s: ISetting, pattern: string): boolean {
pattern = escapeRegExpCharacters(pattern)
.replace(/\\\*/g, '.*');
const regexp = new RegExp(`^${pattern}`, 'i');
return regexp.test(s.key);
}
function getFlatSettings(settingsGroups: ISettingsGroup[]) {
const result: Set<ISetting> = new Set();
for (let group of settingsGroups) {
for (let section of group.sections) {
for (let s of section.settings) {
result.add(s);
}
}
}
return result;
}
export class SettingsDataSource implements IDataSource {
getId(tree: ITree, element: SettingsTreeElement): string {
return element.id;
}
hasChildren(tree: ITree, element: TreeElementOrRoot): boolean {
if (isTOCRoot(element)) {
return true;
}
hasChildren(tree: ITree, element: SettingsTreeElement): boolean {
if (element instanceof SearchResultModel) {
return true;
}
if (element.type === TreeItemType.groupTitle) {
if (element instanceof SettingsTreeGroupElement) {
return true;
}
return false;
}
private _getChildren(element: TreeElementOrRoot): TreeElement[] {
if (isTOCRoot(element)) {
return this.getRootChildren(element);
} else if (element instanceof SearchResultModel) {
private getSearchResultChildren(searchResult: SearchResultModel): SettingsTreeSettingElement[] {
return searchResult.getFlatSettings()
.map(s => this.getSettingElement(s, searchResult, 'searchResult'));
}
getChildren(tree: ITree, element: SettingsTreeElement): TPromise<any, any> {
return TPromise.as(this._getChildren(element));
}
private _getChildren(element: SettingsTreeElement): SettingsTreeElement[] {
if (element instanceof SearchResultModel) {
return this.getSearchResultChildren(element);
} else if (element.type === TreeItemType.groupTitle) {
} else if (element instanceof SettingsTreeGroupElement) {
return this.getGroupChildren(element);
} else {
// No children...
return null;
}
}
private getSearchResultChildren(searchResult: SearchResultModel): ISettingElement[] {
return searchResult.getFlatSettings()
.map(s => this.getSettingElement(s, searchResult, 'searchResult'));
private getGroupChildren(groupElement: SettingsTreeGroupElement): SettingsTreeElement[] {
return groupElement.children;
}
getChildren(tree: ITree, element: TreeElementOrRoot): TPromise<any, any> {
return TPromise.as(this._getChildren(element));
getParent(tree: ITree, element: SettingsTreeElement): TPromise<any, any> {
return TPromise.wrap(element.parent);
}
private getRootChildren(root: ITOCGroupEntry<ISetting>): TreeElement[] {
return root.children
.map((g, i) => this.getGroupElement(g, root, i));
}
private getGroupChildren(groupElement: IGroupElement): TreeElement[] {
return isTOCLeaf(groupElement.group) ?
groupElement.group.settings.map(s => this.getSettingElement(s, groupElement, groupElement.id)) :
groupElement.group.children.map((child, i) => this.getGroupElement(child, groupElement, i));
}
getParent(tree: ITree, element: TreeElementOrRoot): TPromise<any, any> {
return TPromise.wrap(
isTOCRoot(element) ? null :
element instanceof SearchResultModel ? null :
element.parent);
}
}
function sanitizeElementId(id: string): string {
return id.replace(/\./g, '_');
}
function isTOCRoot(element: TreeElementOrRoot): element is IResolvedTOCEntry {
return element.id === 'root';
}
export function settingKeyToDisplayFormat(key: string, groupId: string): { category: string, label: string } {
groupId = groupId.replace(/\//g, '.');
let label = key
.replace(/\.([a-z])/g, (match, p1) => `.${p1.toUpperCase()}`)
.replace(/([a-z])([A-Z])/g, '$1 $2') // fooBar => foo Bar
@@ -267,7 +321,7 @@ interface IDisposableTemplate {
interface ISettingItemTemplate extends IDisposableTemplate {
parent: HTMLElement;
context?: ISettingElement;
context?: SettingsTreeSettingElement;
containerElement: HTMLElement;
categoryElement: HTMLElement;
labelElement: HTMLElement;
@@ -278,7 +332,7 @@ interface ISettingItemTemplate extends IDisposableTemplate {
}
interface IGroupTitleTemplate extends IDisposableTemplate {
context?: IGroupElement;
context?: SettingsTreeGroupElement;
parent: HTMLElement;
labelElement: HTMLElement;
}
@@ -311,12 +365,12 @@ export class SettingsRenderer implements IRenderer {
this.measureContainer = DOM.append(_measureContainer, $('.setting-measure-container.monaco-tree-row'));
}
getHeight(tree: ITree, element: TreeElement): number {
if (element.type === TreeItemType.groupTitle) {
getHeight(tree: ITree, element: SettingsTreeElement): number {
if (element instanceof SettingsTreeGroupElement) {
return 30;
}
if (element.type === TreeItemType.setting) {
if (element instanceof SettingsTreeSettingElement) {
const isSelected = this.elementIsSelected(tree, element);
if (isSelected) {
return this.measureSettingElementHeight(tree, element);
@@ -328,7 +382,7 @@ export class SettingsRenderer implements IRenderer {
return 0;
}
private measureSettingElementHeight(tree: ITree, element: ISettingElement): number {
private measureSettingElementHeight(tree: ITree, element: SettingsTreeSettingElement): number {
const measureHelper = DOM.append(this.measureContainer, $('.setting-measure-helper'));
const template = this.renderSettingTemplate(tree, measureHelper);
@@ -339,12 +393,12 @@ export class SettingsRenderer implements IRenderer {
return Math.max(height, SettingsRenderer.SETTING_ROW_HEIGHT);
}
getTemplateId(tree: ITree, element: TreeElement): string {
if (element.type === TreeItemType.groupTitle) {
getTemplateId(tree: ITree, element: SettingsTreeElement): string {
if (element instanceof SettingsTreeGroupElement) {
return SETTINGS_GROUP_ELEMENT_TEMPLATE_ID;
}
if (element.type === TreeItemType.setting) {
if (element instanceof SettingsTreeSettingElement) {
return SETTINGS_ELEMENT_TEMPLATE_ID;
}
@@ -417,24 +471,24 @@ export class SettingsRenderer implements IRenderer {
return template;
}
renderElement(tree: ITree, element: TreeElement, templateId: string, template: any): void {
renderElement(tree: ITree, element: SettingsTreeElement, templateId: string, template: any): void {
if (templateId === SETTINGS_ELEMENT_TEMPLATE_ID) {
return this.renderSettingElement(tree, <ISettingElement>element, template);
return this.renderSettingElement(tree, <SettingsTreeSettingElement>element, template);
}
if (templateId === SETTINGS_GROUP_ELEMENT_TEMPLATE_ID) {
(<IGroupTitleTemplate>template).labelElement.textContent = (<IGroupElement>element).group.label;
(<IGroupTitleTemplate>template).labelElement.textContent = (<SettingsTreeGroupElement>element).label;
return;
}
}
private elementIsSelected(tree: ITree, element: TreeElement): boolean {
private elementIsSelected(tree: ITree, element: SettingsTreeElement): boolean {
const selection = tree.getSelection();
const selectedElement: TreeElement = selection && selection[0];
const selectedElement: SettingsTreeElement = selection && selection[0];
return selectedElement && selectedElement.id === element.id;
}
private renderSettingElement(tree: ITree, element: ISettingElement, template: ISettingItemTemplate): void {
private renderSettingElement(tree: ITree, element: SettingsTreeSettingElement, template: ISettingItemTemplate): void {
const isSelected = !!this.elementIsSelected(tree, element);
const setting = element.setting;
@@ -482,7 +536,7 @@ export class SettingsRenderer implements IRenderer {
}
}
private renderValue(element: ISettingElement, isSelected: boolean, template: ISettingItemTemplate): void {
private renderValue(element: SettingsTreeSettingElement, isSelected: boolean, template: ISettingItemTemplate): void {
const onChange = value => this._onDidChangeSetting.fire({ key: element.setting.key, value });
template.valueElement.innerHTML = '';
const valueControlElement = DOM.append(template.valueElement, $('.setting-item-control'));
@@ -504,7 +558,7 @@ export class SettingsRenderer implements IRenderer {
}
}
private renderBool(dataElement: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: boolean) => void): void {
private renderBool(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: boolean) => void): void {
const checkboxElement = <HTMLInputElement>DOM.append(element, $('input.setting-value-checkbox.setting-value-input'));
checkboxElement.type = 'checkbox';
checkboxElement.checked = dataElement.value;
@@ -513,7 +567,7 @@ export class SettingsRenderer implements IRenderer {
template.toDispose.push(DOM.addDisposableListener(checkboxElement, 'change', e => onChange(checkboxElement.checked)));
}
private renderEnum(dataElement: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: string) => void): void {
private renderEnum(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: string) => void): void {
const idx = dataElement.enum.indexOf(dataElement.value);
const displayOptions = dataElement.enum.map(escapeInvisibleChars);
const selectBox = new SelectBox(displayOptions, idx, this.contextViewService);
@@ -527,7 +581,7 @@ export class SettingsRenderer implements IRenderer {
selectBox.onDidSelect(e => onChange(dataElement.enum[e.index])));
}
private renderText(dataElement: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: string) => void): void {
private renderText(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: string) => void): void {
const inputBox = new InputBox(element, this.contextViewService);
template.toDispose.push(attachInputBoxStyler(inputBox, this.themeService));
template.toDispose.push(inputBox);
@@ -538,7 +592,7 @@ export class SettingsRenderer implements IRenderer {
inputBox.onDidChange(e => onChange(e)));
}
private renderEditInSettingsJson(dataElement: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement): void {
private renderEditInSettingsJson(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement): void {
const openSettingsButton = new Button(element, { title: true, buttonBackground: null, buttonHoverBackground: null });
openSettingsButton.onDidClick(() => this._onDidOpenSettings.fire());
openSettingsButton.label = localize('editInSettingsJson', "Edit in settings.json");
@@ -570,28 +624,30 @@ export class SettingsTreeFilter implements IFilter {
@IConfigurationService private configurationService: IConfigurationService
) { }
isVisible(tree: ITree, element: TreeElement): boolean {
if (this.viewState.showConfiguredOnly && element.type === TreeItemType.setting) {
isVisible(tree: ITree, element: SettingsTreeElement): boolean {
if (this.viewState.showConfiguredOnly && element instanceof SettingsTreeSettingElement) {
return element.isConfigured;
}
if (element.type === TreeItemType.groupTitle && this.viewState.showConfiguredOnly) {
return this.groupHasConfiguredSetting(element.group);
if (element instanceof SettingsTreeGroupElement && this.viewState.showConfiguredOnly) {
return this.groupHasConfiguredSetting(element);
}
return true;
}
private groupHasConfiguredSetting(element: IResolvedTOCEntry): boolean {
if (isTOCLeaf(element)) {
for (let setting of element.settings) {
const { isConfigured } = inspectSetting(setting.key, this.viewState.settingsTarget, this.configurationService);
private groupHasConfiguredSetting(element: SettingsTreeGroupElement): boolean {
for (let child of element.children) {
if (child instanceof SettingsTreeSettingElement) {
const { isConfigured } = inspectSetting(child.setting.key, this.viewState.settingsTarget, this.configurationService);
if (isConfigured) {
return true;
}
} else {
if (child instanceof SettingsTreeGroupElement) {
return this.groupHasConfiguredSetting(child);
}
}
} else {
return element.children.some(c => this.groupHasConfiguredSetting(c));
}
return false;
@@ -607,17 +663,17 @@ export class SettingsTreeController extends WorkbenchTreeController {
}
export class SettingsAccessibilityProvider implements IAccessibilityProvider {
getAriaLabel(tree: ITree, element: TreeElement): string {
getAriaLabel(tree: ITree, element: SettingsTreeElement): string {
if (!element) {
return '';
}
if (element.type === TreeItemType.setting) {
if (element instanceof SettingsTreeSettingElement) {
return localize('settingRowAriaLabel', "{0} {1}, Setting", element.displayCategory, element.displayLabel);
}
if (element.type === TreeItemType.groupTitle) {
return localize('groupRowAriaLabel', "{0}, group", element.group.label);
if (element instanceof SettingsTreeGroupElement) {
return localize('groupRowAriaLabel', "{0}, group", element.label);
}
return '';
@@ -681,7 +737,3 @@ export class SearchResultModel {
return flatSettings;
}
}
export function isTOCLeaf<T>(entry: ITOCEntry<T>): entry is ITOCLeafEntry<T> {
return !!(<ITOCLeafEntry<T>>entry).settings;
}
@@ -6,32 +6,53 @@
import * as DOM from 'vs/base/browser/dom';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDataSource, IRenderer, ITree } from 'vs/base/parts/tree/browser/tree';
import { IResolvedTOCEntry, ITOCGroupEntry } from 'vs/workbench/parts/preferences/browser/settingsEditor2';
import { ISetting } from 'vs/workbench/services/preferences/common/preferences';
import { isTOCLeaf } from 'vs/workbench/parts/preferences/browser/settingsTree';
const $ = DOM.$;
// export interface ITOCRoot {
// id: string;
// children: ITOCEntry[];
// }
export interface ITOCEntry {
id: string;
label: string;
children?: ITOCEntry[];
settings?: (string | ISetting)[];
}
export class TOCElement {
id: string;
label: string;
parent?: TOCElement;
children?: TOCElement[];
}
export function getTOCElement(tocRoot: ITOCEntry, parent?: TOCElement): TOCElement {
const element = new TOCElement();
element.id = tocRoot.id;
element.label = tocRoot.label;
element.parent = parent;
if (tocRoot.children) {
element.children = tocRoot.children.map(child => getTOCElement(child, element));
}
return element;
}
export class TOCDataSource implements IDataSource {
getId(tree: ITree, element: IResolvedTOCEntry): string {
getId(tree: ITree, element: TOCElement): string {
return element.id;
}
hasChildren(tree: ITree, element: IResolvedTOCEntry): boolean {
return !isTOCLeaf(element) && element.children.length && typeof element.children[0] !== 'string';
hasChildren(tree: ITree, element: TOCElement): boolean {
return !!(element.children && element.children.length);
}
getChildren(tree: ITree, element: ITOCGroupEntry<ISetting>): TPromise<IResolvedTOCEntry[], any> {
return TPromise.as(<IResolvedTOCEntry[]>element.children);
getChildren(tree: ITree, element: TOCElement): TPromise<TOCElement[], any> {
return TPromise.as(<TOCElement[]>element.children);
}
getParent(tree: ITree, element: IResolvedTOCEntry): TPromise<any, any> {
return TPromise.wrap(null); // ??
getParent(tree: ITree, element: TOCElement): TPromise<any, any> {
return TPromise.wrap(element.parent);
}
}
@@ -42,11 +63,11 @@ interface ITOCEntryTemplate {
}
export class TOCRenderer implements IRenderer {
getHeight(tree: ITree, element: IResolvedTOCEntry): number {
getHeight(tree: ITree, element: TOCElement): number {
return 22;
}
getTemplateId(tree: ITree, element: IResolvedTOCEntry): string {
getTemplateId(tree: ITree, element: TOCElement): string {
return TOC_ENTRY_TEMPLATE_ID;
}
@@ -56,7 +77,7 @@ export class TOCRenderer implements IRenderer {
};
}
renderElement(tree: ITree, element: IResolvedTOCEntry, templateId: string, template: ITOCEntryTemplate): void {
renderElement(tree: ITree, element: TOCElement, templateId: string, template: ITOCEntryTemplate): void {
template.element.textContent = element.label;
}