From 804c7a18dcc6f23764ffc44485cebbcb6a8921a7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 5 Feb 2019 10:40:58 +0100 Subject: [PATCH 01/16] outline & new tree, 2nd attempt --- src/vs/editor/common/modes.ts | 4 +- .../documentSymbols/media/symbol-icons.css | 8 +- .../contrib/documentSymbols}/outline.ts | 0 .../contrib/documentSymbols/outlineModel.ts | 43 --- .../contrib/documentSymbols/outlineTree2.ts | 247 ++++++++++++++++ src/vs/workbench/browser/viewlet.ts | 3 +- .../electron-browser/outline.contribution.ts | 4 +- .../outline/electron-browser/outlinePanel.css | 23 +- .../{outlinePanel.ts => outlinePanel2.ts} | 275 +++++------------- 9 files changed, 330 insertions(+), 277 deletions(-) rename src/vs/{workbench/parts/outline/electron-browser => editor/contrib/documentSymbols}/outline.ts (100%) create mode 100644 src/vs/editor/contrib/documentSymbols/outlineTree2.ts rename src/vs/workbench/parts/outline/electron-browser/{outlinePanel.ts => outlinePanel2.ts} (68%) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 1468afc9581..4bad77f0289 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -864,8 +864,8 @@ export const symbolKindToCssClass = (function () { _fromMapping[SymbolKind.Operator] = 'operator'; _fromMapping[SymbolKind.TypeParameter] = 'type-parameter'; - return function toCssClassName(kind: SymbolKind): string { - return `symbol-icon ${_fromMapping[kind] || 'property'}`; + return function toCssClassName(kind: SymbolKind, inline?: boolean): string { + return `symbol-icon ${inline ? 'inline' : 'block'} ${_fromMapping[kind] || 'property'}`; }; })(); diff --git a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css index 76d67d5efcc..2a6a31185fa 100644 --- a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css +++ b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css @@ -3,7 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .symbol-icon { +.monaco-workbench .symbol-icon.inline { + background-position: left center; + padding-left: 20px; + background-size: 16px 16px; +} + +.monaco-workbench .symbol-icon.block { display: inline-block; height: 14px; width: 16px; diff --git a/src/vs/workbench/parts/outline/electron-browser/outline.ts b/src/vs/editor/contrib/documentSymbols/outline.ts similarity index 100% rename from src/vs/workbench/parts/outline/electron-browser/outline.ts rename to src/vs/editor/contrib/documentSymbols/outline.ts diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index cf559331c52..014a75991e8 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -7,7 +7,6 @@ import { binarySearch, coalesceInPlace } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { first, forEach, size } from 'vs/base/common/collections'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; import { LRUCache } from 'vs/base/common/map'; import { commonPrefixLength } from 'vs/base/common/strings'; import { IPosition } from 'vs/editor/common/core/position'; @@ -90,7 +89,6 @@ export abstract class TreeElement { export class OutlineElement extends TreeElement { children: { [id: string]: OutlineElement; } = Object.create(null); - score: FuzzyScore | undefined = FuzzyScore.Default; marker: { count: number, topSev: MarkerSeverity } | undefined; constructor( @@ -127,33 +125,6 @@ export class OutlineGroup extends TreeElement { return res; } - updateMatches(pattern: string, topMatch: OutlineElement | undefined): OutlineElement | undefined { - for (const key in this.children) { - topMatch = this._updateMatches(pattern, this.children[key], topMatch); - } - return topMatch; - } - - private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement | undefined): OutlineElement | undefined { - - item.score = pattern - ? fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true) - : FuzzyScore.Default; - - if (item.score && (!topMatch || !topMatch.score || item.score[0] > topMatch.score[0])) { - topMatch = item; - } - for (const key in item.children) { - let child = item.children[key]; - topMatch = this._updateMatches(pattern, child, topMatch); - if (!item.score && child.score) { - // don't filter parents with unfiltered children - item.score = FuzzyScore.Default; - } - } - return topMatch; - } - getItemEnclosingPosition(position: IPosition): OutlineElement | undefined { return position ? this._getItemEnclosingPosition(position, this.children) : undefined; } @@ -395,20 +366,6 @@ export class OutlineModel extends TreeElement { return true; } - private _matches: [string, OutlineElement | undefined]; - - updateMatches(pattern: string): OutlineElement | undefined { - if (this._matches && this._matches[0] === pattern) { - return this._matches[1]; - } - let topMatch: OutlineElement | undefined; - for (const key in this._groups) { - topMatch = this._groups[key].updateMatches(pattern, topMatch); - } - this._matches = [pattern, topMatch]; - return topMatch; - } - getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement | undefined { let preferredGroup: OutlineGroup | undefined; diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree2.ts b/src/vs/editor/contrib/documentSymbols/outlineTree2.ts new file mode 100644 index 00000000000..7caf7c772ec --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/outlineTree2.ts @@ -0,0 +1,247 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from 'vs/base/browser/dom'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; +import { values } from 'vs/base/common/collections'; +import { createMatches, FuzzyScore } from 'vs/base/common/filters'; +import 'vs/css!./media/outlineTree'; +import 'vs/css!./media/symbol-icons'; +import { Range } from 'vs/editor/common/core/range'; +import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes'; +import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { localize } from 'vs/nls'; +import { IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline'; + +export type NOutlineItem = OutlineGroup | OutlineElement; + +export class NOutlineNavigationLabelProvider implements IKeyboardNavigationLabelProvider { + + constructor(@IKeybindingService private readonly _keybindingService: IKeybindingService) { } + + getKeyboardNavigationLabel(element: NOutlineItem): { toString(): string; } { + if (element instanceof OutlineGroup) { + return element.provider.displayName; + } else { + return element.symbol.name; + } + } + + mightProducePrintableCharacter(event: IKeyboardEvent): boolean { + return this._keybindingService.mightProducePrintableCharacter(event); + } +} + + +export class NOutlineIdentityProvider implements IIdentityProvider { + getId(element: TreeElement): { toString(): string; } { + return element.id; + } +} + +export class NOutlineGroupTemplate { + static id = 'OutlineGroupTemplate'; + + labelContainer: HTMLElement; + label: HighlightedLabel; +} + +export class NOutlineElementTemplate { + static id = 'OutlineElementTemplate'; + iconLabel: IconLabel; + decoration: HTMLElement; +} + +export class NOutlineVirtualDelegate implements IListVirtualDelegate { + + getHeight(_element: NOutlineItem): number { + return 22; + } + + getTemplateId(element: NOutlineItem): string { + if (element instanceof OutlineGroup) { + return NOutlineGroupTemplate.id; + } else { + return NOutlineElementTemplate.id; + } + } +} + +export class NOutlineGroupRenderer implements ITreeRenderer { + + readonly templateId: string = NOutlineGroupTemplate.id; + + renderTemplate(container: HTMLElement): NOutlineGroupTemplate { + const labelContainer = dom.$('.outline-element-label'); + dom.addClass(container, 'outline-element'); + dom.append(container, labelContainer); + return { labelContainer, label: new HighlightedLabel(labelContainer, true) }; + } + + renderElement(node: ITreeNode, index: number, template: NOutlineGroupTemplate): void { + template.label.set( + node.element.provider.displayName || localize('provider', "Outline Provider"), + createMatches(node.filterData) + ); + } + + disposeTemplate(_template: NOutlineGroupTemplate): void { + // nothing + } +} + +export class NOutlineElementRenderer implements ITreeRenderer { + + readonly templateId: string = NOutlineElementTemplate.id; + + renderProblemColors = true; + renderProblemBadges = true; + + constructor( + // @IMarkerService private readonly _markerService: IMarkerService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + ) { } + + renderTemplate(container: HTMLElement): NOutlineElementTemplate { + // const labelContainer = dom.$('.outline-element-label'); + const iconLabel = new IconLabel(container, { supportHighlights: true }); + const decoration = dom.$('.outline-element-decoration'); + container.appendChild(decoration); + return { iconLabel, decoration }; + } + + renderElement(node: ITreeNode, index: number, template: NOutlineElementTemplate): void { + const { element } = node; + const options = { + matches: createMatches(node.filterData), + extraClasses: [], + title: localize('title.template', "{0} ({1})", element.symbol.name, NOutlineElementRenderer._symbolKindNames[element.symbol.kind]) + }; + if (this._configurationService.getValue(OutlineConfigKeys.icons)) { + options.extraClasses.push(`outline-element-icon ${symbolKindToCssClass(element.symbol.kind, true)}`); + } + template.iconLabel.setLabel(element.symbol.name, element.symbol.detail, options); + // this._renderMarkerInfo(element, template); + } + + // private _renderMarkerInfo(element: OutlineElement, template: NOutlineElementTemplate): void { + + // if (!element.marker) { + // dom.hide(template.decoration); + // template.labelContainer.style.removeProperty('--outline-element-color'); + // return; + // } + + // const { count, topSev } = element.marker; + // const color = this._themeService.getTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground); + // const cssColor = color ? color.toString() : 'inherit'; + + // // color of the label + // if (this.renderProblemColors) { + // template.labelContainer.style.setProperty('--outline-element-color', cssColor); + // } else { + // template.labelContainer.style.removeProperty('--outline-element-color'); + // } + + // // badge with color/rollup + // if (!this.renderProblemBadges) { + // dom.hide(template.decoration); + + // } else if (count > 0) { + // dom.show(template.decoration); + // dom.removeClass(template.decoration, 'bubble'); + // template.decoration.innerText = count < 10 ? count.toString() : '+9'; + // template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count); + // template.decoration.style.setProperty('--outline-element-color', cssColor); + + // } else { + // dom.show(template.decoration); + // dom.addClass(template.decoration, 'bubble'); + // template.decoration.innerText = '\uf052'; + // template.decoration.title = localize('deep.problem', "Contains elements with problems"); + // template.decoration.style.setProperty('--outline-element-color', cssColor); + // } + // } + + private static _symbolKindNames: { [symbol: number]: string } = { + [SymbolKind.Array]: localize('Array', "array"), + [SymbolKind.Boolean]: localize('Boolean', "boolean"), + [SymbolKind.Class]: localize('Class', "class"), + [SymbolKind.Constant]: localize('Constant', "constant"), + [SymbolKind.Constructor]: localize('Constructor', "constructor"), + [SymbolKind.Enum]: localize('Enum', "enumeration"), + [SymbolKind.EnumMember]: localize('EnumMember', "enumeration member"), + [SymbolKind.Event]: localize('Event', "event"), + [SymbolKind.Field]: localize('Field', "field"), + [SymbolKind.File]: localize('File', "file"), + [SymbolKind.Function]: localize('Function', "function"), + [SymbolKind.Interface]: localize('Interface', "interface"), + [SymbolKind.Key]: localize('Key', "key"), + [SymbolKind.Method]: localize('Method', "method"), + [SymbolKind.Module]: localize('Module', "module"), + [SymbolKind.Namespace]: localize('Namespace', "namespace"), + [SymbolKind.Null]: localize('Null', "null"), + [SymbolKind.Number]: localize('Number', "number"), + [SymbolKind.Object]: localize('Object', "object"), + [SymbolKind.Operator]: localize('Operator', "operator"), + [SymbolKind.Package]: localize('Package', "package"), + [SymbolKind.Property]: localize('Property', "property"), + [SymbolKind.String]: localize('String', "string"), + [SymbolKind.Struct]: localize('Struct', "struct"), + [SymbolKind.TypeParameter]: localize('TypeParameter', "type parameter"), + [SymbolKind.Variable]: localize('Variable', "variable"), + }; + + disposeTemplate(_template: NOutlineElementTemplate): void { + _template.iconLabel.dispose(); + } +} + +export const enum NOutlineItemCompareType { + ByPosition, + ByName, + ByKind +} + +export class NOutlineItemComparator implements ITreeSorter { + + constructor( + public type: NOutlineItemCompareType = NOutlineItemCompareType.ByPosition + ) { } + + compare(a: NOutlineItem, b: NOutlineItem): number { + if (a instanceof OutlineGroup && b instanceof OutlineGroup) { + return a.providerIndex - b.providerIndex; + + } else if (a instanceof OutlineElement && b instanceof OutlineElement) { + if (this.type === NOutlineItemCompareType.ByKind) { + return a.symbol.kind - b.symbol.kind || a.symbol.name.localeCompare(b.symbol.name); + } else if (this.type === NOutlineItemCompareType.ByName) { + return a.symbol.name.localeCompare(b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); + } else if (this.type === NOutlineItemCompareType.ByPosition) { + return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || a.symbol.name.localeCompare(b.symbol.name); + } + } + return 0; + } +} + +export class NOutlineDataSource implements IDataSource { + + getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement): NOutlineItem[] { + if (!element) { + return []; + } + return values(element.children); + } +} diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index f071f08339a..e284b99ae0d 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -21,6 +21,7 @@ import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { AbstractTree } from 'vs/base/browser/ui/tree/abstractTree'; export abstract class Viewlet extends Composite implements IViewlet { @@ -195,7 +196,7 @@ export class CollapseAction extends Action { // Collapse All action for the new tree export class CollapseAction2 extends Action { - constructor(tree: AsyncDataTree, enabled: boolean, clazz: string) { + constructor(tree: AsyncDataTree | AbstractTree, enabled: boolean, clazz: string) { super('workbench.action.collapse', nls.localize('collapse', "Collapse All"), clazz, enabled, () => { tree.collapseAll(); return Promise.resolve(undefined); diff --git a/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts b/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts index 4b85e7261a2..408be2b2e66 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts @@ -5,11 +5,11 @@ import { localize } from 'vs/nls'; import { ViewsRegistry, IViewDescriptor } from 'vs/workbench/common/views'; -import { OutlinePanel } from './outlinePanel'; +import { OutlinePanel } from './outlinePanel2'; import { VIEW_CONTAINER } from 'vs/workbench/parts/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { OutlineConfigKeys, OutlineViewId } from 'vs/workbench/parts/outline/electron-browser/outline'; +import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymbols/outline'; const _outlineDesc = { id: OutlineViewId, diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css index c8efd60317f..950fb41088b 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css @@ -12,6 +12,7 @@ width: 100%; height: 2px; padding-bottom: 3px; + position: absolute; } .monaco-workbench .outline-panel .outline-progress .monaco-progress-container { @@ -22,20 +23,6 @@ height: 2px; } -.monaco-workbench .outline-panel .outline-input { - box-sizing: border-box; - padding: 2px 9px 5px 9px; - position: relative; -} - -.monaco-workbench .outline-panel .outline-input .monaco-inputbox { - width: 100%; -} - -.monaco-workbench .outline-panel .outline-input .monaco-inputbox .input { - padding-right: 22px; -} - .monaco-workbench .outline-panel .outline-tree { height: 100%; } @@ -50,10 +37,6 @@ display: inherit; } -.monaco-workbench .outline-panel.message .outline-input { - display: none; -} - .monaco-workbench .outline-panel.message .outline-progress { display: none; } @@ -72,7 +55,3 @@ /* allows text color to use the default when selected */ color: inherit !important; } - -.monaco-workbench .outline-panel.no-icons .outline-element .outline-element-icon { - display: none; -} diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts similarity index 68% rename from src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts rename to src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts index f2fda75d216..d028269edaa 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts @@ -5,26 +5,20 @@ import { posix } from 'path'; import * as dom from 'vs/base/browser/dom'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { Action, IAction, RadioGroup } from 'vs/base/common/actions'; import { firstIndex } from 'vs/base/common/arrays'; import { createCancelablePromise, TimeoutTimer } from 'vs/base/common/async'; -import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; import { defaultGenerator } from 'vs/base/common/idGenerator'; -import { KeyCode } from 'vs/base/common/keyCodes'; import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { escape } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { ITree } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./outlinePanel'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; @@ -34,24 +28,25 @@ import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureR import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { WorkbenchTree } from 'vs/platform/list/browser/listService'; +import { WorkbenchDataTree } from 'vs/platform/list/browser/listService'; import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { attachInputBoxStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler'; +import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { CollapseAction } from 'vs/workbench/browser/viewlet'; -import { IViewsService } from 'vs/workbench/common/views'; +import { CollapseAction2 } from 'vs/workbench/browser/viewlet'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineItemCompareType, OutlineItemFilter, OutlineRenderer, OutlineTreeState } from 'vs/editor/contrib/documentSymbols/outlineTree'; -import { OutlineConfigKeys, OutlineViewFiltered, OutlineViewFocused, OutlineViewId } from './outline'; +import { OutlineConfigKeys, OutlineViewFiltered, OutlineViewFocused } from '../../../../editor/contrib/documentSymbols/outline'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { NOutlineDataSource, NOutlineItemComparator, NOutlineItemCompareType, NOutlineVirtualDelegate, NOutlineGroupRenderer, NOutlineElementRenderer, NOutlineItem, NOutlineIdentityProvider, NOutlineNavigationLabelProvider } from 'vs/editor/contrib/documentSymbols/outlineTree2'; +import { IDataTreeViewState } from 'vs/base/browser/ui/tree/dataTree'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; class RequestState { @@ -164,7 +159,7 @@ class OutlineViewState { private _followCursor = false; private _filterOnType = true; - private _sortBy = OutlineItemCompareType.ByKind; + private _sortBy = NOutlineItemCompareType.ByKind; private _onDidChange = new Emitter<{ followCursor?: boolean, sortBy?: boolean, filterOnType?: boolean }>(); readonly onDidChange = this._onDidChange.event; @@ -191,14 +186,14 @@ class OutlineViewState { } } - set sortBy(value: OutlineItemCompareType) { + set sortBy(value: NOutlineItemCompareType) { if (value !== this._sortBy) { this._sortBy = value; this._onDidChange.fire({ sortBy: true }); } } - get sortBy(): OutlineItemCompareType { + get sortBy(): NOutlineItemCompareType { return this._sortBy; } @@ -233,14 +228,14 @@ export class OutlinePanel extends ViewletPanel { private _domNode: HTMLElement; private _message: HTMLDivElement; private _inputContainer: HTMLDivElement; - private _input: InputBox; private _progressBar: ProgressBar; - private _tree: WorkbenchTree; - private _treeDataSource: OutlineDataSource; - private _treeRenderer: OutlineRenderer; - private _treeFilter: OutlineItemFilter; - private _treeComparator: OutlineItemComparator; - private _treeStates = new LRUCache(10); + private _tree: WorkbenchDataTree; + private _treeDataSource: NOutlineDataSource; + private _treeRenderer: NOutlineElementRenderer; + private _treeComparator: NOutlineItemComparator; + private _treeStates = new LRUCache(10); + + private _treeFakeUIEvent = new UIEvent('me'); private readonly _contextKeyFocused: IContextKey; private readonly _contextKeyFiltered: IContextKey; @@ -253,12 +248,12 @@ export class OutlinePanel extends ViewletPanel { @IEditorService private readonly _editorService: IEditorService, @IMarkerService private readonly _markerService: IMarkerService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IKeybindingService keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, ) { - super(options, _keybindingService, contextMenuService, configurationService); + super(options, keybindingService, contextMenuService, configurationService); this._outlineViewState.restore(this._storageService); this._contextKeyFocused = OutlineViewFocused.bindTo(contextKeyService); this._contextKeyFiltered = OutlineViewFiltered.bindTo(contextKeyService); @@ -303,73 +298,33 @@ export class OutlinePanel extends ViewletPanel { progressContainer, this._message, this._inputContainer, treeContainer ); - this._input = new InputBox(this._inputContainer, null, { - placeholder: this._outlineViewState.filterOnType ? localize('filter.placeholder', "Filter") : localize('find.placeholder', "Find") - }); - this._input.disable(); - - this.disposables.push(attachInputBoxStyler(this._input, this._themeService)); - this.disposables.push(dom.addStandardDisposableListener(this._input.inputElement, 'keyup', event => { - if (event.keyCode === KeyCode.DownArrow) { - this._tree.focusNext(); - this._tree.domFocus(); - } else if (event.keyCode === KeyCode.UpArrow) { - this._tree.focusPrevious(); - this._tree.domFocus(); - } else if (event.keyCode === KeyCode.Enter) { - let element = this._tree.getFocus(); - if (element instanceof OutlineElement) { - this._revealTreeSelection(OutlineModel.get(element), element, true, false); - } - } else if (event.keyCode === KeyCode.Escape) { - this._input.value = ''; - this._tree.domFocus(); + this._treeRenderer = this._instantiationService.createInstance(NOutlineElementRenderer); + this._treeDataSource = new NOutlineDataSource(); + this._treeComparator = new NOutlineItemComparator(this._outlineViewState.sortBy); + this._tree = this._instantiationService.createInstance( + WorkbenchDataTree, + treeContainer, + new NOutlineVirtualDelegate(), + [new NOutlineGroupRenderer(), this._treeRenderer], + this._treeDataSource, + { + filterOnType: this._outlineViewState.filterOnType, + sorter: new NOutlineItemComparator(), + identityProvider: new NOutlineIdentityProvider(), + keyboardNavigationLabelProvider: this._instantiationService.createInstance(NOutlineNavigationLabelProvider) } - })); - - const $this = this; - const controller = new class extends OutlineController { - - constructor() { - super({}, $this.configurationService); - } - - onKeyDown(tree: ITree, event: IKeyboardEvent) { - let handled = super.onKeyDown(tree, event); - if (handled) { - return true; - } - if (this.upKeyBindingDispatcher.has(event.keyCode)) { - return false; - } - // crazy -> during keydown focus moves to the input box - // and because of that the keyup event is handled by the - // input field - if ($this._keybindingService.mightProducePrintableCharacter(event)) { - $this._input.focus(); - return true; - } - return false; - } - }; - - this._treeRenderer = this._instantiationService.createInstance(OutlineRenderer); - this._treeDataSource = new OutlineDataSource(); - this._treeComparator = new OutlineItemComparator(this._outlineViewState.sortBy); - this._treeFilter = new OutlineItemFilter(); - this._tree = this._instantiationService.createInstance(WorkbenchTree, treeContainer, { controller, renderer: this._treeRenderer, dataSource: this._treeDataSource, sorter: this._treeComparator, filter: this._treeFilter }, {}); + ) as WorkbenchDataTree; this._treeRenderer.renderProblemColors = this._configurationService.getValue(OutlineConfigKeys.problemsColors); this._treeRenderer.renderProblemBadges = this._configurationService.getValue(OutlineConfigKeys.problemsBadges); - this._disposables.push(this._tree, this._input); + this._disposables.push(this._tree); this._disposables.push(this._outlineViewState.onDidChange(this._onDidChangeUserState, this)); // feature: toggle icons - dom.toggleClass(this._domNode, 'no-icons', !this._configurationService.getValue(OutlineConfigKeys.icons)); this.disposables.push(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(OutlineConfigKeys.icons)) { - dom.toggleClass(this._domNode, 'no-icons', !this._configurationService.getValue(OutlineConfigKeys.icons)); + this._tree.updateChildren(); } })); @@ -386,25 +341,23 @@ export class OutlinePanel extends ViewletPanel { protected layoutBody(height: number): void { if (height !== this._cachedHeight) { - this._cachedHeight = height; - const treeHeight = height - (5 /*progressbar height*/ + 33 /*input height*/); - this._tree.layout(treeHeight); + this._tree.layout(height); } } getActions(): IAction[] { return [ new Action('collapse', localize('collapse', "Collapse All"), 'explorer-action collapse-explorer', true, () => { - return new CollapseAction(this._tree, true, undefined).run(); + return new CollapseAction2(this._tree, true, undefined).run(); }) ]; } getSecondaryActions(): IAction[] { let group = new RadioGroup([ - new SimpleToggleAction(localize('sortByPosition', "Sort By: Position"), this._outlineViewState.sortBy === OutlineItemCompareType.ByPosition, _ => this._outlineViewState.sortBy = OutlineItemCompareType.ByPosition), - new SimpleToggleAction(localize('sortByName', "Sort By: Name"), this._outlineViewState.sortBy === OutlineItemCompareType.ByName, _ => this._outlineViewState.sortBy = OutlineItemCompareType.ByName), - new SimpleToggleAction(localize('sortByKind', "Sort By: Type"), this._outlineViewState.sortBy === OutlineItemCompareType.ByKind, _ => this._outlineViewState.sortBy = OutlineItemCompareType.ByKind), + new SimpleToggleAction(localize('sortByPosition', "Sort By: Position"), this._outlineViewState.sortBy === NOutlineItemCompareType.ByPosition, _ => this._outlineViewState.sortBy = NOutlineItemCompareType.ByPosition), + new SimpleToggleAction(localize('sortByName', "Sort By: Name"), this._outlineViewState.sortBy === NOutlineItemCompareType.ByName, _ => this._outlineViewState.sortBy = NOutlineItemCompareType.ByName), + new SimpleToggleAction(localize('sortByKind', "Sort By: Type"), this._outlineViewState.sortBy === NOutlineItemCompareType.ByKind, _ => this._outlineViewState.sortBy = NOutlineItemCompareType.ByKind), ]); let result = [ new SimpleToggleAction(localize('followCur', "Follow Cursor"), this._outlineViewState.followCursor, action => this._outlineViewState.followCursor = action.checked), @@ -425,10 +378,14 @@ export class OutlinePanel extends ViewletPanel { } if (e.sortBy) { this._treeComparator.type = this._outlineViewState.sortBy; - this._tree.refresh(undefined, true); + // todo@joh resort + // this._tree.refresh(undefined, true); + this._tree.updateChildren(); } if (e.filterOnType) { - this._applyTypeToFilter(); + this._tree.updateOptions({ + filterOnType: this._outlineViewState.filterOnType + }); } } @@ -455,10 +412,6 @@ export class OutlinePanel extends ViewletPanel { this._editorDisposables = new Array(); this._progressBar.infinite().show(150); - this._input.disable(); - if (!event) { - this._input.value = ''; - } if (!editor || !DocumentSymbolProviderRegistry.has(editor.getModel())) { return this._showMessage(localize('no-editor', "There are no editors open that can provide outline information.")); @@ -524,21 +477,19 @@ export class OutlinePanel extends ViewletPanel { this._progressBar.stop().hide(); if (oldModel && oldModel.merge(model)) { - this._tree.refresh(undefined, true); + this._tree.updateChildren(); model = oldModel; } else { // persist state if (oldModel) { - let state = OutlineTreeState.capture(this._tree); + let state = this._tree.getViewState(); this._treeStates.set(oldModel.textModel.uri.toString(), state); } - await this._tree.setInput(model); let state = this._treeStates.get(model.textModel.uri.toString()); - await OutlineTreeState.restore(this._tree, state, this); + await this._tree.setInput(model, state); } - this._input.enable(); this.layoutBody(this._cachedHeight); // transfer focus from domNode to the tree @@ -546,60 +497,30 @@ export class OutlinePanel extends ViewletPanel { this._tree.domFocus(); } - // feature: filter on type - // on type -> update filters - // on first type -> capture tree state - // on erase -> restore captured tree state - let beforePatternState: OutlineTreeState; - let onInputValueChanged = async pattern => { - - this._contextKeyFiltered.set(pattern.length > 0); - - if (pattern && !beforePatternState) { - beforePatternState = OutlineTreeState.capture(this._tree); - } - let item = model.updateMatches(pattern); - await this._tree.refresh(undefined, true); - if (item) { - await this._tree.expandAll(undefined /*all*/); - await this._tree.reveal(item); - this._tree.setFocus(item, this); - this._tree.setSelection([item], this); - } - - if (!pattern && beforePatternState) { - await OutlineTreeState.restore(this._tree, beforePatternState, this); - beforePatternState = undefined; - } - }; - if (this._input.value) { - onInputValueChanged(this._input.value); - } - this._editorDisposables.push(this._input.onDidChange(onInputValueChanged)); - this._editorDisposables.push(toDisposable(() => this._contextKeyFiltered.reset())); // feature: reveal outline selection in editor // on change -> reveal/select defining range this._editorDisposables.push(this._tree.onDidChangeSelection(e => { - if (e.payload === this || e.payload && e.payload.didClickOnTwistie) { + if (e.browserEvent === this._treeFakeUIEvent /* || e.payload && e.payload.didClickOnTwistie */) { return; } - let [first] = e.selection; + let [first] = e.elements; if (!(first instanceof OutlineElement)) { return; } let focus = false; let aside = false; - if (e.payload) { - if (e.payload.origin === 'keyboard') { + // todo@Joh + if (e.browserEvent) { + if (e.browserEvent.type === 'keydown') { focus = true; - - } else if (e.payload.origin === 'mouse' && e.payload.originalEvent instanceof StandardMouseEvent) { - let event = e.payload.originalEvent; - focus = event.detail === 2; - aside = !this._tree.useAltAsMultipleSelectionModifier && event.altKey || this._tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey); + } else if (e.browserEvent.type === 'click') { + const event = new StandardMouseEvent(e.browserEvent as MouseEvent); + focus = e.browserEvent.detail === 2; + aside = (!this._tree.useAltAsMultipleSelectionModifier && event.altKey) + || (this._tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey)); } } this._revealTreeSelection(model, first, focus, aside); @@ -628,7 +549,8 @@ export class OutlinePanel extends ViewletPanel { const marker = this._markerService.read({ resource: textModel.uri, severities: MarkerSeverity.Error | MarkerSeverity.Warning }); if (marker.length > 0 || !ignoreEmpty) { model.updateMarker(marker); - this._tree.refresh(undefined, true); + this._tree.updateChildren(); + // this._tree.refresh(undefined, true); } }; updateMarker([textModel.uri], true); @@ -638,7 +560,7 @@ export class OutlinePanel extends ViewletPanel { if (e.affectsConfiguration(OutlineConfigKeys.problemsBadges) || e.affectsConfiguration(OutlineConfigKeys.problemsColors)) { this._treeRenderer.renderProblemColors = this._configurationService.getValue(OutlineConfigKeys.problemsColors); this._treeRenderer.renderProblemBadges = this._configurationService.getValue(OutlineConfigKeys.problemsBadges); - this._tree.refresh(undefined, true); + this._tree.updateChildren(); return; } if (!e.affectsConfiguration(OutlineConfigKeys.problemsEnabled)) { @@ -646,29 +568,13 @@ export class OutlinePanel extends ViewletPanel { } if (!this._configurationService.getValue(OutlineConfigKeys.problemsEnabled)) { model.updateMarker([]); - this._tree.refresh(undefined, true); + this._tree.updateChildren(); } else { updateMarker([textModel.uri], true); } })); } - private _applyTypeToFilter(): void { - // depending on the user setting we filter or find elements - if (this._outlineViewState.filterOnType) { - this._treeFilter.enabled = true; - this._treeDataSource.filterOnScore = true; - this._input.setPlaceHolder(localize('filter', "Filter")); - } else { - this._treeFilter.enabled = false; - this._treeDataSource.filterOnScore = false; - this._input.setPlaceHolder(localize('find', "Find")); - } - if (this._tree.getInput()) { - this._tree.refresh(undefined, true); - } - } - private async _revealTreeSelection(model: OutlineModel, element: OutlineElement, focus: boolean, aside: boolean): Promise { await this._editorService.openEditor({ @@ -699,50 +605,7 @@ export class OutlinePanel extends ViewletPanel { // only when outside view port await this._tree.reveal(item, 0.5); } - this._tree.setFocus(item, this); - this._tree.setSelection([item], this); - } - - focusHighlightedElement(up: boolean): void { - if (!this._tree.getInput()) { - return; - } - if (!this._tree.isDOMFocused()) { - this._tree.domFocus(); - return; - } - let navi = this._tree.getNavigator(this._tree.getFocus(), false); - let candidate: any; - while (candidate = up ? navi.previous() : navi.next()) { - if (candidate instanceof OutlineElement && candidate.score && candidate.score[1] > 0) { - this._tree.setFocus(candidate, this); - this._tree.reveal(candidate).then(undefined, onUnexpectedError); - break; - } - } + this._tree.setFocus([item], this._treeFakeUIEvent); + this._tree.setSelection([item], this._treeFakeUIEvent); } } - -async function goUpOrDownToHighligthedElement(accessor: ServicesAccessor, prev: boolean) { - const viewsService = accessor.get(IViewsService); - const view = await viewsService.openView(OutlineViewId); - if (view instanceof OutlinePanel) { - view.focusHighlightedElement(prev); - } -} - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'outline.focusDownHighlighted', - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyCode.DownArrow, - when: ContextKeyExpr.and(OutlineViewFiltered, OutlineViewFocused), - handler: accessor => goUpOrDownToHighligthedElement(accessor, false) -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'outline.focusUpHighlighted', - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyCode.UpArrow, - when: ContextKeyExpr.and(OutlineViewFiltered, OutlineViewFocused), - handler: accessor => goUpOrDownToHighligthedElement(accessor, true) -}); From 9f59f4054872c4e49157be0825a1410bed5fdcca Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 5 Feb 2019 12:58:00 +0100 Subject: [PATCH 02/16] first stab at breadcrumb picker --- .../contrib/documentSymbols/outlineTree.ts | 315 -------------- .../contrib/documentSymbols/outlineTree2.ts | 3 - .../browser/parts/editor/breadcrumbsPicker.ts | 407 ++++++++++-------- 3 files changed, 219 insertions(+), 506 deletions(-) delete mode 100644 src/vs/editor/contrib/documentSymbols/outlineTree.ts diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts deleted file mode 100644 index 4c529be6e97..00000000000 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ /dev/null @@ -1,315 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as dom from 'vs/base/browser/dom'; -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; -import { values } from 'vs/base/common/collections'; -import { createMatches } from 'vs/base/common/filters'; -import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; -import 'vs/css!./media/outlineTree'; -import 'vs/css!./media/symbol-icons'; -import { Range } from 'vs/editor/common/core/range'; -import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes'; -import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import { localize } from 'vs/nls'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { WorkbenchTreeController } from 'vs/platform/list/browser/listService'; -import { MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; - -export const enum OutlineItemCompareType { - ByPosition, - ByName, - ByKind -} - -export class OutlineItemComparator implements ISorter { - - constructor( - public type: OutlineItemCompareType = OutlineItemCompareType.ByPosition - ) { } - - compare(tree: ITree, a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): number { - - if (a instanceof OutlineGroup && b instanceof OutlineGroup) { - return a.providerIndex - b.providerIndex; - } - - if (a instanceof OutlineElement && b instanceof OutlineElement) { - switch (this.type) { - case OutlineItemCompareType.ByKind: - return a.symbol.kind - b.symbol.kind; - case OutlineItemCompareType.ByName: - return a.symbol.name.localeCompare(b.symbol.name); - case OutlineItemCompareType.ByPosition: - default: - return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); - } - } - - return 0; - } -} - -export class OutlineItemFilter implements IFilter { - - enabled: boolean = true; - - isVisible(tree: ITree, element: OutlineElement | any): boolean { - if (!this.enabled) { - return true; - } - return !(element instanceof OutlineElement) || Boolean(element.score); - } -} - -export class OutlineDataSource implements IDataSource { - - // this is a workaround for the tree showing twisties for items - // with only filtered children - filterOnScore: boolean = true; - - getId(tree: ITree, element: TreeElement): string { - return element ? element.id : 'empty'; - } - - hasChildren(tree: ITree, element: OutlineModel | OutlineGroup | OutlineElement): boolean { - if (!element) { - return false; - } - if (element instanceof OutlineModel) { - return true; - } - if (element instanceof OutlineElement && (this.filterOnScore && !element.score)) { - return false; - } - for (const id in element.children) { - if (!this.filterOnScore || element.children[id].score) { - return true; - } - } - return false; - } - - getChildren(tree: ITree, element: TreeElement): Promise { - let res = values(element.children); - // console.log(element.id + ' with children ' + res.length); - return Promise.resolve(res); - } - - getParent(tree: ITree, element: TreeElement | any): Promise { - return Promise.resolve(element && element.parent); - } - - shouldAutoexpand(tree: ITree, element: TreeElement): boolean { - return element && (element instanceof OutlineModel || element.parent instanceof OutlineModel || element instanceof OutlineGroup || element.parent instanceof OutlineGroup); - } -} - -export interface OutlineTemplate { - labelContainer: HTMLElement; - label: HighlightedLabel; - icon?: HTMLElement; - detail?: HTMLElement; - decoration?: HTMLElement; -} - -export class OutlineRenderer implements IRenderer { - - renderProblemColors = true; - renderProblemBadges = true; - - constructor( - @IThemeService readonly _themeService: IThemeService, - @IConfigurationService readonly _configurationService: IConfigurationService - ) { - // - } - - getHeight(tree: ITree, element: any): number { - return 22; - } - - getTemplateId(tree: ITree, element: OutlineGroup | OutlineElement): string { - return element instanceof OutlineGroup ? 'outline-group' : 'outline-element'; - } - - renderTemplate(tree: ITree, templateId: string, container: HTMLElement): OutlineTemplate { - if (templateId === 'outline-element') { - const icon = dom.$('.outline-element-icon symbol-icon'); - const labelContainer = dom.$('.outline-element-label'); - const detail = dom.$('.outline-element-detail'); - const decoration = dom.$('.outline-element-decoration'); - dom.addClass(container, 'outline-element'); - dom.append(container, icon, labelContainer, detail, decoration); - return { icon, labelContainer, label: new HighlightedLabel(labelContainer, true), detail, decoration }; - } - if (templateId === 'outline-group') { - const labelContainer = dom.$('.outline-element-label'); - dom.addClass(container, 'outline-element'); - dom.append(container, labelContainer); - return { labelContainer, label: new HighlightedLabel(labelContainer, true) }; - } - - throw new Error(templateId); - } - - renderElement(tree: ITree, element: OutlineGroup | OutlineElement, templateId: string, template: OutlineTemplate): void { - if (element instanceof OutlineElement) { - template.icon.className = `outline-element-icon ${symbolKindToCssClass(element.symbol.kind)}`; - template.label.set(element.symbol.name, element.score ? createMatches(element.score) : undefined, localize('title.template', "{0} ({1})", element.symbol.name, OutlineRenderer._symbolKindNames[element.symbol.kind])); - template.detail.innerText = element.symbol.detail || ''; - this._renderMarkerInfo(element, template); - - } - if (element instanceof OutlineGroup) { - template.label.set(element.provider.displayName || localize('provider', "Outline Provider")); - } - } - - private _renderMarkerInfo(element: OutlineElement, template: OutlineTemplate): void { - - if (!element.marker) { - dom.hide(template.decoration); - template.labelContainer.style.removeProperty('--outline-element-color'); - return; - } - - const { count, topSev } = element.marker; - const color = this._themeService.getTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground); - const cssColor = color ? color.toString() : 'inherit'; - - // color of the label - if (this.renderProblemColors) { - template.labelContainer.style.setProperty('--outline-element-color', cssColor); - } else { - template.labelContainer.style.removeProperty('--outline-element-color'); - } - - // badge with color/rollup - if (!this.renderProblemBadges) { - dom.hide(template.decoration); - - } else if (count > 0) { - dom.show(template.decoration); - dom.removeClass(template.decoration, 'bubble'); - template.decoration.innerText = count < 10 ? count.toString() : '+9'; - template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count); - template.decoration.style.setProperty('--outline-element-color', cssColor); - - } else { - dom.show(template.decoration); - dom.addClass(template.decoration, 'bubble'); - template.decoration.innerText = '\uf052'; - template.decoration.title = localize('deep.problem', "Contains elements with problems"); - template.decoration.style.setProperty('--outline-element-color', cssColor); - } - } - - private static _symbolKindNames: { [symbol: number]: string } = { - [SymbolKind.Array]: localize('Array', "array"), - [SymbolKind.Boolean]: localize('Boolean', "boolean"), - [SymbolKind.Class]: localize('Class', "class"), - [SymbolKind.Constant]: localize('Constant', "constant"), - [SymbolKind.Constructor]: localize('Constructor', "constructor"), - [SymbolKind.Enum]: localize('Enum', "enumeration"), - [SymbolKind.EnumMember]: localize('EnumMember', "enumeration member"), - [SymbolKind.Event]: localize('Event', "event"), - [SymbolKind.Field]: localize('Field', "field"), - [SymbolKind.File]: localize('File', "file"), - [SymbolKind.Function]: localize('Function', "function"), - [SymbolKind.Interface]: localize('Interface', "interface"), - [SymbolKind.Key]: localize('Key', "key"), - [SymbolKind.Method]: localize('Method', "method"), - [SymbolKind.Module]: localize('Module', "module"), - [SymbolKind.Namespace]: localize('Namespace', "namespace"), - [SymbolKind.Null]: localize('Null', "null"), - [SymbolKind.Number]: localize('Number', "number"), - [SymbolKind.Object]: localize('Object', "object"), - [SymbolKind.Operator]: localize('Operator', "operator"), - [SymbolKind.Package]: localize('Package', "package"), - [SymbolKind.Property]: localize('Property', "property"), - [SymbolKind.String]: localize('String', "string"), - [SymbolKind.Struct]: localize('Struct', "struct"), - [SymbolKind.TypeParameter]: localize('TypeParameter', "type parameter"), - [SymbolKind.Variable]: localize('Variable', "variable"), - }; - - disposeTemplate(tree: ITree, templateId: string, template: OutlineTemplate): void { - // noop - } -} - -export class OutlineTreeState { - - readonly selected: string; - readonly focused: string; - readonly expanded: string[]; - - static capture(tree: ITree): OutlineTreeState { - // selection - let selected: string; - let element = tree.getSelection()[0]; - if (element instanceof TreeElement) { - selected = element.id; - } - - // focus - let focused: string; - element = tree.getFocus(true); - if (element instanceof TreeElement) { - focused = element.id; - } - - // expansion - let expanded = new Array(); - let nav = tree.getNavigator(); - while (nav.next()) { - let element = nav.current(); - if (element instanceof TreeElement) { - if (tree.isExpanded(element)) { - expanded.push(element.id); - } - } - } - return { selected, focused, expanded }; - } - - static async restore(tree: ITree, state: OutlineTreeState, eventPayload: any): Promise { - let model = tree.getInput(); - if (!state || !(model instanceof OutlineModel)) { - return Promise.resolve(undefined); - } - - // expansion - let items: TreeElement[] = []; - for (const id of state.expanded) { - let item = model.getItemById(id); - if (item) { - items.push(item); - } - } - await tree.collapseAll(undefined); - await tree.expandAll(items); - - // selection & focus - let selected = model.getItemById(state.selected); - let focused = model.getItemById(state.focused); - tree.setSelection([selected], eventPayload); - tree.setFocus(focused, eventPayload); - } -} - -export class OutlineController extends WorkbenchTreeController { - protected shouldToggleExpansion(element: any, event: IMouseEvent, origin: string): boolean { - if (element instanceof OutlineElement) { - return this.isClickOnTwistie(event); - } else { - return super.shouldToggleExpansion(element, event, origin); - } - } -} diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree2.ts b/src/vs/editor/contrib/documentSymbols/outlineTree2.ts index 7caf7c772ec..e4dcff776a5 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree2.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree2.ts @@ -16,9 +16,6 @@ import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; import { IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; -import { MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline'; diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index b027e26f87b..794dfc7e639 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -8,27 +8,27 @@ import * as dom from 'vs/base/browser/dom'; import { compareFileNames } from 'vs/base/common/comparers'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { createMatches, FuzzyScore, fuzzyScore } from 'vs/base/common/filters'; +import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import * as glob from 'vs/base/common/glob'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { join } from 'vs/base/common/paths'; import { basename, dirname, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./media/breadcrumbscontrol'; import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import { OutlineDataSource, OutlineItemComparator, OutlineRenderer, OutlineItemCompareType } from 'vs/editor/contrib/documentSymbols/outlineTree'; -import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { HighlightingWorkbenchTree, IHighlighter, IHighlightingTreeConfiguration, IHighlightingTreeOptions } from 'vs/platform/list/browser/listService'; +import { WorkbenchDataTree, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { breadcrumbsPickerBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ResourceLabels, IResourceLabel, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; +import { NOutlineVirtualDelegate, NOutlineGroupRenderer, NOutlineElementRenderer, NOutlineItemComparator, NOutlineIdentityProvider, NOutlineNavigationLabelProvider, NOutlineDataSource, NOutlineItemCompareType } from 'vs/editor/contrib/documentSymbols/outlineTree2'; +import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { let ctor: IConstructorSignature1 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; @@ -43,13 +43,16 @@ interface ILayoutInfo { inputHeight: number; } +type Tree = WorkbenchDataTree | WorkbenchAsyncDataTree; + export abstract class BreadcrumbsPicker { protected readonly _disposables = new Array(); protected readonly _domNode: HTMLDivElement; protected readonly _arrow: HTMLDivElement; protected readonly _treeContainer: HTMLDivElement; - protected readonly _tree: HighlightingWorkbenchTree; + protected readonly _tree: Tree; + protected readonly _fakeEvent = new UIEvent('fakeEvent'); protected readonly _focus: dom.IFocusTracker; protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>; private _layoutInfo: ILayoutInfo; @@ -93,38 +96,33 @@ export abstract class BreadcrumbsPicker { const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService); this._disposables.push(filterConfig); - const treeConfig = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined }); - this._tree = this._instantiationService.createInstance( - HighlightingWorkbenchTree, - this._treeContainer, - treeConfig, - { useShadows: false, filterOnType: filterConfig.getValue(), showTwistie: false, twistiePixels: 12 }, - { placeholder: localize('placeholder', "Find") } - ); + // const treeConfig = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined }); + // this._tree = this._instantiationService.createInstance( + // HighlightingWorkbenchTree, + // this._treeContainer, + // treeConfig, + // { useShadows: false, filterOnType: filterConfig.getValue(), showTwistie: false, twistiePixels: 12 }, + // { placeholder: localize('placeholder', "Find") } + // ); + this._tree = this._createTree(this._treeContainer); + this._disposables.push(this._tree.onDidChangeSelection(e => { - if (e.payload !== this._tree) { - const target = this._getTargetFromEvent(e.selection[0], e.payload); + if (e.browserEvent !== this._fakeEvent) { + const target = this._getTargetFromEvent(e.elements[0], e.browserEvent); if (target) { setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click - this._onDidPickElement.fire({ target, payload: e.payload }); + this._onDidPickElement.fire({ target, payload: undefined }); }, 0); } } })); this._disposables.push(this._tree.onDidChangeFocus(e => { - const target = this._getTargetFromEvent(e.focus, e.payload); + const target = this._getTargetFromEvent(e.elements[0], e.browserEvent); if (target) { - this._onDidFocusElement.fire({ target, payload: e.payload }); + this._onDidFocusElement.fire({ target, payload: undefined }); } })); - this._disposables.push(this._tree.onDidStartFiltering(() => { - this._layoutInfo.inputHeight = 36; - this._layout(); - })); - this._disposables.push(this._tree.onDidExpandItem(() => { - this._layout(); - })); - this._disposables.push(this._tree.onDidCollapseItem(() => { + this._disposables.push(this._tree.onDidChangeContentHeight(() => { this._layout(); })); @@ -150,37 +148,40 @@ export abstract class BreadcrumbsPicker { } setInput(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void { - let actualInput = this._getInput(input); - this._tree.setInput(actualInput).then(() => { - this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 }; + this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 }; + + this._setInput(input).then(() => { this._layout(); + }).catch(onUnexpectedError); - // use proper selection, reveal - let selection = this._getInitialSelection(this._tree, input); - if (selection) { - return this._tree.reveal(selection, 0.5).then(() => { - this._tree.setSelection([selection], this._tree); - this._tree.setFocus(selection); - this._tree.domFocus(); - }); - } else { - this._tree.focusFirst(); - this._tree.setSelection([this._tree.getFocus()], this._tree); - this._tree.domFocus(); - return Promise.resolve(null); - } - }, onUnexpectedError); + // let actualInput = this._getInput(input); + // Promise.resolve(this._tree.setInput(actualInput, undefined)).then(() => { + + // this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 }; + // this._layout(); + + // // use proper selection, reveal + // let selection = this._getInitialSelection(this._tree, input); + // if (selection) { + // return this._tree.reveal(selection, 0.5).then(() => { + // this._tree.setSelection([selection], this._tree); + // this._tree.setFocus(selection); + // this._tree.domFocus(); + // }); + // } else { + // this._tree.focusFirst(); + // this._tree.setSelection([this._tree.getFocus()], this._tree); + // this._tree.domFocus(); + // return Promise.resolve(null); + // } + // }, onUnexpectedError); } private _layout(info: ILayoutInfo = this._layoutInfo): void { - let count = 0; - let nav = this._tree.getNavigator(undefined, false); - while (nav.next() && count < 13) { count += 1; } - let headerHeight = 2 * info.arrowSize; - let treeHeight = Math.min(info.maxHeight - headerHeight, count * 22); + let treeHeight = Math.min(info.maxHeight - headerHeight, this._tree.visibleNodeCount * 22); let totalHeight = treeHeight + headerHeight; this._domNode.style.height = `${totalHeight}px`; @@ -195,23 +196,25 @@ export abstract class BreadcrumbsPicker { } - protected abstract _getInput(input: BreadcrumbElement): any; - protected abstract _getInitialSelection(tree: ITree, input: BreadcrumbElement): any; - protected abstract _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration; - protected abstract _getTargetFromEvent(element: any, payload: any): any | undefined; + protected abstract _setInput(element: BreadcrumbElement): Promise; + + protected abstract _createTree(container: HTMLElement): Tree; + protected abstract _getTargetFromEvent(element: any, payload: UIEvent): any | undefined; } //#region - Files -export class FileDataSource implements IDataSource { +class FileVirtualDelegate implements IListVirtualDelegate { + getHeight(_element: IFileStat | IWorkspaceFolder) { + return 22; + } + getTemplateId(_element: IFileStat | IWorkspaceFolder): string { + return 'FileStat'; + } +} - private readonly _parents = new WeakMap(); - - constructor( - @IFileService private readonly _fileService: IFileService, - ) { } - - getId(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): string { +class FileIdentityProvider implements IIdentityProvider { + getId(element: IWorkspace | IWorkspaceFolder | IFileStat | URI): { toString(): string; } { if (URI.isUri(element)) { return element.toString(); } else if (IWorkspace.isIWorkspace(element)) { @@ -222,12 +225,26 @@ export class FileDataSource implements IDataSource { return element.resource.toString(); } } +} - hasChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): boolean { - return URI.isUri(element) || IWorkspace.isIWorkspace(element) || IWorkspaceFolder.isIWorkspaceFolder(element) || element.isDirectory; + +class FileDataSource implements IAsyncDataSource { + + private readonly _parents = new WeakMap(); + + constructor( + @IFileService private readonly _fileService: IFileService, + ) { } + + hasChildren(element: IWorkspace | URI | IWorkspaceFolder | IFileStat): boolean { + return URI.isUri(element) + || IWorkspace.isIWorkspace(element) + || IWorkspaceFolder.isIWorkspaceFolder(element) + || element.isDirectory; } - getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): Promise { + getChildren(element: IWorkspace | URI | IWorkspaceFolder | IFileStat): Promise<(IWorkspaceFolder | IFileStat)[]> { + if (IWorkspace.isIWorkspace(element)) { return Promise.resolve(element.folders).then(folders => { for (let child of folders) { @@ -251,13 +268,56 @@ export class FileDataSource implements IDataSource { return stat.children; }); } +} - getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): Promise { - return Promise.resolve(this._parents.get(element)); +class FileRenderer implements ITreeRenderer { + + readonly templateId: string = 'FileStat'; + + constructor( + private readonly _labels: ResourceLabels, + @IConfigurationService private readonly _configService: IConfigurationService, + ) { } + + + renderTemplate(container: HTMLElement): IResourceLabel { + return this._labels.create(container, { supportHighlights: true }); + } + + renderElement(node: ITreeNode, index: number, templateData: IResourceLabel): void { + const fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); + const { element } = node; + let resource: URI; + let fileKind: FileKind; + if (IWorkspaceFolder.isIWorkspaceFolder(element)) { + resource = element.uri; + fileKind = FileKind.ROOT_FOLDER; + } else { + resource = element.resource; + fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE; + } + templateData.setFile(resource, { + fileKind, + hidePath: true, + fileDecorations: fileDecorations, + matches: createMatches(node.filterData), + extraClasses: ['picker-item'] + }); + } + + disposeTemplate(templateData: IResourceLabel): void { + templateData.dispose(); } } -export class FileFilter implements IFilter { +class FileNavigationLabelProvider implements IKeyboardNavigationLabelProvider { + + getKeyboardNavigationLabel(element: IWorkspaceFolder | IFileStat): { toString(): string; } { + return element.name; + } +} + +class FileFilter implements ITreeFilter { private readonly _cachedExpressions = new Map(); private readonly _disposables: IDisposable[] = []; @@ -301,7 +361,7 @@ export class FileFilter implements IFilter { dispose(this._disposables); } - isVisible(tree: ITree, element: IWorkspaceFolder | IFileStat): boolean { + filter(element: IWorkspaceFolder | IFileStat, _parentVisibility: TreeVisibility): boolean { if (IWorkspaceFolder.isIWorkspaceFolder(element)) { // not a file return true; @@ -317,72 +377,19 @@ export class FileFilter implements IFilter { } } -export class FileHighlighter implements IHighlighter { - getHighlightsStorageKey(element: IFileStat | IWorkspaceFolder): string { - return IWorkspaceFolder.isIWorkspaceFolder(element) ? element.uri.toString() : element.resource.toString(); - } - getHighlights(tree: ITree, element: IFileStat | IWorkspaceFolder, pattern: string): FuzzyScore { - return fuzzyScore(pattern, pattern.toLowerCase(), 0, element.name, element.name.toLowerCase(), 0, true); - } -} -export class FileRenderer implements IRenderer { - - constructor( - private readonly _labels: ResourceLabels, - @IConfigurationService private readonly _configService: IConfigurationService, - ) { } - - getHeight(tree: ITree, element: any): number { - return 22; - } - - getTemplateId(tree: ITree, element: any): string { - return 'FileStat'; - } - - renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { - return this._labels.create(container, { supportHighlights: true }); - } - - renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: IResourceLabel): void { - let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - let resource: URI; - let fileKind: FileKind; - if (IWorkspaceFolder.isIWorkspaceFolder(element)) { - resource = element.uri; - fileKind = FileKind.ROOT_FOLDER; - } else { - resource = element.resource; - fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE; - } - templateData.setFile(resource, { - fileKind, - hidePath: true, - fileDecorations: fileDecorations, - matches: createMatches((tree as HighlightingWorkbenchTree).getHighlighterScore(element)), - extraClasses: ['picker-item'] - }); - } - - disposeTemplate(tree: ITree, templateId: string, templateData: IResourceLabel): void { - templateData.dispose(); - } -} - -export class FileSorter implements ISorter { - compare(tree: ITree, a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number { +export class FileSorter implements ITreeSorter { + compare(a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number { if (IWorkspaceFolder.isIWorkspaceFolder(a) && IWorkspaceFolder.isIWorkspaceFolder(b)) { return a.index - b.index; + } + if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) { + // same type -> compare on names + return compareFileNames(a.name, b.name); + } else if ((a as IFileStat).isDirectory) { + return -1; } else { - if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) { - // same type -> compare on names - return compareFileNames(a.name, b.name); - } else if ((a as IFileStat).isDirectory) { - return -1; - } else { - return 1; - } + return 1; } } } @@ -399,44 +406,58 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { super(parent, instantiationService, themeService, configService); } - protected _getInput(input: BreadcrumbElement): any { - let { uri, kind } = (input as FileElement); - if (kind === FileKind.ROOT_FOLDER) { - return this._workspaceService.getWorkspace(); - } else { - return dirname(uri); - } - } + _createTree(container: HTMLElement) { - protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any { - let { uri } = (input as FileElement); - let nav = tree.getNavigator(); - while (nav.next()) { - let cur = nav.current(); - let candidate = IWorkspaceFolder.isIWorkspaceFolder(cur) ? cur.uri : (cur as IFileStat).resource; - if (isEqual(uri, candidate)) { - return cur; - } - } - return undefined; - } - - protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration { - // todo@joh reuse explorer implementations? - const filter = this._instantiationService.createInstance(FileFilter); - this._disposables.push(filter); - - config.dataSource = this._instantiationService.createInstance(FileDataSource); const labels = this._instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER /* TODO@Jo visibility propagation */); this._disposables.push(labels); - config.renderer = this._instantiationService.createInstance(FileRenderer, labels); - config.sorter = new FileSorter(); - config.highlighter = new FileHighlighter(); - config.filter = filter; - return config; + + return this._instantiationService.createInstance( + WorkbenchAsyncDataTree, + container, + new FileVirtualDelegate(), + [this._instantiationService.createInstance(FileRenderer, labels)], + this._instantiationService.createInstance(FileDataSource), + { + filterOnType: true, + sorter: new FileSorter(), + filter: this._instantiationService.createInstance(FileFilter), + identityProvider: new FileIdentityProvider(), + keyboardNavigationLabelProvider: new FileNavigationLabelProvider() + } + ) as WorkbenchAsyncDataTree; + } + + _setInput(element: BreadcrumbElement): Promise { + const { uri, kind } = (element as FileElement); + let input: IWorkspace | URI; + if (kind === FileKind.ROOT_FOLDER) { + input = this._workspaceService.getWorkspace(); + } else { + input = dirname(uri); + } + + const tree = this._tree as WorkbenchAsyncDataTree; + return tree.setInput(input).then(() => { + let focusElement: IWorkspaceFolder | IFileStat; + for (const { element } of tree.getNode().children) { + if (IWorkspaceFolder.isIWorkspaceFolder(element) && isEqual(element.uri, uri)) { + focusElement = element; + break; + } else if (isEqual((element as IFileStat).resource, uri)) { + focusElement = element as IFileStat; + break; + } + } + if (focusElement) { + tree.reveal(focusElement, 0.5); + tree.setFocus([focusElement], this._fakeEvent); + } + tree.domFocus(); + }); } protected _getTargetFromEvent(element: any, _payload: any): any | undefined { + // todo@joh if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) { return new FileElement((element as IFileStat).resource, FileKind.FILE); } @@ -446,35 +467,45 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { //#region - Symbols -class OutlineHighlighter implements IHighlighter { - getHighlights(tree: ITree, element: OutlineElement, pattern: string): FuzzyScore { - OutlineModel.get(element).updateMatches(pattern); - return element.score; - } -} - export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { - protected _getInput(input: BreadcrumbElement): any { - let element = input as TreeElement; - let model = OutlineModel.get(element); - model.updateMatches(''); - return model; + protected _createTree(container: HTMLElement) { + return this._instantiationService.createInstance( + WorkbenchDataTree, + container, + new NOutlineVirtualDelegate(), + [new NOutlineGroupRenderer(), this._instantiationService.createInstance(NOutlineElementRenderer)], + new NOutlineDataSource(), + { + filterOnType: true, + sorter: new NOutlineItemComparator(this._getOutlineItemCompareType()), + identityProvider: new NOutlineIdentityProvider(), + keyboardNavigationLabelProvider: this._instantiationService.createInstance(NOutlineNavigationLabelProvider) + } + ) as WorkbenchDataTree; } - protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any { - return input instanceof OutlineModel ? undefined : input; - } + protected _setInput(input: BreadcrumbElement): Promise { + const element = input as TreeElement; + const model = OutlineModel.get(element); + const tree = this._tree as WorkbenchDataTree; + tree.setInput(model); - protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration { - config.dataSource = this._instantiationService.createInstance(OutlineDataSource); - config.renderer = this._instantiationService.createInstance(OutlineRenderer); - config.sorter = new OutlineItemComparator(this._getOutlineItemComparator()); - config.highlighter = new OutlineHighlighter(); - return config; + let focusElement: TreeElement; + if (element === model) { + focusElement = tree.navigate().first(); + } else { + focusElement = element; + } + tree.reveal(focusElement, 0.5); + tree.setFocus([focusElement], this._fakeEvent); + tree.domFocus(); + + return Promise.resolve(); } protected _getTargetFromEvent(element: any, payload: any): any | undefined { + // todo@joh if (payload && payload.didClickOnTwistie) { return; } @@ -483,15 +514,15 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { } } - private _getOutlineItemComparator(): OutlineItemCompareType { + private _getOutlineItemCompareType(): NOutlineItemCompareType { switch (this._symbolSortOrder.getValue()) { case 'name': - return OutlineItemCompareType.ByName; + return NOutlineItemCompareType.ByName; case 'type': - return OutlineItemCompareType.ByKind; + return NOutlineItemCompareType.ByKind; case 'position': default: - return OutlineItemCompareType.ByPosition; + return NOutlineItemCompareType.ByPosition; } } } From 881c4c42aabdacdf416e17dce9353b477d155227 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 5 Feb 2019 17:45:22 +0100 Subject: [PATCH 03/16] adopt `expandOnlyOnTwistieClick` --- src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts | 1 + src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 794dfc7e639..8536a5989d0 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -478,6 +478,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { new NOutlineDataSource(), { filterOnType: true, + expandOnlyOnTwistieClick: true, sorter: new NOutlineItemComparator(this._getOutlineItemCompareType()), identityProvider: new NOutlineIdentityProvider(), keyboardNavigationLabelProvider: this._instantiationService.createInstance(NOutlineNavigationLabelProvider) diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts index d028269edaa..d775feea402 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts @@ -308,6 +308,7 @@ export class OutlinePanel extends ViewletPanel { [new NOutlineGroupRenderer(), this._treeRenderer], this._treeDataSource, { + expandOnlyOnTwistieClick: true, filterOnType: this._outlineViewState.filterOnType, sorter: new NOutlineItemComparator(), identityProvider: new NOutlineIdentityProvider(), From 8dbf25e4581c81bd3bed5341160948b56ab7a49b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 5 Feb 2019 17:47:58 +0100 Subject: [PATCH 04/16] remove old highlighting tree --- src/vs/platform/list/browser/listService.ts | 247 +------------------- 1 file changed, 3 insertions(+), 244 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 0a26a9aebf4..0d73ae92229 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -3,32 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addClass, addStandardDisposableListener, createStyleSheet, getTotalHeight, removeClass } from 'vs/base/browser/dom'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IInputOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { createStyleSheet } from 'vs/base/browser/dom'; import { IListMouseEvent, IListTouchEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer, PagedList } from 'vs/base/browser/ui/list/listPaging'; import { DefaultStyleController, IListOptions, IMultipleSelectionController, IOpenController, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, List } from 'vs/base/browser/ui/list/listWidget'; -import { canceled, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { FuzzyScore } from 'vs/base/common/filters'; -import { KeyCode } from 'vs/base/common/keyCodes'; import { combinedDisposable, Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import { IFilter, ITree, ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; +import { ITree, ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; import { ClickBehavior, DefaultController, DefaultTreestyler, IControllerOptions, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Registry } from 'vs/platform/registry/common/platform'; -import { attachInputBoxStyler, attachListStyler, computeStyles, defaultListStyles } from 'vs/platform/theme/common/styler'; +import { attachListStyler, computeStyles, defaultListStyles } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys'; import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; @@ -722,241 +716,6 @@ export class TreeResourceNavigator2 extends Disposable { } } -export interface IHighlighter { - getHighlights(tree: ITree, element: any, pattern: string): FuzzyScore; - getHighlightsStorageKey?(element: any): any; -} - -export interface IHighlightingTreeConfiguration extends ITreeConfiguration { - highlighter: IHighlighter; -} - -export interface IHighlightingTreeOptions extends ITreeOptions { - filterOnType?: boolean; -} - -export class HighlightingTreeController extends WorkbenchTreeController { - - constructor( - options: IControllerOptions, - private readonly onType: () => any, - @IConfigurationService configurationService: IConfigurationService, - @IKeybindingService private readonly _keybindingService: IKeybindingService, - ) { - super(options, configurationService); - } - - onKeyDown(tree: ITree, event: IKeyboardEvent) { - let handled = super.onKeyDown(tree, event); - if (handled) { - return true; - } - if (this.upKeyBindingDispatcher.has(event.keyCode)) { - return false; - } - if (this._keybindingService.mightProducePrintableCharacter(event)) { - this.onType(); - return true; - } - return false; - } -} - -class HightlightsFilter implements IFilter { - - static add(config: ITreeConfiguration, options: IHighlightingTreeOptions): ITreeConfiguration { - const myFilter = new HightlightsFilter(); - myFilter.enabled = !!options.filterOnType; - if (!config.filter) { - config.filter = myFilter; - } else { - let otherFilter = config.filter; - config.filter = { - isVisible(tree: ITree, element: any): boolean { - return myFilter.isVisible(tree, element) && otherFilter.isVisible(tree, element); - } - }; - } - return config; - } - - enabled: boolean = true; - - isVisible(tree: ITree, element: any): boolean { - if (!this.enabled) { - return true; - } - let tree2 = (tree as HighlightingWorkbenchTree); - if (!tree2.isHighlighterScoring()) { - return true; - } - if (tree2.getHighlighterScore(element)) { - return true; - } - return false; - } -} - -export class HighlightingWorkbenchTree extends WorkbenchTree { - - protected readonly domNode: HTMLElement; - protected readonly inputContainer: HTMLElement; - protected readonly input: InputBox; - - protected readonly highlighter: IHighlighter; - protected readonly highlights: Map; - - private readonly _onDidStartFilter: Emitter; - readonly onDidStartFiltering: Event; - - constructor( - parent: HTMLElement, - treeConfiguration: IHighlightingTreeConfiguration, - treeOptions: IHighlightingTreeOptions, - listOptions: IInputOptions, - @IContextKeyService contextKeyService: IContextKeyService, - @IContextViewService contextViewService: IContextViewService, - @IListService listService: IListService, - @IThemeService themeService: IThemeService, - @IInstantiationService instantiationService: IInstantiationService, - @IConfigurationService configurationService: IConfigurationService - ) { - // build html skeleton - const container = document.createElement('div'); - container.className = 'highlighting-tree'; - const inputContainer = document.createElement('div'); - inputContainer.className = 'input'; - const treeContainer = document.createElement('div'); - treeContainer.className = 'tree'; - container.appendChild(inputContainer); - container.appendChild(treeContainer); - parent.appendChild(container); - - // create tree - treeConfiguration.controller = treeConfiguration.controller || instantiationService.createInstance(HighlightingTreeController, {}, () => this.onTypeInTree()); - super(treeContainer, HightlightsFilter.add(treeConfiguration, treeOptions), treeOptions, contextKeyService, listService, themeService, instantiationService, configurationService); - this.highlighter = treeConfiguration.highlighter; - this.highlights = new Map(); - - this.domNode = container; - addClass(this.domNode, 'inactive'); - - // create input - this.inputContainer = inputContainer; - this.input = new InputBox(inputContainer, contextViewService, listOptions); - this.input.setEnabled(false); - this.input.onDidChange(this.updateHighlights, this, this.disposables); - this.disposables.push(attachInputBoxStyler(this.input, themeService)); - this.disposables.push(this.input); - this.disposables.push(addStandardDisposableListener(this.input.inputElement, 'keydown', event => { - //todo@joh make this command/context-key based - switch (event.keyCode) { - case KeyCode.UpArrow: - case KeyCode.DownArrow: - case KeyCode.Tab: - this.domFocus(); - event.preventDefault(); - break; - case KeyCode.Enter: - this.setSelection(this.getSelection()); - event.preventDefault(); - break; - case KeyCode.Escape: - this.input.value = ''; - this.domFocus(); - event.preventDefault(); - break; - } - })); - - this._onDidStartFilter = new Emitter(); - this.onDidStartFiltering = this._onDidStartFilter.event; - this.disposables.push(this._onDidStartFilter); - } - - setInput(element: any): Promise { - this.input.setEnabled(false); - return super.setInput(element).then(value => { - if (!this.input.inputElement) { - // has been disposed in the meantime -> cancel - return Promise.reject(canceled()); - } - this.input.setEnabled(true); - return value; - }); - } - - layout(height?: number, width?: number): void { - this.input.layout(); - super.layout(typeof height !== 'number' || isNaN(height) ? height : height - getTotalHeight(this.inputContainer), width); - } - - private onTypeInTree(): void { - removeClass(this.domNode, 'inactive'); - this.input.focus(); - this.layout(); - this._onDidStartFilter.fire(this); - } - - private lastSelection: any[]; - - private updateHighlights(pattern: string): void { - - // remember old selection - let defaultSelection: any[] = []; - if (!this.lastSelection && pattern) { - this.lastSelection = this.getSelection(); - } else if (this.lastSelection && !pattern) { - defaultSelection = this.lastSelection; - this.lastSelection = []; - } - - let topElement: any; - if (pattern) { - let nav = this.getNavigator(undefined, false); - let topScore: FuzzyScore | undefined; - while (nav.next()) { - let element = nav.current(); - let score = this.highlighter.getHighlights(this, element, pattern); - this.highlights.set(this._getHighlightsStorageKey(element), score); - element.foo = 1; - if (!topScore || score && topScore[0] < score[0]) { - topScore = score; - topElement = element; - } - } - } else { - // no pattern, clear highlights - this.highlights.clear(); - } - - this.refresh().then(() => { - if (topElement) { - this.reveal(topElement, 0.5).then(_ => { - this.setSelection([topElement], this); - this.setFocus(topElement, this); - }); - } else { - this.setSelection(defaultSelection, this); - } - }, onUnexpectedError); - } - - isHighlighterScoring(): boolean { - return this.highlights.size > 0; - } - - getHighlighterScore(element: any): FuzzyScore | undefined { - return this.highlights.get(this._getHighlightsStorageKey(element)); - } - - private _getHighlightsStorageKey(element: any): any { - return typeof this.highlighter.getHighlightsStorageKey === 'function' - ? this.highlighter.getHighlightsStorageKey(element) - : element; - } -} - function createKeyboardNavigationEventFilter(container: HTMLElement, keybindingService: IKeybindingService): IKeyboardNavigationEventFilter { let inChord = false; From 77d4906990a1e9a418e004a1126ee5ccea2abced Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 5 Feb 2019 18:06:41 +0100 Subject: [PATCH 05/16] :lipstick: --- .../contrib/documentSymbols/outlineTree2.ts | 62 ++++++------ .../browser/parts/editor/breadcrumbsPicker.ts | 95 ++++++++----------- .../outline/electron-browser/outlinePanel2.ts | 40 ++++---- 3 files changed, 92 insertions(+), 105 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree2.ts b/src/vs/editor/contrib/documentSymbols/outlineTree2.ts index e4dcff776a5..2e54137a43b 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree2.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree2.ts @@ -20,13 +20,13 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline'; -export type NOutlineItem = OutlineGroup | OutlineElement; +export type OutlineItem = OutlineGroup | OutlineElement; -export class NOutlineNavigationLabelProvider implements IKeyboardNavigationLabelProvider { +export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelProvider { constructor(@IKeybindingService private readonly _keybindingService: IKeybindingService) { } - getKeyboardNavigationLabel(element: NOutlineItem): { toString(): string; } { + getKeyboardNavigationLabel(element: OutlineItem): { toString(): string; } { if (element instanceof OutlineGroup) { return element.provider.displayName; } else { @@ -40,66 +40,66 @@ export class NOutlineNavigationLabelProvider implements IKeyboardNavigationLabel } -export class NOutlineIdentityProvider implements IIdentityProvider { +export class OutlineIdentityProvider implements IIdentityProvider { getId(element: TreeElement): { toString(): string; } { return element.id; } } -export class NOutlineGroupTemplate { +export class OutlineGroupTemplate { static id = 'OutlineGroupTemplate'; labelContainer: HTMLElement; label: HighlightedLabel; } -export class NOutlineElementTemplate { +export class OutlineElementTemplate { static id = 'OutlineElementTemplate'; iconLabel: IconLabel; decoration: HTMLElement; } -export class NOutlineVirtualDelegate implements IListVirtualDelegate { +export class OutlineVirtualDelegate implements IListVirtualDelegate { - getHeight(_element: NOutlineItem): number { + getHeight(_element: OutlineItem): number { return 22; } - getTemplateId(element: NOutlineItem): string { + getTemplateId(element: OutlineItem): string { if (element instanceof OutlineGroup) { - return NOutlineGroupTemplate.id; + return OutlineGroupTemplate.id; } else { - return NOutlineElementTemplate.id; + return OutlineElementTemplate.id; } } } -export class NOutlineGroupRenderer implements ITreeRenderer { +export class OutlineGroupRenderer implements ITreeRenderer { - readonly templateId: string = NOutlineGroupTemplate.id; + readonly templateId: string = OutlineGroupTemplate.id; - renderTemplate(container: HTMLElement): NOutlineGroupTemplate { + renderTemplate(container: HTMLElement): OutlineGroupTemplate { const labelContainer = dom.$('.outline-element-label'); dom.addClass(container, 'outline-element'); dom.append(container, labelContainer); return { labelContainer, label: new HighlightedLabel(labelContainer, true) }; } - renderElement(node: ITreeNode, index: number, template: NOutlineGroupTemplate): void { + renderElement(node: ITreeNode, index: number, template: OutlineGroupTemplate): void { template.label.set( node.element.provider.displayName || localize('provider', "Outline Provider"), createMatches(node.filterData) ); } - disposeTemplate(_template: NOutlineGroupTemplate): void { + disposeTemplate(_template: OutlineGroupTemplate): void { // nothing } } -export class NOutlineElementRenderer implements ITreeRenderer { +export class OutlineElementRenderer implements ITreeRenderer { - readonly templateId: string = NOutlineElementTemplate.id; + readonly templateId: string = OutlineElementTemplate.id; renderProblemColors = true; renderProblemBadges = true; @@ -109,7 +109,7 @@ export class NOutlineElementRenderer implements ITreeRenderer, index: number, template: NOutlineElementTemplate): void { + renderElement(node: ITreeNode, index: number, template: OutlineElementTemplate): void { const { element } = node; const options = { matches: createMatches(node.filterData), extraClasses: [], - title: localize('title.template', "{0} ({1})", element.symbol.name, NOutlineElementRenderer._symbolKindNames[element.symbol.kind]) + title: localize('title.template', "{0} ({1})", element.symbol.name, OutlineElementRenderer._symbolKindNames[element.symbol.kind]) }; if (this._configurationService.getValue(OutlineConfigKeys.icons)) { options.extraClasses.push(`outline-element-icon ${symbolKindToCssClass(element.symbol.kind, true)}`); @@ -199,33 +199,33 @@ export class NOutlineElementRenderer implements ITreeRenderer { +export class OutlineItemComparator implements ITreeSorter { constructor( - public type: NOutlineItemCompareType = NOutlineItemCompareType.ByPosition + public type: OutlineSortOrder = OutlineSortOrder.ByPosition ) { } - compare(a: NOutlineItem, b: NOutlineItem): number { + compare(a: OutlineItem, b: OutlineItem): number { if (a instanceof OutlineGroup && b instanceof OutlineGroup) { return a.providerIndex - b.providerIndex; } else if (a instanceof OutlineElement && b instanceof OutlineElement) { - if (this.type === NOutlineItemCompareType.ByKind) { + if (this.type === OutlineSortOrder.ByKind) { return a.symbol.kind - b.symbol.kind || a.symbol.name.localeCompare(b.symbol.name); - } else if (this.type === NOutlineItemCompareType.ByName) { + } else if (this.type === OutlineSortOrder.ByName) { return a.symbol.name.localeCompare(b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); - } else if (this.type === NOutlineItemCompareType.ByPosition) { + } else if (this.type === OutlineSortOrder.ByPosition) { return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || a.symbol.name.localeCompare(b.symbol.name); } } @@ -233,9 +233,9 @@ export class NOutlineItemComparator implements ITreeSorter { } } -export class NOutlineDataSource implements IDataSource { +export class OutlineDataSource implements IDataSource { - getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement): NOutlineItem[] { + getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement): OutlineItem[] { if (!element) { return []; } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 8536a5989d0..6b9431ed154 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -27,11 +27,14 @@ import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; -import { NOutlineVirtualDelegate, NOutlineGroupRenderer, NOutlineElementRenderer, NOutlineItemComparator, NOutlineIdentityProvider, NOutlineNavigationLabelProvider, NOutlineDataSource, NOutlineItemCompareType } from 'vs/editor/contrib/documentSymbols/outlineTree2'; +import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineItem } from 'vs/editor/contrib/documentSymbols/outlineTree2'; import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { - let ctor: IConstructorSignature1 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; + const ctor: IConstructorSignature1 = element instanceof FileElement + ? BreadcrumbsFilePicker + : BreadcrumbsOutlinePicker; + return instantiationService.createInstance(ctor, parent); } @@ -43,7 +46,7 @@ interface ILayoutInfo { inputHeight: number; } -type Tree = WorkbenchDataTree | WorkbenchAsyncDataTree; +type Tree = WorkbenchDataTree | WorkbenchAsyncDataTree; export abstract class BreadcrumbsPicker { @@ -51,10 +54,9 @@ export abstract class BreadcrumbsPicker { protected readonly _domNode: HTMLDivElement; protected readonly _arrow: HTMLDivElement; protected readonly _treeContainer: HTMLDivElement; - protected readonly _tree: Tree; + protected readonly _tree: Tree; protected readonly _fakeEvent = new UIEvent('fakeEvent'); protected readonly _focus: dom.IFocusTracker; - protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>; private _layoutInfo: ILayoutInfo; private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>(); @@ -67,7 +69,7 @@ export abstract class BreadcrumbsPicker { parent: HTMLElement, @IInstantiationService protected readonly _instantiationService: IInstantiationService, @IWorkbenchThemeService protected readonly _themeService: IWorkbenchThemeService, - @IConfigurationService private readonly _configurationService: IConfigurationService, + @IConfigurationService protected readonly _configurationService: IConfigurationService, ) { this._domNode = document.createElement('div'); this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons'; @@ -91,19 +93,10 @@ export abstract class BreadcrumbsPicker { this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`; this._domNode.appendChild(this._treeContainer); - this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService); const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService); this._disposables.push(filterConfig); - // const treeConfig = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined }); - // this._tree = this._instantiationService.createInstance( - // HighlightingWorkbenchTree, - // this._treeContainer, - // treeConfig, - // { useShadows: false, filterOnType: filterConfig.getValue(), showTwistie: false, twistiePixels: 12 }, - // { placeholder: localize('placeholder', "Find") } - // ); this._tree = this._createTree(this._treeContainer); this._disposables.push(this._tree.onDidChangeSelection(e => { @@ -144,7 +137,6 @@ export abstract class BreadcrumbsPicker { this._onDidPickElement.dispose(); this._tree.dispose(); this._focus.dispose(); - this._symbolSortOrder.dispose(); } setInput(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void { @@ -154,35 +146,13 @@ export abstract class BreadcrumbsPicker { this._setInput(input).then(() => { this._layout(); }).catch(onUnexpectedError); - - // let actualInput = this._getInput(input); - // Promise.resolve(this._tree.setInput(actualInput, undefined)).then(() => { - - // this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 }; - // this._layout(); - - // // use proper selection, reveal - // let selection = this._getInitialSelection(this._tree, input); - // if (selection) { - // return this._tree.reveal(selection, 0.5).then(() => { - // this._tree.setSelection([selection], this._tree); - // this._tree.setFocus(selection); - // this._tree.domFocus(); - // }); - // } else { - // this._tree.focusFirst(); - // this._tree.setSelection([this._tree.getFocus()], this._tree); - // this._tree.domFocus(); - // return Promise.resolve(null); - // } - // }, onUnexpectedError); } - private _layout(info: ILayoutInfo = this._layoutInfo): void { + protected _layout(info: ILayoutInfo = this._layoutInfo): void { - let headerHeight = 2 * info.arrowSize; - let treeHeight = Math.min(info.maxHeight - headerHeight, this._tree.visibleNodeCount * 22); - let totalHeight = treeHeight + headerHeight; + const headerHeight = 2 * info.arrowSize; + const treeHeight = Math.min(info.maxHeight - headerHeight, this._tree.visibleNodeCount * 22); + const totalHeight = treeHeight + headerHeight; this._domNode.style.height = `${totalHeight}px`; this._domNode.style.width = `${info.width}px`; @@ -198,7 +168,7 @@ export abstract class BreadcrumbsPicker { protected abstract _setInput(element: BreadcrumbElement): Promise; - protected abstract _createTree(container: HTMLElement): Tree; + protected abstract _createTree(container: HTMLElement): Tree; protected abstract _getTargetFromEvent(element: any, payload: UIEvent): any | undefined; } @@ -469,21 +439,38 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { + protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>; + + constructor( + parent: HTMLElement, + @IInstantiationService instantiationService: IInstantiationService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(parent, instantiationService, themeService, configurationService); + this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService); + } + protected _createTree(container: HTMLElement) { return this._instantiationService.createInstance( WorkbenchDataTree, container, - new NOutlineVirtualDelegate(), - [new NOutlineGroupRenderer(), this._instantiationService.createInstance(NOutlineElementRenderer)], - new NOutlineDataSource(), + new OutlineVirtualDelegate(), + [new OutlineGroupRenderer(), this._instantiationService.createInstance(OutlineElementRenderer)], + new OutlineDataSource(), { filterOnType: true, expandOnlyOnTwistieClick: true, - sorter: new NOutlineItemComparator(this._getOutlineItemCompareType()), - identityProvider: new NOutlineIdentityProvider(), - keyboardNavigationLabelProvider: this._instantiationService.createInstance(NOutlineNavigationLabelProvider) + sorter: new OutlineItemComparator(this._getOutlineItemCompareType()), + identityProvider: new OutlineIdentityProvider(), + keyboardNavigationLabelProvider: this._instantiationService.createInstance(OutlineNavigationLabelProvider) } - ) as WorkbenchDataTree; + ) as WorkbenchDataTree; + } + + dispose(): void { + this._symbolSortOrder.dispose(); + super.dispose(); } protected _setInput(input: BreadcrumbElement): Promise { @@ -515,15 +502,15 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { } } - private _getOutlineItemCompareType(): NOutlineItemCompareType { + private _getOutlineItemCompareType(): OutlineSortOrder { switch (this._symbolSortOrder.getValue()) { case 'name': - return NOutlineItemCompareType.ByName; + return OutlineSortOrder.ByName; case 'type': - return NOutlineItemCompareType.ByKind; + return OutlineSortOrder.ByKind; case 'position': default: - return NOutlineItemCompareType.ByPosition; + return OutlineSortOrder.ByPosition; } } } diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts index d775feea402..d6d0913d457 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts @@ -44,7 +44,7 @@ import { CollapseAction2 } from 'vs/workbench/browser/viewlet'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { OutlineConfigKeys, OutlineViewFiltered, OutlineViewFocused } from '../../../../editor/contrib/documentSymbols/outline'; import { FuzzyScore } from 'vs/base/common/filters'; -import { NOutlineDataSource, NOutlineItemComparator, NOutlineItemCompareType, NOutlineVirtualDelegate, NOutlineGroupRenderer, NOutlineElementRenderer, NOutlineItem, NOutlineIdentityProvider, NOutlineNavigationLabelProvider } from 'vs/editor/contrib/documentSymbols/outlineTree2'; +import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItem, OutlineIdentityProvider, OutlineNavigationLabelProvider } from 'vs/editor/contrib/documentSymbols/outlineTree2'; import { IDataTreeViewState } from 'vs/base/browser/ui/tree/dataTree'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -159,7 +159,7 @@ class OutlineViewState { private _followCursor = false; private _filterOnType = true; - private _sortBy = NOutlineItemCompareType.ByKind; + private _sortBy = OutlineSortOrder.ByKind; private _onDidChange = new Emitter<{ followCursor?: boolean, sortBy?: boolean, filterOnType?: boolean }>(); readonly onDidChange = this._onDidChange.event; @@ -186,14 +186,14 @@ class OutlineViewState { } } - set sortBy(value: NOutlineItemCompareType) { + set sortBy(value: OutlineSortOrder) { if (value !== this._sortBy) { this._sortBy = value; this._onDidChange.fire({ sortBy: true }); } } - get sortBy(): NOutlineItemCompareType { + get sortBy(): OutlineSortOrder { return this._sortBy; } @@ -229,10 +229,10 @@ export class OutlinePanel extends ViewletPanel { private _message: HTMLDivElement; private _inputContainer: HTMLDivElement; private _progressBar: ProgressBar; - private _tree: WorkbenchDataTree; - private _treeDataSource: NOutlineDataSource; - private _treeRenderer: NOutlineElementRenderer; - private _treeComparator: NOutlineItemComparator; + private _tree: WorkbenchDataTree; + private _treeDataSource: OutlineDataSource; + private _treeRenderer: OutlineElementRenderer; + private _treeComparator: OutlineItemComparator; private _treeStates = new LRUCache(10); private _treeFakeUIEvent = new UIEvent('me'); @@ -298,23 +298,23 @@ export class OutlinePanel extends ViewletPanel { progressContainer, this._message, this._inputContainer, treeContainer ); - this._treeRenderer = this._instantiationService.createInstance(NOutlineElementRenderer); - this._treeDataSource = new NOutlineDataSource(); - this._treeComparator = new NOutlineItemComparator(this._outlineViewState.sortBy); + this._treeRenderer = this._instantiationService.createInstance(OutlineElementRenderer); + this._treeDataSource = new OutlineDataSource(); + this._treeComparator = new OutlineItemComparator(this._outlineViewState.sortBy); this._tree = this._instantiationService.createInstance( WorkbenchDataTree, treeContainer, - new NOutlineVirtualDelegate(), - [new NOutlineGroupRenderer(), this._treeRenderer], + new OutlineVirtualDelegate(), + [new OutlineGroupRenderer(), this._treeRenderer], this._treeDataSource, { expandOnlyOnTwistieClick: true, filterOnType: this._outlineViewState.filterOnType, - sorter: new NOutlineItemComparator(), - identityProvider: new NOutlineIdentityProvider(), - keyboardNavigationLabelProvider: this._instantiationService.createInstance(NOutlineNavigationLabelProvider) + sorter: new OutlineItemComparator(), + identityProvider: new OutlineIdentityProvider(), + keyboardNavigationLabelProvider: this._instantiationService.createInstance(OutlineNavigationLabelProvider) } - ) as WorkbenchDataTree; + ) as WorkbenchDataTree; this._treeRenderer.renderProblemColors = this._configurationService.getValue(OutlineConfigKeys.problemsColors); this._treeRenderer.renderProblemBadges = this._configurationService.getValue(OutlineConfigKeys.problemsBadges); @@ -356,9 +356,9 @@ export class OutlinePanel extends ViewletPanel { getSecondaryActions(): IAction[] { let group = new RadioGroup([ - new SimpleToggleAction(localize('sortByPosition', "Sort By: Position"), this._outlineViewState.sortBy === NOutlineItemCompareType.ByPosition, _ => this._outlineViewState.sortBy = NOutlineItemCompareType.ByPosition), - new SimpleToggleAction(localize('sortByName', "Sort By: Name"), this._outlineViewState.sortBy === NOutlineItemCompareType.ByName, _ => this._outlineViewState.sortBy = NOutlineItemCompareType.ByName), - new SimpleToggleAction(localize('sortByKind', "Sort By: Type"), this._outlineViewState.sortBy === NOutlineItemCompareType.ByKind, _ => this._outlineViewState.sortBy = NOutlineItemCompareType.ByKind), + new SimpleToggleAction(localize('sortByPosition', "Sort By: Position"), this._outlineViewState.sortBy === OutlineSortOrder.ByPosition, _ => this._outlineViewState.sortBy = OutlineSortOrder.ByPosition), + new SimpleToggleAction(localize('sortByName', "Sort By: Name"), this._outlineViewState.sortBy === OutlineSortOrder.ByName, _ => this._outlineViewState.sortBy = OutlineSortOrder.ByName), + new SimpleToggleAction(localize('sortByKind', "Sort By: Type"), this._outlineViewState.sortBy === OutlineSortOrder.ByKind, _ => this._outlineViewState.sortBy = OutlineSortOrder.ByKind), ]); let result = [ new SimpleToggleAction(localize('followCur', "Follow Cursor"), this._outlineViewState.followCursor, action => this._outlineViewState.followCursor = action.checked), From 1bbd92326894c4a2d316b77d25f69252bcd3b71f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 5 Feb 2019 18:11:08 +0100 Subject: [PATCH 06/16] renames --- .../contrib/documentSymbols/{outlineTree2.ts => outlineTree.ts} | 0 src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts | 2 +- .../parts/outline/electron-browser/outline.contribution.ts | 2 +- .../electron-browser/{outlinePanel2.ts => outlinePanel.ts} | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/vs/editor/contrib/documentSymbols/{outlineTree2.ts => outlineTree.ts} (100%) rename src/vs/workbench/parts/outline/electron-browser/{outlinePanel2.ts => outlinePanel.ts} (99%) diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree2.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts similarity index 100% rename from src/vs/editor/contrib/documentSymbols/outlineTree2.ts rename to src/vs/editor/contrib/documentSymbols/outlineTree.ts diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 6b9431ed154..abe8dc86260 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -27,7 +27,7 @@ import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; -import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineItem } from 'vs/editor/contrib/documentSymbols/outlineTree2'; +import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineItem } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { diff --git a/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts b/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts index 408be2b2e66..8fada49cfa3 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { ViewsRegistry, IViewDescriptor } from 'vs/workbench/common/views'; -import { OutlinePanel } from './outlinePanel2'; +import { OutlinePanel } from './outlinePanel'; import { VIEW_CONTAINER } from 'vs/workbench/parts/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts similarity index 99% rename from src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts rename to src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index d6d0913d457..e4cf0a0ada4 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel2.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -44,7 +44,7 @@ import { CollapseAction2 } from 'vs/workbench/browser/viewlet'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { OutlineConfigKeys, OutlineViewFiltered, OutlineViewFocused } from '../../../../editor/contrib/documentSymbols/outline'; import { FuzzyScore } from 'vs/base/common/filters'; -import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItem, OutlineIdentityProvider, OutlineNavigationLabelProvider } from 'vs/editor/contrib/documentSymbols/outlineTree2'; +import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItem, OutlineIdentityProvider, OutlineNavigationLabelProvider } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { IDataTreeViewState } from 'vs/base/browser/ui/tree/dataTree'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; From defcf949bfb4711ec1a26f0df2eb43a87a25c35f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 5 Feb 2019 18:43:30 +0100 Subject: [PATCH 07/16] outline - bring back marker rendering --- .../documentSymbols/media/outlineTree.css | 33 ++------ .../contrib/documentSymbols/outlineTree.ts | 80 ++++++++++--------- .../outline/electron-browser/outlinePanel.ts | 6 -- 3 files changed, 47 insertions(+), 72 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css index a208aacb5dd..ebc2389a0e2 100644 --- a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css +++ b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css @@ -3,49 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-tree.focused .selected .outline-element-label, .monaco-tree.focused .selected .outline-element-decoration { +.monaco-list .monaco-list-row.focused.selected .outline-element .monaco-highlighted-label, +.monaco-list .monaco-list-row.focused.selected .outline-element-decoration { /* make sure selection color wins when a label is being selected */ color: inherit !important; } -.monaco-tree .outline-element { +.monaco-list .outline-element { display: flex; flex: 1; flex-flow: row nowrap; align-items: center; } -.monaco-tree .outline-element .outline-element-icon { - padding-right: 3px; -} - -.monaco-tree .outline-element .outline-element-label { - text-overflow: ellipsis; - overflow: hidden; +.monaco-list .outline-element .monaco-highlighted-label { color: var(--outline-element-color); } -.monaco-tree .outline-element .outline-element-label .monaco-highlighted-label .highlight { - font-weight: bold; -} - -.monaco-tree .outline-element .outline-element-detail { - visibility: hidden; - flex: 1; - flex-basis: 10%; - opacity: 0.8; - overflow: hidden; - text-overflow: ellipsis; - font-size: 90%; - padding-left: 4px; - padding-top: 3px; -} - .monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail { visibility: inherit; } -.monaco-tree .outline-element .outline-element-decoration { +.monaco-list .outline-element .outline-element-decoration { opacity: 0.75; font-size: 90%; font-weight: 600; @@ -55,7 +34,7 @@ color: var(--outline-element-color); } -.monaco-tree .outline-element .outline-element-decoration.bubble { +.monaco-list .outline-element .outline-element-decoration.bubble { font-family: octicons; font-size: 14px; opacity: 0.4; diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 2e54137a43b..46f351a34e6 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -19,6 +19,9 @@ import { IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/commo import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline'; +import { MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; export type OutlineItem = OutlineGroup | OutlineElement; @@ -55,6 +58,7 @@ export class OutlineGroupTemplate { export class OutlineElementTemplate { static id = 'OutlineElementTemplate'; + container: HTMLElement; iconLabel: IconLabel; decoration: HTMLElement; } @@ -101,20 +105,17 @@ export class OutlineElementRenderer implements ITreeRenderer, index: number, template: OutlineElementTemplate): void { @@ -125,50 +126,51 @@ export class OutlineElementRenderer implements ITreeRenderer 0) { - // dom.show(template.decoration); - // dom.removeClass(template.decoration, 'bubble'); - // template.decoration.innerText = count < 10 ? count.toString() : '+9'; - // template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count); - // template.decoration.style.setProperty('--outline-element-color', cssColor); + } else if (count > 0) { + dom.show(template.decoration); + dom.removeClass(template.decoration, 'bubble'); + template.decoration.innerText = count < 10 ? count.toString() : '+9'; + template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count); + template.decoration.style.setProperty('--outline-element-color', cssColor); - // } else { - // dom.show(template.decoration); - // dom.addClass(template.decoration, 'bubble'); - // template.decoration.innerText = '\uf052'; - // template.decoration.title = localize('deep.problem', "Contains elements with problems"); - // template.decoration.style.setProperty('--outline-element-color', cssColor); - // } - // } + } else { + dom.show(template.decoration); + dom.addClass(template.decoration, 'bubble'); + template.decoration.innerText = '\uf052'; + template.decoration.title = localize('deep.problem', "Contains elements with problems"); + template.decoration.style.setProperty('--outline-element-color', cssColor); + } + } private static _symbolKindNames: { [symbol: number]: string } = { [SymbolKind.Array]: localize('Array', "array"), diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index e4cf0a0ada4..1c54cf26de5 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -316,9 +316,6 @@ export class OutlinePanel extends ViewletPanel { } ) as WorkbenchDataTree; - this._treeRenderer.renderProblemColors = this._configurationService.getValue(OutlineConfigKeys.problemsColors); - this._treeRenderer.renderProblemBadges = this._configurationService.getValue(OutlineConfigKeys.problemsBadges); - this._disposables.push(this._tree); this._disposables.push(this._outlineViewState.onDidChange(this._onDidChangeUserState, this)); @@ -551,7 +548,6 @@ export class OutlinePanel extends ViewletPanel { if (marker.length > 0 || !ignoreEmpty) { model.updateMarker(marker); this._tree.updateChildren(); - // this._tree.refresh(undefined, true); } }; updateMarker([textModel.uri], true); @@ -559,8 +555,6 @@ export class OutlinePanel extends ViewletPanel { this._editorDisposables.push(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(OutlineConfigKeys.problemsBadges) || e.affectsConfiguration(OutlineConfigKeys.problemsColors)) { - this._treeRenderer.renderProblemColors = this._configurationService.getValue(OutlineConfigKeys.problemsColors); - this._treeRenderer.renderProblemBadges = this._configurationService.getValue(OutlineConfigKeys.problemsBadges); this._tree.updateChildren(); return; } From 078cce2b65fed1f2b6fd47215c5b12ad29529c7f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 5 Feb 2019 19:05:14 +0100 Subject: [PATCH 08/16] fix ctor issue --- .../parts/editor/breadcrumbsControl.ts | 2 +- .../browser/parts/editor/breadcrumbsPicker.ts | 44 ++++++++----------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 5f62cfbb159..c3624d9eb90 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -381,7 +381,7 @@ export class BreadcrumbsControl { } else { pickerArrowOffset = (data.left + (data.width * 0.3)) - x; } - picker.setInput(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset)); + picker.show(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset)); return { x, y }; }, onHide: (data) => { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index abe8dc86260..353be076247 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -52,12 +52,12 @@ export abstract class BreadcrumbsPicker { protected readonly _disposables = new Array(); protected readonly _domNode: HTMLDivElement; - protected readonly _arrow: HTMLDivElement; - protected readonly _treeContainer: HTMLDivElement; - protected readonly _tree: Tree; - protected readonly _fakeEvent = new UIEvent('fakeEvent'); - protected readonly _focus: dom.IFocusTracker; - private _layoutInfo: ILayoutInfo; + protected _arrow: HTMLDivElement; + protected _treeContainer: HTMLDivElement; + protected _tree: Tree; + protected _fakeEvent = new UIEvent('fakeEvent'); + protected _focus: dom.IFocusTracker; + protected _layoutInfo: ILayoutInfo; private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>(); readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event; @@ -78,6 +78,16 @@ export abstract class BreadcrumbsPicker { this._focus = dom.trackFocus(this._domNode); this._focus.onDidBlur(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined }), undefined, this._disposables); this._disposables.push(onDidChangeZoomLevel(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined }))); + } + + dispose(): void { + dispose(this._disposables); + this._onDidPickElement.dispose(); + this._tree.dispose(); + this._focus.dispose(); + } + + show(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void { const theme = this._themeService.getTheme(); const color = theme.getColor(breadcrumbsPickerBackground); @@ -126,21 +136,10 @@ export abstract class BreadcrumbsPicker { dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons); dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true); }; - this._disposables.push(_themeService.onDidFileIconThemeChange(onFileIconThemeChange)); - onFileIconThemeChange(_themeService.getFileIconTheme()); + this._disposables.push(this._themeService.onDidFileIconThemeChange(onFileIconThemeChange)); + onFileIconThemeChange(this._themeService.getFileIconTheme()); this._domNode.focus(); - } - - dispose(): void { - dispose(this._disposables); - this._onDidPickElement.dispose(); - this._tree.dispose(); - this._focus.dispose(); - } - - setInput(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void { - this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 }; this._setInput(input).then(() => { @@ -167,7 +166,6 @@ export abstract class BreadcrumbsPicker { } protected abstract _setInput(element: BreadcrumbElement): Promise; - protected abstract _createTree(container: HTMLElement): Tree; protected abstract _getTargetFromEvent(element: any, payload: UIEvent): any | undefined; } @@ -492,11 +490,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { return Promise.resolve(); } - protected _getTargetFromEvent(element: any, payload: any): any | undefined { - // todo@joh - if (payload && payload.didClickOnTwistie) { - return; - } + protected _getTargetFromEvent(element: any): any | undefined { if (element instanceof OutlineElement) { return element; } From 872b5f30498d3ac2bcffea2bac76d30a1f81406c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 6 Feb 2019 17:58:00 +0100 Subject: [PATCH 09/16] disallow multi select --- src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts | 2 ++ src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 353be076247..f39c7d6b00a 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -387,6 +387,7 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { this._instantiationService.createInstance(FileDataSource), { filterOnType: true, + multipleSelectionSupport: false, sorter: new FileSorter(), filter: this._instantiationService.createInstance(FileFilter), identityProvider: new FileIdentityProvider(), @@ -459,6 +460,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { { filterOnType: true, expandOnlyOnTwistieClick: true, + multipleSelectionSupport: false, sorter: new OutlineItemComparator(this._getOutlineItemCompareType()), identityProvider: new OutlineIdentityProvider(), keyboardNavigationLabelProvider: this._instantiationService.createInstance(OutlineNavigationLabelProvider) diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 1c54cf26de5..11ecb7f6a2f 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -309,6 +309,7 @@ export class OutlinePanel extends ViewletPanel { this._treeDataSource, { expandOnlyOnTwistieClick: true, + multipleSelectionSupport: false, filterOnType: this._outlineViewState.filterOnType, sorter: new OutlineItemComparator(), identityProvider: new OutlineIdentityProvider(), From 39bb6e5eb81cde32d74ab41f98f76402b62dd75f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 7 Feb 2019 09:53:26 +0100 Subject: [PATCH 10/16] :lipstick: --- src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 11ecb7f6a2f..8ada460a62c 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -418,7 +418,7 @@ export class OutlinePanel extends ViewletPanel { let textModel = editor.getModel(); let loadingMessage: IDisposable; - let oldModel = this._tree.getInput(); + let oldModel = this._tree.getInput(); if (!oldModel) { loadingMessage = new TimeoutTimer( () => this._showMessage(localize('loading', "Loading document symbols for '{0}'...", posix.basename(textModel.uri.path))), From a3ece298bc1350e084f597aefedbd0e6753333b4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 7 Feb 2019 10:17:46 +0100 Subject: [PATCH 11/16] persist view mode also when leaving outline --- .../outline/electron-browser/outlinePanel.ts | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 8ada460a62c..b9059482e71 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -412,13 +412,20 @@ export class OutlinePanel extends ViewletPanel { this._editorDisposables = new Array(); this._progressBar.infinite().show(150); + let textModel = editor.getModel(); + let oldModel = this._tree.getInput(); + + // persist state + if (oldModel) { + let state = this._tree.getViewState(); + this._treeStates.set(oldModel.textModel.uri.toString(), state); + } + if (!editor || !DocumentSymbolProviderRegistry.has(editor.getModel())) { return this._showMessage(localize('no-editor', "There are no editors open that can provide outline information.")); } - let textModel = editor.getModel(); let loadingMessage: IDisposable; - let oldModel = this._tree.getInput(); if (!oldModel) { loadingMessage = new TimeoutTimer( () => this._showMessage(localize('loading', "Loading document symbols for '{0}'...", posix.basename(textModel.uri.path))), @@ -426,17 +433,17 @@ export class OutlinePanel extends ViewletPanel { ); } - let model = await OutlinePanel._createOutlineModel(textModel, this._editorDisposables); + let newModel = await OutlinePanel._createOutlineModel(textModel, this._editorDisposables); dispose(loadingMessage); - if (!model) { + if (!newModel) { return; } - if (TreeElement.empty(model)) { + if (TreeElement.empty(newModel)) { return this._showMessage(localize('no-symbols', "No symbols found in document '{0}'", posix.basename(textModel.uri.path))); } - let newSize = TreeElement.size(model); + let newSize = TreeElement.size(newModel); if (newSize > 7500) { // this is a workaround for performance issues with the tree: https://github.com/Microsoft/vscode/issues/18180 return this._showMessage(localize('too-many-symbols', "We are sorry, but this file is too large for showing an outline.")); @@ -475,18 +482,12 @@ export class OutlinePanel extends ViewletPanel { this._progressBar.stop().hide(); - if (oldModel && oldModel.merge(model)) { + if (oldModel && oldModel.merge(newModel)) { this._tree.updateChildren(); - model = oldModel; - + newModel = oldModel; } else { - // persist state - if (oldModel) { - let state = this._tree.getViewState(); - this._treeStates.set(oldModel.textModel.uri.toString(), state); - } - let state = this._treeStates.get(model.textModel.uri.toString()); - await this._tree.setInput(model, state); + let state = this._treeStates.get(newModel.textModel.uri.toString()); + await this._tree.setInput(newModel, state); } this.layoutBody(this._cachedHeight); @@ -522,18 +523,18 @@ export class OutlinePanel extends ViewletPanel { || (this._tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey)); } } - this._revealTreeSelection(model, first, focus, aside); + this._revealTreeSelection(newModel, first, focus, aside); })); // feature: reveal editor selection in outline - this._revealEditorSelection(model, editor.getSelection()); - const versionIdThen = model.textModel.getVersionId(); + this._revealEditorSelection(newModel, editor.getSelection()); + const versionIdThen = newModel.textModel.getVersionId(); this._editorDisposables.push(editor.onDidChangeCursorSelection(e => { // first check if the document has changed and stop revealing the // cursor position iff it has -> we will update/recompute the // outline view then anyways - if (!model.textModel.isDisposed() && model.textModel.getVersionId() === versionIdThen) { - this._revealEditorSelection(model, e.selection); + if (!newModel.textModel.isDisposed() && newModel.textModel.getVersionId() === versionIdThen) { + this._revealEditorSelection(newModel, e.selection); } })); @@ -547,7 +548,7 @@ export class OutlinePanel extends ViewletPanel { } const marker = this._markerService.read({ resource: textModel.uri, severities: MarkerSeverity.Error | MarkerSeverity.Warning }); if (marker.length > 0 || !ignoreEmpty) { - model.updateMarker(marker); + newModel.updateMarker(marker); this._tree.updateChildren(); } }; @@ -563,7 +564,7 @@ export class OutlinePanel extends ViewletPanel { return; } if (!this._configurationService.getValue(OutlineConfigKeys.problemsEnabled)) { - model.updateMarker([]); + newModel.updateMarker([]); this._tree.updateChildren(); } else { updateMarker([textModel.uri], true); From 744cdcf47c868ff50b76ec23dd605e81ffbad0fc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 7 Feb 2019 10:22:09 +0100 Subject: [PATCH 12/16] fix sorting and use updateChildren as 'resort' --- .../workbench/parts/outline/electron-browser/outlinePanel.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index b9059482e71..f3531d02a7f 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -311,7 +311,7 @@ export class OutlinePanel extends ViewletPanel { expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, filterOnType: this._outlineViewState.filterOnType, - sorter: new OutlineItemComparator(), + sorter: this._treeComparator, identityProvider: new OutlineIdentityProvider(), keyboardNavigationLabelProvider: this._instantiationService.createInstance(OutlineNavigationLabelProvider) } @@ -378,7 +378,6 @@ export class OutlinePanel extends ViewletPanel { if (e.sortBy) { this._treeComparator.type = this._outlineViewState.sortBy; // todo@joh resort - // this._tree.refresh(undefined, true); this._tree.updateChildren(); } if (e.filterOnType) { From dd3817272fe2f1865890933bb3df7c418ff670ef Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 7 Feb 2019 10:25:51 +0100 Subject: [PATCH 13/16] enforce outline filter/find mode is applied --- .../parts/outline/electron-browser/outlinePanel.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index f3531d02a7f..9b9a44dd8af 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -320,6 +320,12 @@ export class OutlinePanel extends ViewletPanel { this._disposables.push(this._tree); this._disposables.push(this._outlineViewState.onDidChange(this._onDidChangeUserState, this)); + // todo@joh workaournd for the tree resetting the filter behaviour + // to something globally defined + this._tree.updateOptions({ + filterOnType: this._outlineViewState.filterOnType + }); + // feature: toggle icons this.disposables.push(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(OutlineConfigKeys.icons)) { From 0fae32c77ef98316171d9a8d4e0e68220a08de2d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 7 Feb 2019 11:57:57 +0100 Subject: [PATCH 14/16] fix null reference --- src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 9b9a44dd8af..70684ff8365 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -417,7 +417,6 @@ export class OutlinePanel extends ViewletPanel { this._editorDisposables = new Array(); this._progressBar.infinite().show(150); - let textModel = editor.getModel(); let oldModel = this._tree.getInput(); // persist state @@ -430,6 +429,7 @@ export class OutlinePanel extends ViewletPanel { return this._showMessage(localize('no-editor', "There are no editors open that can provide outline information.")); } + let textModel = editor.getModel(); let loadingMessage: IDisposable; if (!oldModel) { loadingMessage = new TimeoutTimer( From b54ef5a7e2a95133f4372b4d1fda2841f51639a0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 7 Feb 2019 12:02:37 +0100 Subject: [PATCH 15/16] fix #61808 --- .../browser/parts/editor/breadcrumbsPicker.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index f39c7d6b00a..b22e048f38e 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -129,16 +129,6 @@ export abstract class BreadcrumbsPicker { this._layout(); })); - // tree icon theme specials - dom.addClass(this._treeContainer, 'file-icon-themable-tree'); - dom.addClass(this._treeContainer, 'show-file-icons'); - const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => { - dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons); - dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true); - }; - this._disposables.push(this._themeService.onDidFileIconThemeChange(onFileIconThemeChange)); - onFileIconThemeChange(this._themeService.getFileIconTheme()); - this._domNode.focus(); this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 }; @@ -376,6 +366,16 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { _createTree(container: HTMLElement) { + // tree icon theme specials + dom.addClass(this._treeContainer, 'file-icon-themable-tree'); + dom.addClass(this._treeContainer, 'show-file-icons'); + const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => { + dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons); + dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true); + }; + this._disposables.push(this._themeService.onDidFileIconThemeChange(onFileIconThemeChange)); + onFileIconThemeChange(this._themeService.getFileIconTheme()); + const labels = this._instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER /* TODO@Jo visibility propagation */); this._disposables.push(labels); From 8b593195e2d34342e597087b37b37a16cd7ea9d1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 7 Feb 2019 12:32:27 +0100 Subject: [PATCH 16/16] fix strict null --- src/tsconfig.strictNullChecks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index aeddd30bfc3..622ba23aaa4 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -54,6 +54,7 @@ "./vs/editor/contrib/cursorUndo/cursorUndo.ts", "./vs/editor/contrib/dnd/dnd.ts", "./vs/editor/contrib/dnd/dragAndDropCommand.ts", + "./vs/editor/contrib/documentSymbols/outline.ts", "./vs/editor/contrib/documentSymbols/outlineModel.ts", "./vs/editor/contrib/find/findController.ts", "./vs/editor/contrib/find/findDecorations.ts", @@ -547,7 +548,6 @@ "./vs/workbench/contrib/markers/electron-browser/markersPanelActions.ts", "./vs/workbench/contrib/markers/electron-browser/messages.ts", "./vs/workbench/contrib/markers/test/electron-browser/markersModel.test.ts", - "./vs/workbench/contrib/outline/electron-browser/outline.ts", "./vs/workbench/contrib/output/common/output.ts", "./vs/workbench/contrib/output/common/outputLinkComputer.ts", "./vs/workbench/contrib/output/common/outputLinkProvider.ts", @@ -747,4 +747,4 @@ "exclude": [ "./typings/require-monaco.d.ts" ] -} \ No newline at end of file +}