未验证 提交 57edc531 编写于 作者: J Johannes Rieken 提交者: GitHub

Merge pull request #68080 from Microsoft/joh/o2

new trees for outline and breadcrumbs
......@@ -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",
......@@ -746,4 +746,4 @@
"exclude": [
"./typings/require-monaco.d.ts"
]
}
\ No newline at end of file
}
......@@ -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'}`;
};
})();
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -4,178 +4,140 @@
*--------------------------------------------------------------------------------------------*/
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 { 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 } from 'vs/base/common/filters';
import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
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 { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline';
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 { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry';
export const enum OutlineItemCompareType {
ByPosition,
ByName,
ByKind
}
export class OutlineItemComparator implements ISorter {
export type OutlineItem = OutlineGroup | OutlineElement;
constructor(
public type: OutlineItemCompareType = OutlineItemCompareType.ByPosition
) { }
export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelProvider<OutlineItem> {
compare(tree: ITree, a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): number {
constructor(@IKeybindingService private readonly _keybindingService: IKeybindingService) { }
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);
}
getKeyboardNavigationLabel(element: OutlineItem): { toString(): string; } {
if (element instanceof OutlineGroup) {
return element.provider.displayName;
} else {
return element.symbol.name;
}
}
return 0;
mightProducePrintableCharacter(event: IKeyboardEvent): boolean {
return this._keybindingService.mightProducePrintableCharacter(event);
}
}
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 OutlineIdentityProvider implements IIdentityProvider<OutlineItem> {
getId(element: TreeElement): { toString(): string; } {
return element.id;
}
}
export class OutlineDataSource implements IDataSource {
// this is a workaround for the tree showing twisties for items
// with only filtered children
filterOnScore: boolean = true;
export class OutlineGroupTemplate {
static id = 'OutlineGroupTemplate';
getId(tree: ITree, element: TreeElement): string {
return element ? element.id : 'empty';
}
labelContainer: HTMLElement;
label: HighlightedLabel;
}
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;
}
export class OutlineElementTemplate {
static id = 'OutlineElementTemplate';
container: HTMLElement;
iconLabel: IconLabel;
decoration: HTMLElement;
}
getChildren(tree: ITree, element: TreeElement): Promise<TreeElement[]> {
let res = values(element.children);
// console.log(element.id + ' with children ' + res.length);
return Promise.resolve(res);
}
export class OutlineVirtualDelegate implements IListVirtualDelegate<OutlineItem> {
getParent(tree: ITree, element: TreeElement | any): Promise<TreeElement> {
return Promise.resolve(element && element.parent);
getHeight(_element: OutlineItem): number {
return 22;
}
shouldAutoexpand(tree: ITree, element: TreeElement): boolean {
return element && (element instanceof OutlineModel || element.parent instanceof OutlineModel || element instanceof OutlineGroup || element.parent instanceof OutlineGroup);
getTemplateId(element: OutlineItem): string {
if (element instanceof OutlineGroup) {
return OutlineGroupTemplate.id;
} else {
return OutlineElementTemplate.id;
}
}
}
export interface OutlineTemplate {
labelContainer: HTMLElement;
label: HighlightedLabel;
icon?: HTMLElement;
detail?: HTMLElement;
decoration?: HTMLElement;
}
export class OutlineRenderer implements IRenderer {
export class OutlineGroupRenderer implements ITreeRenderer<OutlineGroup, FuzzyScore, OutlineGroupTemplate> {
renderProblemColors = true;
renderProblemBadges = true;
readonly templateId: string = OutlineGroupTemplate.id;
constructor(
@IThemeService readonly _themeService: IThemeService,
@IConfigurationService readonly _configurationService: IConfigurationService
) {
//
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) };
}
getHeight(tree: ITree, element: any): number {
return 22;
renderElement(node: ITreeNode<OutlineGroup, FuzzyScore>, index: number, template: OutlineGroupTemplate): void {
template.label.set(
node.element.provider.displayName || localize('provider', "Outline Provider"),
createMatches(node.filterData)
);
}
getTemplateId(tree: ITree, element: OutlineGroup | OutlineElement): string {
return element instanceof OutlineGroup ? 'outline-group' : 'outline-element';
disposeTemplate(_template: OutlineGroupTemplate): void {
// nothing
}
}
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) };
}
export class OutlineElementRenderer implements ITreeRenderer<OutlineElement, FuzzyScore, OutlineElementTemplate> {
throw new Error(templateId);
}
readonly templateId: string = OutlineElementTemplate.id;
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);
constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IThemeService private readonly _themeService: IThemeService,
) { }
renderTemplate(container: HTMLElement): OutlineElementTemplate {
dom.addClass(container, 'outline-element');
const iconLabel = new IconLabel(container, { supportHighlights: true });
const decoration = dom.$('.outline-element-decoration');
container.appendChild(decoration);
return { container, iconLabel, decoration };
}
renderElement(node: ITreeNode<OutlineElement, FuzzyScore>, index: number, template: OutlineElementTemplate): void {
const { element } = node;
const options = {
matches: createMatches(node.filterData),
extraClasses: [],
title: localize('title.template', "{0} ({1})", element.symbol.name, OutlineElementRenderer._symbolKindNames[element.symbol.kind])
};
if (this._configurationService.getValue(OutlineConfigKeys.icons)) {
// add styles for the icons
options.extraClasses.push(`outline-element-icon ${symbolKindToCssClass(element.symbol.kind, true)}`);
}
if (element instanceof OutlineGroup) {
template.label.set(element.provider.displayName || localize('provider', "Outline Provider"));
}
template.iconLabel.setLabel(element.symbol.name, element.symbol.detail, options);
this._renderMarkerInfo(element, template);
}
private _renderMarkerInfo(element: OutlineElement, template: OutlineTemplate): void {
private _renderMarkerInfo(element: OutlineElement, template: OutlineElementTemplate): void {
if (!element.marker) {
dom.hide(template.decoration);
template.labelContainer.style.removeProperty('--outline-element-color');
template.container.style.removeProperty('--outline-element-color');
return;
}
......@@ -184,14 +146,14 @@ export class OutlineRenderer implements IRenderer {
const cssColor = color ? color.toString() : 'inherit';
// color of the label
if (this.renderProblemColors) {
template.labelContainer.style.setProperty('--outline-element-color', cssColor);
if (this._configurationService.getValue(OutlineConfigKeys.problemsColors)) {
template.container.style.setProperty('--outline-element-color', cssColor);
} else {
template.labelContainer.style.removeProperty('--outline-element-color');
template.container.style.removeProperty('--outline-element-color');
}
// badge with color/rollup
if (!this.renderProblemBadges) {
if (!this._configurationService.getValue(OutlineConfigKeys.problemsBadges)) {
dom.hide(template.decoration);
} else if (count > 0) {
......@@ -239,77 +201,46 @@ export class OutlineRenderer implements IRenderer {
[SymbolKind.Variable]: localize('Variable', "variable"),
};
disposeTemplate(tree: ITree, templateId: string, template: OutlineTemplate): void {
// noop
disposeTemplate(_template: OutlineElementTemplate): void {
_template.iconLabel.dispose();
}
}
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;
}
export const enum OutlineSortOrder {
ByPosition,
ByName,
ByKind
}
// focus
let focused: string;
element = tree.getFocus(true);
if (element instanceof TreeElement) {
focused = element.id;
}
export class OutlineItemComparator implements ITreeSorter<OutlineItem> {
// expansion
let expanded = new Array<string>();
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 };
}
constructor(
public type: OutlineSortOrder = OutlineSortOrder.ByPosition
) { }
static async restore(tree: ITree, state: OutlineTreeState, eventPayload: any): Promise<void> {
let model = <OutlineModel>tree.getInput();
if (!state || !(model instanceof OutlineModel)) {
return Promise.resolve(undefined);
}
compare(a: OutlineItem, b: OutlineItem): number {
if (a instanceof OutlineGroup && b instanceof OutlineGroup) {
return a.providerIndex - b.providerIndex;
// expansion
let items: TreeElement[] = [];
for (const id of state.expanded) {
let item = model.getItemById(id);
if (item) {
items.push(item);
} else if (a instanceof OutlineElement && b instanceof OutlineElement) {
if (this.type === OutlineSortOrder.ByKind) {
return a.symbol.kind - b.symbol.kind || a.symbol.name.localeCompare(b.symbol.name);
} 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 === OutlineSortOrder.ByPosition) {
return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || a.symbol.name.localeCompare(b.symbol.name);
}
}
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);
return 0;
}
}
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);
export class OutlineDataSource implements IDataSource<OutlineModel, OutlineItem> {
getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement): OutlineItem[] {
if (!element) {
return [];
}
return values(element.children);
}
}
......@@ -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';
......@@ -723,241 +717,6 @@ export class TreeResourceNavigator2<T, TFilterData> 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<any, FuzzyScore>;
private readonly _onDidStartFilter: Emitter<this>;
readonly onDidStartFiltering: Event<this>;
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<any, FuzzyScore>();
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>();
this.onDidStartFiltering = this._onDidStartFilter.event;
this.disposables.push(this._onDidStartFilter);
}
setInput(element: any): Promise<any> {
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;
......
......@@ -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) => {
......
......@@ -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<any, any, any>, enabled: boolean, clazz: string) {
constructor(tree: AsyncDataTree<any, any, any> | AbstractTree<any, any, any>, enabled: boolean, clazz: string) {
super('workbench.action.collapse', nls.localize('collapse', "Collapse All"), clazz, enabled, () => {
tree.collapseAll();
return Promise.resolve(undefined);
......
......@@ -9,7 +9,7 @@ import { OutlinePanel } from './outlinePanel';
import { VIEW_CONTAINER } from 'vs/workbench/contrib/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/contrib/outline/electron-browser/outline';
import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymbols/outline';
const _outlineDesc = <IViewDescriptor>{
id: OutlineViewId,
......
......@@ -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;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册