提交 c49edf91 编写于 作者: J Johannes Rieken

Merge branch 'joh/breadcrumbs'

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-breadcrumbs {
user-select: none;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
}
.monaco-breadcrumbs .monaco-breadcrumb-item {
display: flex;
align-items: center;
flex: 0 1 auto;
white-space: nowrap;
cursor: default;
align-self: center;
height: 100%;
}
.monaco-breadcrumbs .monaco-breadcrumb-item:not(:last-child)::after {
background-image: url(./collapsed.svg);
width: 16px;
height: 16px;
display: inline-block;
background-size: 16px;
background-position: 50% 50%;
content: ' ';
}
.vs-dark .monaco-breadcrumbs .monaco-breadcrumb-item:not(:last-child)::after {
background-image: url(./collpased-dark.svg);
}
.monaco-breadcrumbs .monaco-breadcrumb-item.focused {
font-weight: bold;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./breadcrumbsWidget';
import * as dom from 'vs/base/browser/dom';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { Event, Emitter } from 'vs/base/common/event';
import { Color } from 'vs/base/common/color';
import { commonPrefixLength } from 'vs/base/common/arrays';
export abstract class BreadcrumbsItem {
dispose(): void { }
abstract equals(other: BreadcrumbsItem): boolean;
abstract render(container: HTMLElement): void;
}
export class SimpleBreadcrumbsItem extends BreadcrumbsItem {
constructor(
readonly text: string,
readonly title: string = text
) {
super();
}
equals(other: this) {
return other === this || other instanceof SimpleBreadcrumbsItem && other.text === this.text && other.title === this.title;
}
render(container: HTMLElement): void {
let node = document.createElement('div');
node.title = this.title;
node.innerText = this.text;
container.appendChild(node);
}
}
export interface IBreadcrumbsWidgetStyles {
breadcrumbsBackground?: Color;
breadcrumbsActiveForeground?: Color;
breadcrumbsInactiveForeground?: Color;
}
export interface IBreadcrumbsItemEvent {
item: BreadcrumbsItem;
node: HTMLElement;
}
export class BreadcrumbsWidget {
private readonly _disposables = new Array<IDisposable>();
private readonly _domNode: HTMLDivElement;
private readonly _styleElement: HTMLStyleElement;
private readonly _scrollable: DomScrollableElement;
private readonly _onDidSelectItem = new Emitter<IBreadcrumbsItemEvent>();
private readonly _onDidFocusItem = new Emitter<IBreadcrumbsItemEvent>();
private readonly _onDidChangeFocus = new Emitter<boolean>();
readonly onDidSelectItem: Event<IBreadcrumbsItemEvent> = this._onDidSelectItem.event;
readonly onDidFocusItem: Event<IBreadcrumbsItemEvent> = this._onDidFocusItem.event;
readonly onDidChangeFocus: Event<boolean> = this._onDidChangeFocus.event;
private readonly _items = new Array<BreadcrumbsItem>();
private readonly _nodes = new Array<HTMLDivElement>();
private readonly _freeNodes = new Array<HTMLDivElement>();
private _focusedItemIdx: number = -1;
private _selectedItemIdx: number = -1;
constructor(
container: HTMLElement
) {
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-breadcrumbs';
this._domNode.tabIndex = -1;
this._scrollable = new DomScrollableElement(this._domNode, {
vertical: ScrollbarVisibility.Hidden,
horizontal: ScrollbarVisibility.Auto,
horizontalScrollbarSize: 3,
useShadows: false
});
this._disposables.push(this._scrollable);
this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e)));
container.appendChild(this._scrollable.getDomNode());
this._styleElement = dom.createStyleSheet(this._domNode);
let focusTracker = dom.trackFocus(this._domNode);
this._disposables.push(focusTracker);
this._disposables.push(focusTracker.onDidBlur(_ => this._onDidChangeFocus.fire(false)));
this._disposables.push(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true)));
}
dispose(): void {
dispose(this._disposables);
this._domNode.remove();
this._disposables.length = 0;
this._nodes.length = 0;
this._freeNodes.length = 0;
}
layout(dim: dom.Dimension): void {
if (!dim) {
this._scrollable.scanDomNode();
} else {
this._domNode.style.width = `${dim.width}px`;
this._domNode.style.height = `${dim.height}px`;
this._scrollable.scanDomNode();
}
}
style(style: IBreadcrumbsWidgetStyles): void {
let content = '';
if (style.breadcrumbsBackground) {
content += `.monaco-breadcrumbs { background-color: ${style.breadcrumbsBackground}}`;
}
if (style.breadcrumbsActiveForeground) {
content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item { color: ${style.breadcrumbsActiveForeground}}\n`;
}
if (style.breadcrumbsInactiveForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item { color: ${style.breadcrumbsInactiveForeground}}\n`;
}
if (this._styleElement.innerHTML !== content) {
this._styleElement.innerHTML = content;
}
}
domFocus(): void {
this._domNode.focus();
}
getFocused(): BreadcrumbsItem {
return this._items[this._focusedItemIdx];
}
setFocused(item: BreadcrumbsItem): void {
this._focus(this._items.indexOf(item));
}
focusPrev(): any {
this._focus((this._focusedItemIdx - 1 + this._nodes.length) % this._nodes.length);
this._domNode.focus();
}
focusNext(): any {
this._focus((this._focusedItemIdx + 1) % this._nodes.length);
this._domNode.focus();
}
private _focus(nth: number): void {
this._focusedItemIdx = -1;
for (let i = 0; i < this._nodes.length; i++) {
const node = this._nodes[i];
if (i !== nth) {
dom.removeClass(node, 'focused');
} else {
this._focusedItemIdx = i;
dom.addClass(node, 'focused');
}
}
this._onDidFocusItem.fire({ item: this._items[this._focusedItemIdx], node: this._nodes[this._focusedItemIdx] });
}
getSelected(): BreadcrumbsItem {
return this._items[this._selectedItemIdx];
}
setSelected(item: BreadcrumbsItem): void {
this._select(this._items.indexOf(item));
}
private _select(nth: number): void {
this._selectedItemIdx = -1;
for (let i = 0; i < this._nodes.length; i++) {
const node = this._nodes[i];
if (i !== nth) {
dom.removeClass(node, 'selected');
} else {
this._selectedItemIdx = i;
dom.addClass(node, 'selected');
}
}
this._onDidSelectItem.fire({ item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx] });
}
setItems(items: BreadcrumbsItem[]): void {
let prefix = commonPrefixLength(this._items, items, (a, b) => a.equals(b));
let removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix));
this._render(prefix);
dispose(removed);
}
private _render(start: number): void {
for (; start < this._items.length && start < this._nodes.length; start++) {
let item = this._items[start];
let node = this._nodes[start];
this._renderItem(item, node);
}
// case a: more nodes -> remove them
for (; start < this._nodes.length; start++) {
this._nodes[start].remove();
this._freeNodes.push(this._nodes[start]);
}
this._nodes.length = this._items.length;
// case b: more items -> render them
for (; start < this._items.length; start++) {
let item = this._items[start];
let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div');
this._renderItem(item, node);
this._domNode.appendChild(node);
this._nodes[start] = node;
}
this.layout(undefined);
}
private _renderItem(item: BreadcrumbsItem, container: HTMLDivElement): void {
dom.clearNode(container);
item.render(container);
dom.append(container);
dom.addClass(container, 'monaco-breadcrumb-item');
}
private _onClick(event: IMouseEvent): void {
for (let el = event.target; el; el = el.parentElement) {
let idx = this._nodes.indexOf(el as any);
if (idx >= 0) {
this._focus(idx);
this._select(idx);
break;
}
}
}
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* 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 {
/* make sure selection color wins when a label is being selected */
color: inherit !important;
}
.monaco-tree .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.no-icons .outline-element .outline-element-icon {
display: none;
} */
.monaco-tree .outline-element .outline-element-label {
text-overflow: ellipsis;
overflow: hidden;
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 {
opacity: 0.75;
font-size: 90%;
font-weight: 600;
padding: 0 12px 0 5px;
margin-left: auto;
text-align: center;
color: var(--outline-element-color);
}
.monaco-tree .outline-element .outline-element-decoration.bubble {
font-family: octicons;
font-size: 14px;
opacity: 0.4;
}
......@@ -5,22 +5,23 @@
'use strict';
import * as dom from 'vs/base/browser/dom';
import 'vs/css!./media/symbol-icons';
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 { TPromise } from 'vs/base/common/winjs.base';
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 { symbolKindToCssClass, SymbolKind } from 'vs/editor/common/modes';
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';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export enum OutlineItemCompareType {
ByPosition,
......
......@@ -261,4 +261,34 @@ export function attachProgressBarStyler(widget: IThemable, themeService: IThemeS
export function attachStylerCallback(themeService: IThemeService, colors: { [name: string]: ColorIdentifier }, callback: styleFn): IDisposable {
return attachStyler(themeService, colors, callback);
}
\ No newline at end of file
}
export interface IBreadcrumbsWidgetStyleOverrides extends IStyleOverrides {
breadcrumbsBackground?: ColorIdentifier;
breadcrumbsItemHoverBackground?: ColorIdentifier;
breadcrumbsItemHoverForeground?: ColorIdentifier;
breadcrumbsItemFocusBackground?: ColorIdentifier;
breadcrumbsItemFocusForeground?: ColorIdentifier;
breadcrumbsActiveItemSelectionBackground?: ColorIdentifier;
breadcrumbsActiveItemSelectionForeground?: ColorIdentifier;
breadcrumbsInactiveItemSelectionBackground?: ColorIdentifier;
breadcrumbsInactiveItemSelectionForeground?: ColorIdentifier;
}
export const defaultBreadcrumbsStyles = <IBreadcrumbsWidgetStyleOverrides>{
breadcrumbsBackground: editorBackground,
breadcrumbsItemHoverBackground: listHoverBackground,
breadcrumbsItemHoverForeground: listHoverForeground,
breadcrumbsItemFocusBackground: listFocusBackground,
breadcrumbsItemFocusForeground: listFocusForeground,
breadcrumbsItemSelectionBackground: listActiveSelectionBackground,
breadcrumbsItemSelectionForeground: listActiveSelectionForeground,
breadcrumbsActiveItemSelectionBackground: listActiveSelectionBackground,
breadcrumbsActiveItemSelectionForeground: listActiveSelectionForeground,
breadcrumbsInactiveItemSelectionBackground: editorBackground,
breadcrumbsInactiveItemSelectionForeground: listInactiveSelectionForeground,
};
export function attachBreadcrumbsStyler(widget: IThemable, themeService: IThemeService, style?: IBreadcrumbsWidgetStyleOverrides): IDisposable {
return attachStyler(themeService, { ...defaultBreadcrumbsStyles, ...style }, widget);
}
......@@ -159,4 +159,4 @@ export interface EditorGroupsServiceImpl extends IEditorGroupsService {
* A promise that resolves when groups have been restored.
*/
readonly whenRestored: TPromise<void>;
}
\ No newline at end of file
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as dom from 'vs/base/browser/dom';
import { BreadcrumbsItem, BreadcrumbsWidget, IBreadcrumbsItemEvent } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget';
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { compareFileNames } from 'vs/base/common/comparers';
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { dirname, isEqual } from 'vs/base/common/resources';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree';
import 'vs/css!./media/editorbreadcrumbs';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Extensions, 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 { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files';
import { IConstructorSignature2, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
import { Registry } from 'vs/platform/registry/common/platform';
import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { FileLabel } from 'vs/workbench/browser/labels';
import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel';
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
import { EditorInput } from 'vs/workbench/common/editor';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorBreadcrumbs, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
class Item extends BreadcrumbsItem {
private readonly _disposables: IDisposable[] = [];
constructor(
readonly element: BreadcrumbElement,
@IInstantiationService private readonly _instantiationService: IInstantiationService
) {
super();
}
dispose(): void {
dispose(this._disposables);
}
equals(other: BreadcrumbsItem): boolean {
if (!(other instanceof Item)) {
return false;
}
if (this.element instanceof FileElement && other.element instanceof FileElement) {
return isEqual(this.element.uri, other.element.uri);
}
if (this.element instanceof TreeElement && other.element instanceof TreeElement) {
return this.element.id === other.element.id;
}
return false;
}
render(container: HTMLElement): void {
if (this.element instanceof FileElement) {
// file/folder
let label = this._instantiationService.createInstance(FileLabel, container, {});
label.setFile(this.element.uri, {
hidePath: true,
fileKind: this.element.isFile ? FileKind.FILE : FileKind.FOLDER
});
this._disposables.push(label);
} else if (this.element instanceof OutlineGroup) {
// provider
let label = new IconLabel(container);
label.setValue(this.element.provider.displayName);
this._disposables.push(label);
} else if (this.element instanceof OutlineElement) {
// symbol
let label = new IconLabel(container);
label.setValue(this.element.symbol.name);
this._disposables.push(label);
}
}
}
export class EditorBreadcrumbs implements IEditorBreadcrumbs {
static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false);
static CK_BreadcrumbsFocused = new RawContextKey('breadcrumbsFocused', false);
private readonly _ckBreadcrumbsVisible: IContextKey<boolean>;
private readonly _ckBreadcrumbsFocused: IContextKey<boolean>;
private readonly _cfEnabled: Config<boolean>;
private readonly _disposables = new Array<IDisposable>();
private readonly _domNode: HTMLDivElement;
private readonly _widget: BreadcrumbsWidget;
private _breadcrumbsDisposables = new Array<IDisposable>();
constructor(
container: HTMLElement,
private readonly _editorGroup: EditorGroupView,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IContextViewService private readonly _contextViewService: IContextViewService,
@IEditorService private readonly _editorService: IEditorService,
@IFileService private readonly _fileService: IFileService,
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IThemeService private readonly _themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
) {
this._domNode = document.createElement('div');
dom.addClasses(this._domNode, 'editor-breadcrumbs', 'show-file-icons');
dom.append(container, this._domNode);
this._widget = new BreadcrumbsWidget(this._domNode);
this._widget.onDidSelectItem(this._onDidSelectItem, this, this._disposables);
this._widget.onDidChangeFocus(val => this._ckBreadcrumbsFocused.set(val), undefined, this._disposables);
this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService));
this._cfEnabled = Config.create(configurationService, 'breadcrumbs.enabled');
this._disposables.push(this._cfEnabled.onDidChange(value => {
if (!value) {
this.closeEditor(undefined);
this._editorGroup.relayout();
} else if (this._editorGroup.activeEditor) {
this.openEditor(this._editorGroup.activeEditor);
this._editorGroup.relayout();
}
}));
this._ckBreadcrumbsVisible = EditorBreadcrumbs.CK_BreadcrumbsVisible.bindTo(this._contextKeyService);
this._ckBreadcrumbsFocused = EditorBreadcrumbs.CK_BreadcrumbsFocused.bindTo(this._contextKeyService);
}
dispose(): void {
dispose(this._disposables);
this._widget.dispose();
this._ckBreadcrumbsVisible.reset();
this._cfEnabled.dispose();
}
getPreferredHeight(): number {
return this._cfEnabled.value ? 25 : 0;
}
layout(dim: dom.Dimension): void {
this._domNode.style.width = `${dim.width}px`;
this._domNode.style.height = `${dim.height}px`;
this._widget.layout(dim);
}
setActive(value: boolean): void {
dom.toggleClass(this._domNode, 'active', value);
}
openEditor(input: EditorInput): void {
if (!this._cfEnabled.value) {
// not enabled -> return early
return;
}
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
let uri = input.getResource();
if (!uri || !this._fileService.canHandleResource(uri)) {
return this.closeEditor(undefined);
}
dom.toggleClass(this._domNode, 'hidden', false);
this._ckBreadcrumbsVisible.set(true);
let control = this._editorGroup.activeControl.getControl() as ICodeEditor;
let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService);
let listener = model.onDidUpdate(_ => this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService))));
this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService)));
this._breadcrumbsDisposables.push(model, listener);
}
closeEditor(input: EditorInput): void {
this._ckBreadcrumbsVisible.set(false);
dom.toggleClass(this._domNode, 'hidden', true);
}
focus(): void {
this._widget.domFocus();
}
focusNext(): void {
this._widget.focusNext();
}
focusPrev(): void {
this._widget.focusPrev();
}
select(): void {
const item = this._widget.getFocused();
if (item) {
this._widget.setSelected(item);
}
}
private _onDidSelectItem(event: IBreadcrumbsItemEvent): void {
if (!event.item) {
return;
}
this._editorGroup.focus();
this._contextViewService.showContextView({
getAnchor() {
return event.node;
},
render: (container: HTMLElement) => {
dom.addClasses(container, 'monaco-workbench', 'show-file-icons');
let { element } = event.item as Item;
let ctor: IConstructorSignature2<HTMLElement, BreadcrumbElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
let res = this._instantiationService.createInstance(ctor, container, element);
res.layout({ width: 250, height: 300 });
res.onDidPickElement(data => {
this._contextViewService.hideContextView();
if (!data) {
return;
}
if (URI.isUri(data)) {
// open new editor
this._editorService.openEditor({ resource: data });
} else if (data instanceof OutlineElement) {
let resource: URI;
let candidate = data.parent;
while (candidate) {
if (candidate instanceof OutlineModel) {
resource = candidate.textModel.uri;
break;
}
candidate = candidate.parent;
}
this._editorService.openEditor({ resource, options: { selection: Range.collapseToStart(data.symbol.selectionRange) } });
}
});
return res;
},
onHide: () => {
this._widget.setSelected(undefined);
this._widget.setFocused(undefined);
}
});
}
}
export abstract class BreadcrumbsPicker {
readonly focus: dom.IFocusTracker;
protected readonly _onDidPickElement = new Emitter<any>();
readonly onDidPickElement: Event<any> = this._onDidPickElement.event;
protected readonly _disposables = new Array<IDisposable>();
protected readonly _domNode: HTMLDivElement;
protected readonly _tree: WorkbenchTree;
constructor(
container: HTMLElement,
input: BreadcrumbElement,
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
@IThemeService protected readonly _themeService: IThemeService,
) {
this._domNode = document.createElement('div');
this._domNode.style.background = this._themeService.getTheme().getColor(SIDE_BAR_BACKGROUND).toString();
container.appendChild(this._domNode);
this._tree = this._instantiationService.createInstance(WorkbenchTree, this._domNode, this._completeTreeConfiguration({ dataSource: undefined }), {});
debounceEvent(this._tree.onDidChangeSelection, (_last, cur) => cur, 0)(this._onDidChangeSelection, this, this._disposables);
this.focus = dom.trackFocus(this._domNode);
this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables);
this._tree.domFocus();
this._tree.setInput(this._getInput(input));
}
dispose(): void {
dispose(this._disposables);
this._onDidPickElement.dispose();
this._tree.dispose();
this.focus.dispose();
}
layout(dim: dom.Dimension) {
this._domNode.style.width = `${dim.width}px`;
this._domNode.style.height = `${dim.height}px`;
this._tree.layout(dim.height, dim.width);
}
protected abstract _getInput(input: BreadcrumbElement): any;
protected abstract _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration;
protected abstract _onDidChangeSelection(e: any): void;
}
export class FileDataSource implements IDataSource {
private readonly _parents = new WeakMap<IFileStat, IFileStat>();
constructor(
@IFileService private readonly _fileService: IFileService,
) { }
getId(tree: ITree, element: IFileStat | URI): string {
return URI.isUri(element) ? element.toString() : element.resource.toString();
}
hasChildren(tree: ITree, element: IFileStat | URI): boolean {
return URI.isUri(element) || element.isDirectory;
}
getChildren(tree: ITree, element: IFileStat | URI): TPromise<IFileStat[]> {
return this._fileService.resolveFile(
URI.isUri(element) ? element : element.resource
).then(stat => {
for (const child of stat.children) {
this._parents.set(child, stat);
}
return stat.children;
});
}
getParent(tree: ITree, element: IFileStat | URI): TPromise<IFileStat> {
return TPromise.as(URI.isUri(element) ? undefined : this._parents.get(element));
}
}
export class FileRenderer implements IRenderer {
constructor(
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) { }
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._instantiationService.createInstance(FileLabel, container, {});
}
renderElement(tree: ITree, element: IFileStat, templateId: string, templateData: FileLabel): void {
templateData.setFile(element.resource, {
hidePath: true,
fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE,
fileDecorations: { colors: true, badges: true }
});
}
disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void {
templateData.dispose();
}
}
export class FileSorter implements ISorter {
compare(tree: ITree, a: IFileStat, b: IFileStat): number {
if (a.isDirectory === b.isDirectory) {
// same type -> compare on names
return compareFileNames(a.name, b.name);
} else if (a.isDirectory) {
return -1;
} else {
return 1;
}
}
}
export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
protected _getInput(input: BreadcrumbElement): any {
let { uri } = (input as FileElement);
return dirname(uri);
}
protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration {
// todo@joh reuse explorer implementations?
config.dataSource = this._instantiationService.createInstance(FileDataSource);
config.renderer = this._instantiationService.createInstance(FileRenderer);
config.sorter = new FileSorter();
return config;
}
protected _onDidChangeSelection(e: ISelectionEvent): void {
let [first] = e.selection;
let stat = first as IFileStat;
if (stat && !stat.isDirectory) {
this._onDidPickElement.fire(stat.resource);
}
}
}
export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
protected _getInput(input: BreadcrumbElement): any {
return (input as TreeElement).parent;
}
protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration {
config.dataSource = this._instantiationService.createInstance(OutlineDataSource);
config.renderer = this._instantiationService.createInstance(OutlineRenderer);
config.controller = this._instantiationService.createInstance(OutlineController, {});
config.sorter = new OutlineItemComparator();
return config;
}
protected _onDidChangeSelection(e: ISelectionEvent): void {
if (e.payload && e.payload.didClickOnTwistie) {
return;
}
let [first] = e.selection;
if (first instanceof OutlineElement) {
this._onDidPickElement.fire(first);
}
}
}
//#region config
abstract class Config<T> {
name: string;
value: T;
onDidChange: Event<T>;
abstract dispose(): void;
static create<T>(service: IConfigurationService, name: string): Config<T> {
let value: T = service.getValue(name);
let onDidChange = new Emitter<T>();
let listener = service.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(name)) {
value = service.getValue(name);
onDidChange.fire(value);
}
});
return {
name,
get value() { return value; },
onDidChange: onDidChange.event,
dispose(): void {
listener.dispose();
onDidChange.dispose();
}
};
}
}
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
id: 'breadcrumbs',
title: localize('title', "Breadcrumb Navigation"),
order: 101,
type: 'object',
properties: {
'breadcrumbs.enabled': {
'description': localize('enabled', "Enable/disable navigation breadcrumbss"),
'type': 'boolean',
'default': false
}
}
});
//#endregion
//#region commands
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.focus',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT,
when: EditorBreadcrumbs.CK_BreadcrumbsVisible,
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.breadcrumbs.focus();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.focusNext',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyCode.RightArrow,
when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused),
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.breadcrumbs.focusNext();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.focusPrevious',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyCode.LeftArrow,
when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused),
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.breadcrumbs.focusPrev();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.selectFocused',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyCode.Enter,
secondary: [KeyCode.UpArrow, KeyCode.Space],
when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused),
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.breadcrumbs.select();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.selectEditor',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyCode.Escape,
when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused),
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.activeControl.focus();
}
});
//#endregion
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { equals } from 'vs/base/common/arrays';
import { TimeoutTimer } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { size } from 'vs/base/common/collections';
import { onUnexpectedError } from 'vs/base/common/errors';
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import * as paths from 'vs/base/common/paths';
import { isEqual } from 'vs/base/common/resources';
import URI from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IPosition } from 'vs/editor/common/core/position';
import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
export class FileElement {
constructor(
readonly uri: URI,
readonly isFile: boolean
) { }
}
export type BreadcrumbElement = FileElement | OutlineGroup | OutlineElement;
export class EditorBreadcrumbsModel {
private readonly _disposables: IDisposable[] = [];
private readonly _fileElements: FileElement[] = [];
private _outlineElements: (OutlineGroup | OutlineElement)[] = [];
private _outlineDisposables: IDisposable[] = [];
private _onDidUpdate = new Emitter<this>();
readonly onDidUpdate: Event<this> = this._onDidUpdate.event;
constructor(
private readonly _uri: URI,
private readonly _editor: ICodeEditor | undefined,
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
) {
this._fileElements = EditorBreadcrumbsModel._getFileElements(this._uri, workspaceService);
this._bindToEditor();
this._onDidUpdate.fire(this);
}
dispose(): void {
dispose(this._disposables);
}
getElements(): ReadonlyArray<BreadcrumbElement> {
return [].concat(this._fileElements, this._outlineElements);
}
private static _getFileElements(uri: URI, workspaceService: IWorkspaceContextService): FileElement[] {
let result: FileElement[] = [];
let workspace = workspaceService.getWorkspaceFolder(uri);
let path = uri.path;
while (path !== '/') {
if (workspace && isEqual(workspace.uri, uri)) {
break;
}
result.push(new FileElement(uri, result.length === 0));
path = paths.dirname(path);
uri = uri.with({ path });
}
return result.reverse();
}
private _bindToEditor(): void {
if (!this._editor) {
return;
}
// update as model changes
this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline()));
this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline()));
this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline()));
this._disposables.push(debounceEvent(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline(true)));
this._updateOutline();
// stop when editor dies
this._disposables.push(this._editor.onDidDispose(() => this._outlineDisposables = dispose(this._outlineDisposables)));
}
private _updateOutline(didChangeContent?: boolean): void {
this._outlineDisposables = dispose(this._outlineDisposables);
if (!didChangeContent) {
this._updateOutlineElements([]);
}
const buffer = this._editor.getModel();
if (!buffer || !DocumentSymbolProviderRegistry.has(buffer) || !isEqual(buffer.uri, this._uri)) {
return;
}
const source = new CancellationTokenSource();
this._outlineDisposables.push({
dispose: () => {
source.cancel();
source.dispose();
}
});
OutlineModel.create(buffer, source.token).then(model => {
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
const timeout = new TimeoutTimer();
const lastVersionId = buffer.getVersionId();
this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => {
timeout.cancelAndSet(() => {
if (!buffer.isDisposed() && lastVersionId === buffer.getVersionId()) {
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
}
}, 150);
}));
this._outlineDisposables.push(timeout);
}).catch(err => {
this._updateOutlineElements([]);
onUnexpectedError(err);
});
}
private _getOutlineElements(model: OutlineModel, position: IPosition): (OutlineGroup | OutlineElement)[] {
if (!model) {
return [];
}
let item: OutlineGroup | OutlineElement = model.getItemEnclosingPosition(position);
let chain: (OutlineGroup | OutlineElement)[] = [];
while (item) {
chain.push(item);
let parent = item.parent;
if (parent instanceof OutlineModel) {
break;
}
if (parent instanceof OutlineGroup && size(parent.parent.children) === 1) {
break;
}
item = parent;
}
return chain.reverse();
}
private _updateOutlineElements(elements: (OutlineGroup | OutlineElement)[]): void {
if (!equals(elements, this._outlineElements, EditorBreadcrumbsModel._outlineElementEquals)) {
this._outlineElements = elements;
this._onDidUpdate.fire(this);
}
}
private static _outlineElementEquals(a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): boolean {
if (a === b) {
return true;
} else if (!a || !b) {
return false;
} else {
return a.id === b.id;
}
}
}
......@@ -19,7 +19,7 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_FOCUSED_EMPTY_BORDER } from 'vs/workbench/common/theme';
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder, IEditorBreadcrumbs } from 'vs/workbench/services/group/common/editorGroupsService';
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl';
import { IProgressService } from 'vs/platform/progress/common/progress';
......@@ -46,6 +46,7 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { EditorBreadcrumbs } from 'vs/workbench/browser/parts/editor/editorBreadcrumbs';
export class EditorGroupView extends Themable implements IEditorGroupView {
......@@ -104,6 +105,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private titleContainer: HTMLElement;
private titleAreaControl: TitleControl;
private breadcrumbsContainer: HTMLElement;
private breadcrumbsControl: EditorBreadcrumbs;
private progressBar: ProgressBar;
private editorContainer: HTMLElement;
......@@ -190,6 +194,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Title control
this.createTitleAreaControl();
// Breadcrumbs container
this.breadcrumbsContainer = document.createElement('div');
addClass(this.breadcrumbsContainer, 'editor-breadcrumbs');
this.element.appendChild(this.breadcrumbsContainer);
// Breadcrumbs control
this.createEditorBreadcrumbs();
// Editor container
this.editorContainer = document.createElement('div');
addClass(this.editorContainer, 'editor-container');
......@@ -397,6 +409,16 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
}
private createEditorBreadcrumbs(): void {
if (this.breadcrumbsControl) {
this.breadcrumbsControl.dispose();
clearNode(this.breadcrumbsContainer);
}
this.breadcrumbsControl = this.scopedInstantiationService.createInstance(EditorBreadcrumbs, this.breadcrumbsContainer, this);
}
private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): TPromise<void> {
if (this._group.count === 0) {
return TPromise.as(void 0); // nothing to show
......@@ -593,6 +615,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this._label;
}
get breadcrumbs(): IEditorBreadcrumbs {
return this.breadcrumbsControl;
}
get disposed(): boolean {
return this._disposed;
}
......@@ -618,6 +644,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Update title control
this.titleAreaControl.setActive(isActive);
this.breadcrumbsControl.setActive(isActive);
// Update styles
this.updateStyles();
......@@ -789,6 +817,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Show in title control after editor control because some actions depend on it
this.titleAreaControl.openEditor(editor);
this.breadcrumbsControl.openEditor(editor);
return openEditorPromise;
}
......@@ -956,8 +986,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this.doCloseInactiveEditor(editor);
}
// Forward to title control
// Forward to title control & breadcrumbs
this.titleAreaControl.closeEditor(editor);
this.breadcrumbsControl.closeEditor(editor);
}
private doCloseActiveEditor(focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
......@@ -1345,7 +1376,15 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Forward to controls
this.titleAreaControl.layout(new Dimension(this.dimension.width, EDITOR_TITLE_HEIGHT));
this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - EDITOR_TITLE_HEIGHT));
this.breadcrumbsControl.layout(new Dimension(this.dimension.width, this.breadcrumbsControl.getPreferredHeight()));
this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + this.breadcrumbsControl.getPreferredHeight())));
}
relayout(): void {
if (this.dimension) {
const { width, height } = this.dimension;
this.layout(width, height);
}
}
toJSON(): ISerializedEditorGroup {
......@@ -1365,6 +1404,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this.titleAreaControl.dispose();
this.breadcrumbsControl.dispose();
super.dispose();
}
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .editor-breadcrumbs {
opacity: .8;
}
.monaco-workbench>.part.editor>.content .editor-group-container .editor-breadcrumbs .monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/
padding-left: 8px;
}
......@@ -76,59 +76,6 @@
color: inherit !important;
}
.monaco-workbench .outline-panel .outline-element {
display: flex;
flex: 1;
flex-flow: row nowrap;
align-items: center;
}
.monaco-workbench .outline-panel .outline-element .outline-element-icon {
padding-right: 3px;
}
.monaco-workbench .outline-panel.no-icons .outline-element .outline-element-icon {
display: none;
}
.monaco-workbench .outline-panel .outline-element .outline-element-label {
text-overflow: ellipsis;
overflow: hidden;
color: var(--outline-element-color);
}
.monaco-workbench .outline-panel .outline-element .outline-element-label .monaco-highlighted-label .highlight {
font-weight: bold;
}
.monaco-workbench .outline-panel .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-workbench .outline-panel .monaco-tree-row.focused .outline-element .outline-element-detail {
visibility: inherit;
}
.monaco-workbench .outline-panel .outline-element .outline-element-decoration {
opacity: 0.75;
font-size: 90%;
font-weight: 600;
padding: 0 12px 0 5px;
margin-left: auto;
text-align: center;
color: var(--outline-element-color);
}
.monaco-workbench .outline-panel .outline-element .outline-element-decoration.bubble {
font-family: octicons;
font-size: 14px;
opacity: 0.4;
}
......@@ -312,6 +312,13 @@ export interface IGroupChangeEvent {
editorIndex?: number;
}
export interface IEditorBreadcrumbs {
focus(): void;
focusNext(): void;
focusPrev(): void;
select(): void;
}
export interface IEditorGroup {
/**
......@@ -332,6 +339,11 @@ export interface IEditorGroup {
*/
readonly label: string;
/**
*
*/
readonly breadcrumbs: IEditorBreadcrumbs;
/**
* The active control is the currently visible control of the group.
*/
......@@ -476,4 +488,4 @@ export interface IEditorGroup {
* Invoke a function in the context of the services of this group.
*/
invokeWithinContext<T>(fn: (accessor: ServicesAccessor) => T): T;
}
\ No newline at end of file
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel';
import { TestContextService } from 'vs/workbench/test/workbenchTestServices';
suite('Breadcrumb Model', function () {
const workspaceService = new TestContextService(new Workspace('ffff', 'Test', [new WorkspaceFolder({ uri: URI.parse('foo:/bar/baz/ws'), name: 'ws', index: 0 })]));
test('only uri, inside workspace', function () {
let model = new EditorBreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, workspaceService);
let elements = model.getElements();
assert.equal(elements.length, 3);
let [one, two, three] = elements as FileElement[];
assert.equal(one.isFile, false);
assert.equal(two.isFile, false);
assert.equal(three.isFile, true);
assert.equal(one.uri.toString(), 'foo:/bar/baz/ws/some');
assert.equal(two.uri.toString(), 'foo:/bar/baz/ws/some/path');
assert.equal(three.uri.toString(), 'foo:/bar/baz/ws/some/path/file.ts');
});
test('only uri, outside workspace', function () {
let model = new EditorBreadcrumbsModel(URI.parse('foo:/outside/file.ts'), undefined, workspaceService);
let elements = model.getElements();
assert.equal(elements.length, 2);
let [one, two] = elements as FileElement[];
assert.equal(one.isFile, false);
assert.equal(two.isFile, true);
assert.equal(one.uri.toString(), 'foo:/outside');
assert.equal(two.uri.toString(), 'foo:/outside/file.ts');
});
});
......@@ -67,7 +67,7 @@ import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensi
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations';
import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, EditorsOrder, IFindGroupScope, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, EditorsOrder, IFindGroupScope, EditorGroupLayout, IEditorBreadcrumbs } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorService, IOpenEditorOverrideHandler } from 'vs/workbench/services/editor/common/editorService';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
......@@ -583,6 +583,7 @@ export class TestEditorGroup implements IEditorGroupView {
constructor(public id: number) { }
group: EditorGroup = void 0;
breadcrumbs: IEditorBreadcrumbs;
activeControl: IEditor;
activeEditor: IEditorInput;
previewEditor: IEditorInput;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册