diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index 0c59d96a99dd9502530262f420f23de654c6f1e0..943b6e1f350229db623331c5e43ecf81993d9ff5 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -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; private localSearchDelayer: Delayer; @@ -69,7 +68,7 @@ export class SettingsEditor2 extends BaseEditor { private settingUpdateDelayer: Delayer; 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 { - id: string; - label: string; -} - -export interface ITOCGroupEntry extends ITOCEntry { - children?: ITOCEntry[]; -} - -export interface ITOCLeafEntry extends ITOCEntry { - settings?: T[]; -} - -export type IRawTOCEntry = ITOCGroupEntry | ITOCLeafEntry; -export type IResolvedTOCEntry = ITOCGroupEntry | ITOCLeafEntry; - -function resolveSettingsTree(tocData: IRawTOCEntry, defaultSettings: DefaultSettingsEditorModel): IResolvedTOCEntry { - return _resolveSettingsTree(tocData, getAllSettings(defaultSettings)); -} - -function _resolveSettingsTree(tocData: IRawTOCEntry, allSettings: Set): IResolvedTOCEntry { - if (isTOCLeaf(tocData)) { - return { - id: tocData.id, - label: tocData.label, - settings: arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern))) - }; - } else { - return { - id: tocData.id, - label: tocData.label, - children: tocData.children.map(child => _resolveSettingsTree(child, allSettings)) - }; - } -} - -function getMatchingSettings(allSettings: Set, 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 = 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; -} diff --git a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts index bf7784e085349287a08b3dc9b98c188491d305f0..476f528d72db524801c5b48729b462ebe25f0717 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts @@ -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: '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', + 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: '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: ['*'] + } + ] }; diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 9b81e662bea5cf39d0eb3125ec26066375453ea5..0cff11828d8289d946410c1854a18fc6effb3344 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -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,15 +69,78 @@ 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(); + + constructor( + private viewState: ISettingsEditorViewState, + tocRoot: ITOCEntry, + allSettings: ISettingsGroup[], + @IConfigurationService private configurationService: IConfigurationService + ) { + const resolvedTOC = resolveSettingsTree(tocRoot, allSettings); + this._root = this.createSettingsTreeGroupElement(resolvedTOC); + } + + 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; -export type TreeElement = ISettingElement | IGroupElement; -export type TreeElementOrRoot = TreeElement | IResolvedTOCEntry | SearchResultModel; + 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(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; + const overriddenScopeList = []; + if (targetSelector === 'user' && typeof inspected.workspace !== 'undefined') { + overriddenScopeList.push(localize('workspace', "Workspace")); + } + + if (targetSelector === 'workspace' && typeof inspected.user !== 'undefined') { + overriddenScopeList.push(localize('user', "User")); + } + + const displayKeyFormat = settingKeyToDisplayFormat(setting.key, parent.id); + element.setting = setting; + element.displayLabel = displayKeyFormat.label; + element.displayCategory = displayKeyFormat.category; + element.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; + + 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; @@ -88,125 +153,114 @@ function inspectSetting(key: string, target: SettingsTarget, configurationServic return { isConfigured, inspected, targetSelector }; } -export class SettingsDataSource implements IDataSource { - constructor( - private viewState: ISettingsEditorViewState, - @IConfigurationService private configurationService: IConfigurationService - ) { } +function resolveSettingsTree(tocData: ITOCEntry, settingsGroups: ISettingsGroup[]): ITOCEntry { + return _resolveSettingsTree(tocData, getFlatSettings(settingsGroups)); +} - getGroupElement(group: IResolvedTOCEntry, parent: IGroupElement | IResolvedTOCEntry, index: number): IGroupElement { - return { - type: TreeItemType.groupTitle, - group, - parent, - id: sanitizeElementId(group.id), - index +function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set): ITOCEntry { + if (tocData.settings) { + return { + id: tocData.id, + label: tocData.label, + settings: arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern))) + }; + } else if (tocData.children) { + return { + id: tocData.id, + label: tocData.label, + children: tocData.children.map(child => _resolveSettingsTree(child, allSettings)) }; } - getSettingElement(setting: ISetting, parent: IGroupElement | SearchResultModel, category: string): ISettingElement { - const { isConfigured, inspected, targetSelector } = inspectSetting(setting.key, this.viewState.settingsTarget, this.configurationService); + return null; +} - const displayValue = isConfigured ? inspected[targetSelector] : inspected.default; - const overriddenScopeList = []; - if (targetSelector === 'user' && typeof inspected.workspace !== 'undefined') { - overriddenScopeList.push(localize('workspace', "Workspace")); - } +function getMatchingSettings(allSettings: Set, pattern: string): ISetting[] { + const result: ISetting[] = []; - if (targetSelector === 'workspace' && typeof inspected.user !== 'undefined') { - overriddenScopeList.push(localize('user', "User")); + allSettings.forEach(s => { + if (settingMatches(s, pattern)) { + result.push(s); + allSettings.delete(s); } + }); - const displayKeyFormat = settingKeyToDisplayFormat(setting.key, category); - return { - type: TreeItemType.setting, - parent, - id: sanitizeElementId(setting.key), - setting, - - displayLabel: displayKeyFormat.label, - displayCategory: displayKeyFormat.category, - isExpanded: false, - - value: displayValue, - isConfigured, - overriddenScopeList, - description: setting.description.join('\n'), - enum: setting.enum, - valueType: setting.type - }; + + 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 = new Set(); + + for (let group of settingsGroups) { + for (let section of group.sections) { + for (let s of section.settings) { + result.add(s); + } + } } - getId(tree: ITree, element: TreeElementOrRoot): string { + 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) { - return this.getSearchResultChildren(element); - } else if (element.type === TreeItemType.groupTitle) { - return this.getGroupChildren(element); - } else { - // No children... - return null; - } - } - - private getSearchResultChildren(searchResult: SearchResultModel): ISettingElement[] { + private getSearchResultChildren(searchResult: SearchResultModel): SettingsTreeSettingElement[] { return searchResult.getFlatSettings() .map(s => this.getSettingElement(s, searchResult, 'searchResult')); } - getChildren(tree: ITree, element: TreeElementOrRoot): TPromise { + getChildren(tree: ITree, element: SettingsTreeElement): TPromise { return TPromise.as(this._getChildren(element)); } - private getRootChildren(root: ITOCGroupEntry): TreeElement[] { - return root.children - .map((g, i) => this.getGroupElement(g, root, i)); + private _getChildren(element: SettingsTreeElement): SettingsTreeElement[] { + if (element instanceof SearchResultModel) { + return this.getSearchResultChildren(element); + } else if (element instanceof SettingsTreeGroupElement) { + return this.getGroupChildren(element); + } else { + // No children... + return null; + } } - - 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)); + private getGroupChildren(groupElement: SettingsTreeGroupElement): SettingsTreeElement[] { + return groupElement.children; } - getParent(tree: ITree, element: TreeElementOrRoot): TPromise { - return TPromise.wrap( - isTOCRoot(element) ? null : - element instanceof SearchResultModel ? null : - element.parent); + getParent(tree: ITree, element: SettingsTreeElement): TPromise { + return TPromise.wrap(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, element, template); + return this.renderSettingElement(tree, element, template); } if (templateId === SETTINGS_GROUP_ELEMENT_TEMPLATE_ID) { - (template).labelElement.textContent = (element).group.label; + (template).labelElement.textContent = (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 = 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(entry: ITOCEntry): entry is ITOCLeafEntry { - return !!(>entry).settings; -} diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index f38680200d57a636be5359f3ce847d56132cc1ff..dc5eb740dd93e06cb056ca4838b4f3ba2c84db2d 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -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): TPromise { - return TPromise.as(element.children); + getChildren(tree: ITree, element: TOCElement): TPromise { + return TPromise.as(element.children); } - getParent(tree: ITree, element: IResolvedTOCEntry): TPromise { - return TPromise.wrap(null); // ?? + getParent(tree: ITree, element: TOCElement): TPromise { + 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; }