提交 7e81c46f 编写于 作者: B Benjamin Pasero

grid - adopt new services in title controls

上级 c8efe59e
......@@ -48,8 +48,8 @@ export class NextNoTabsTitleControl extends NextTitleControl {
this.createEditorActionsToolBar(actionsContainer);
// Context Menu
this._register(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu({ group: this.group, editor: this.group.activeEditor }, e, this.titleContainer)));
this._register(DOM.addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu({ group: this.group, editor: this.group.activeEditor }, e, this.titleContainer)));
this._register(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
this._register(DOM.addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
}
private onTitleLabelClick(e: MouseEvent): void {
......@@ -60,7 +60,7 @@ export class NextNoTabsTitleControl extends NextTitleControl {
private onTitleDoubleClick(e: MouseEvent): void {
DOM.EventHelper.stop(e);
this.editorGroupService.pinEditor(this.group, this.group.activeEditor);
this.groupController.pinEditor(this.group.activeEditor);
}
private onTitleClick(e: MouseEvent | GestureEvent): void {
......@@ -75,7 +75,7 @@ export class NextNoTabsTitleControl extends NextTitleControl {
// - mouse click: do not focus group if there are more than one as it otherwise makes group DND funky
// - touch: always focus
else if ((this.stacks.groups.length === 1 || !(e instanceof MouseEvent)) && !DOM.isAncestor(((e as GestureEvent).initialTarget || e.target || e.srcElement) as HTMLElement, this.editorActionsToolbar.getContainer())) {
this.editorGroupService.focusGroup(this.group);
this.groupController.focus();
}
}
......@@ -89,7 +89,7 @@ export class NextNoTabsTitleControl extends NextTitleControl {
}
const isPinned = this.group.isPinned(this.group.activeEditor);
const isActive = this.nextEditorGroupsService.isGroupActive(this.group.id);
const isActive = this.groupController.isActive;
// Dirty state
if (editor.isDirty()) {
......@@ -102,7 +102,7 @@ export class NextNoTabsTitleControl extends NextTitleControl {
const resource = toResource(editor, { supportSideBySide: true });
const name = editor.getName() || '';
const labelFormat = this.editorGroupService.getTabOptions().labelFormat;
const labelFormat = 'default'; //TODO@grid support tab options (this.editorGroupService.getTabOptions().labelFormat);
let description: string;
if (labelFormat === 'default' && !isActive) {
description = ''; // hide description when group is not active and style is 'default'
......
......@@ -13,7 +13,7 @@ import * as DOM from 'vs/base/browser/dom';
import { isMacintosh } from 'vs/base/common/platform';
import { shorten } from 'vs/base/common/labels';
import { ActionRunner, IAction } from 'vs/base/common/actions';
import { Position, IEditorInput, Verbosity, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
import { IEditorInput, Verbosity, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
import { IEditorGroup, toResource } from 'vs/workbench/common/editor';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
......@@ -22,7 +22,7 @@ import { ResourceLabel } from 'vs/workbench/browser/labels';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IEditorTabOptions } from 'vs/workbench/services/group/common/groupService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
......@@ -43,6 +43,7 @@ import { Color } from 'vs/base/common/color';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { INextEditorGroupsService } from 'vs/workbench/services/editor/common/nextEditorGroupsService';
import { INextEditorService } from 'vs/workbench/services/editor/common/nextEditorService';
interface IEditorInputLabel {
name: string;
......@@ -70,9 +71,8 @@ export class NextTabsTitleControl extends NextTitleControl {
group: IEditorGroup,
@IContextMenuService contextMenuService: IContextMenuService,
@IInstantiationService instantiationService: IInstantiationService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@INextEditorGroupsService nextEditorGroupsService: INextEditorGroupsService,
@INextEditorService private nextEditorService: INextEditorService,
@IContextKeyService contextKeyService: IContextKeyService,
@IKeybindingService keybindingService: IKeybindingService,
@ITelemetryService telemetryService: ITelemetryService,
......@@ -82,7 +82,7 @@ export class NextTabsTitleControl extends NextTitleControl {
@IThemeService themeService: IThemeService,
@IExtensionService extensionService: IExtensionService
) {
super(parent, group, contextMenuService, instantiationService, editorService, editorGroupService, nextEditorGroupsService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService);
super(parent, group, contextMenuService, instantiationService, nextEditorGroupsService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService);
this.tabDisposeables = [];
this.editorLabels = [];
......@@ -93,7 +93,6 @@ export class NextTabsTitleControl extends NextTitleControl {
}
private createScopedInstantiationService(): IInstantiationService {
const stacks = this.editorGroupService.getStacksModel();
const delegatingEditorService = this.instantiationService.createInstance(DelegatingWorkbenchEditorService);
// We create a scoped instantiation service to override the behaviour when closing an inactive editor
......@@ -102,10 +101,10 @@ export class NextTabsTitleControl extends NextTitleControl {
// the inactive editors because closing the active one will always cause a tab switch that sets focus.
// We also want to block the tabs container to reveal the currently active tab because that makes it very
// hard to close multiple inactive tabs next to each other.
delegatingEditorService.setEditorCloseHandler((position, editor) => {
const group = stacks.groupAt(position);
if (group && stacks.isActive(group) && !group.isActive(editor)) {
this.editorGroupService.focusGroup(group);
delegatingEditorService.setEditorCloseHandler((groupId, editor) => {
const group = this.nextEditorGroupsService.getGroup(groupId);
if (group.isActive && group.activeEditor !== editor) {
group.focus();
}
this.blockRevealActiveTab = true;
......@@ -139,7 +138,7 @@ export class NextTabsTitleControl extends NextTitleControl {
if (target instanceof HTMLElement && target.className.indexOf('tabs-container') === 0) {
DOM.EventHelper.stop(e);
this.editorService.openEditor({ options: { pinned: true, index: this.group.count /* always at the end */ } } as IUntitledResourceInput).done(null, errors.onUnexpectedError); // untitled are always pinned
this.nextEditorService.openEditor({ options: { pinned: true, index: this.group.count /* always at the end */ } } as IUntitledResourceInput); // untitled are always pinned
}
}));
......@@ -211,10 +210,7 @@ export class NextTabsTitleControl extends NextTitleControl {
const target = e.target;
if (target instanceof HTMLElement && target.className.indexOf('tabs-container') === 0) {
const targetPosition = this.stacks.positionOfGroup(this.group);
const targetIndex = this.group.count;
this.onDrop(e, this.group, targetPosition, targetIndex);
this.onDrop(e, this.group.count);
}
}));
......@@ -262,7 +258,7 @@ export class NextTabsTitleControl extends NextTitleControl {
const labels = this.getTabLabels(editorsOfGroup);
// Tab label and styles
const isGroupActive = this.nextEditorGroupsService.isGroupActive(this.group.id);
const isGroupActive = this.groupController.isActive;
editorsOfGroup.forEach((editor, index) => {
const tabContainer = this.tabsContainer.children[index] as HTMLElement;
if (!tabContainer) {
......@@ -285,7 +281,7 @@ export class NextTabsTitleControl extends NextTitleControl {
tabContainer.style.borderRightColor = (index === editorsOfGroup.length - 1) ? (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)) : null;
tabContainer.style.outlineColor = this.getColor(activeContrastBorder);
const tabOptions = this.editorGroupService.getTabOptions();
const tabOptions = {} as IEditorTabOptions; // TODO@grid support tab options (this.editorGroupService.getTabOptions());
['off', 'left', 'right'].forEach(option => {
const domAction = tabOptions.tabCloseButton === option ? DOM.addClass : DOM.removeClass;
......@@ -349,7 +345,7 @@ export class NextTabsTitleControl extends NextTitleControl {
}
private getTabLabels(editors: IEditorInput[]): IEditorInputLabel[] {
const labelFormat = this.editorGroupService.getTabOptions().labelFormat;
const labelFormat = 'default'; // TODO@grid support tab options (this.editorGroupService.getTabOptions().labelFormat);
const { verbosity, shortenDuplicates } = this.getLabelConfigFlags(labelFormat);
// Build labels and descriptions for each editor
......@@ -613,9 +609,8 @@ export class NextTabsTitleControl extends NextTitleControl {
return void 0; // only for left mouse click
}
const { editor, position } = this.getGroupPositionAndEditor(index);
if (!this.isTabActionBar(((e as GestureEvent).initialTarget || e.target || e.srcElement) as HTMLElement)) {
setTimeout(() => this.editorService.openEditor(editor, null, position).done(null, errors.onUnexpectedError)); // timeout to keep focus in editor after mouse up
setTimeout(() => this.groupController.openEditor(this.group.getEditor(index))); // timeout to keep focus in editor after mouse up
}
return void 0;
......@@ -624,9 +619,7 @@ export class NextTabsTitleControl extends NextTitleControl {
const showContextMenu = (e: Event) => {
DOM.EventHelper.stop(e);
const { group, editor } = this.getGroupPositionAndEditor(index);
this.onContextMenu({ group, editor }, e, tab);
this.onContextMenu(this.group.getEditor(index), e, tab);
};
// Open on Click
......@@ -668,12 +661,10 @@ export class NextTabsTitleControl extends NextTitleControl {
const event = new StandardKeyboardEvent(e);
let handled = false;
const { group, position, editor } = this.getGroupPositionAndEditor(index);
// Run action on Enter/Space
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
handled = true;
this.editorService.openEditor(editor, null, position).done(null, errors.onUnexpectedError);
this.groupController.openEditor(this.group.getEditor(index));
}
// Navigate in editors
......@@ -686,13 +677,13 @@ export class NextTabsTitleControl extends NextTitleControl {
} else if (event.equals(KeyCode.Home)) {
targetIndex = 0;
} else {
targetIndex = group.count - 1;
targetIndex = this.group.count - 1;
}
const target = group.getEditor(targetIndex);
const target = this.group.getEditor(targetIndex);
if (target) {
handled = true;
this.editorService.openEditor(target, { preserveFocus: true }, position).done(null, errors.onUnexpectedError);
this.groupController.openEditor(target, { preserveFocus: true });
(<HTMLElement>this.tabsContainer.childNodes[targetIndex]).focus();
}
}
......@@ -711,24 +702,20 @@ export class NextTabsTitleControl extends NextTitleControl {
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.DBLCLICK, (e: MouseEvent) => {
DOM.EventHelper.stop(e);
const { group, editor } = this.getGroupPositionAndEditor(index);
this.editorGroupService.pinEditor(group, editor);
this.groupController.pinEditor(this.group.getEditor(index));
}));
// Context menu
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.CONTEXT_MENU, (e: Event) => {
DOM.EventHelper.stop(e, true);
const { group, editor } = this.getGroupPositionAndEditor(index);
this.onContextMenu({ group, editor }, e, tab);
this.onContextMenu(this.group.getEditor(index), e, tab);
}, true /* use capture to fix https://github.com/Microsoft/vscode/issues/19145 */));
// Drag start
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.DRAG_START, (e: DragEvent) => {
const { group, editor } = this.getGroupPositionAndEditor(index);
this.transfer.setData([new DraggedEditorIdentifier({ editor, group })], DraggedEditorIdentifier.prototype);
const editor = this.group.getEditor(index);
this.transfer.setData([new DraggedEditorIdentifier({ editor, group: this.group })], DraggedEditorIdentifier.prototype);
e.dataTransfer.effectAllowed = 'copyMove';
......@@ -758,8 +745,7 @@ export class NextTabsTitleControl extends NextTitleControl {
let draggedEditorIsTab = false;
const draggedEditor = this.transfer.hasData(DraggedEditorIdentifier.prototype) ? this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier : void 0;
if (draggedEditor) {
const { group, editor } = this.getGroupPositionAndEditor(index);
if (draggedEditor.editor === editor && draggedEditor.group === group) {
if (draggedEditor.editor === this.group.getEditor(index) && draggedEditor.group === this.group) {
draggedEditorIsTab = true;
}
}
......@@ -795,9 +781,7 @@ export class NextTabsTitleControl extends NextTitleControl {
DOM.removeClass(tab, 'dragged-over');
this.updateDropFeedback(tab, false, index);
const { group, position } = this.getGroupPositionAndEditor(index);
this.onDrop(e, group, position, index);
this.onDrop(e, index);
}));
return combinedDisposable(disposables);
......@@ -807,14 +791,7 @@ export class NextTabsTitleControl extends NextTitleControl {
return !!DOM.findParentWithClass(element, 'monaco-action-bar', 'tab');
}
private getGroupPositionAndEditor(index: number): { group: IEditorGroup, position: Position, editor: IEditorInput } {
const position = this.stacks.positionOfGroup(this.group);
const editor = this.group.getEditor(index);
return { group: this.group, position, editor };
}
private onDrop(e: DragEvent, group: IEditorGroup, targetPosition: Position, targetIndex: number): void {
private onDrop(e: DragEvent, targetIndex: number): void {
DOM.EventHelper.stop(e, true);
this.updateDropFeedback(this.tabsContainer, false);
......@@ -825,13 +802,14 @@ export class NextTabsTitleControl extends NextTitleControl {
if (draggedEditor) {
// Move editor to target position and index
if (this.isMoveOperation(e, draggedEditor.group, group)) {
this.editorGroupService.moveEditor(draggedEditor.editor, draggedEditor.group, group, { index: targetIndex });
if (this.isMoveOperation(e, draggedEditor.group)) {
const sourceGroup = this.nextEditorGroupsService.getGroup(draggedEditor.group.id);
sourceGroup.moveEditor(draggedEditor.editor, this.groupController, { index: targetIndex });
}
// Copy: just open editor at target index
else {
this.editorService.openEditor(draggedEditor.editor, { pinned: true, index: targetIndex }, targetPosition).done(null, errors.onUnexpectedError);
this.groupController.openEditor(draggedEditor.editor, { pinned: true, index: targetIndex });
}
this.transfer.clearData();
......@@ -840,14 +818,14 @@ export class NextTabsTitleControl extends NextTitleControl {
// External DND
else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false /* open workspace file as file if dropped */ });
dropHandler.handleDrop(e, () => this.editorGroupService.focusGroup(targetPosition), targetPosition, targetIndex);
dropHandler.handleDrop(e, () => this.groupController.focus(), this.group.id /* TODO@grid position => group id */, targetIndex);
}
}
private isMoveOperation(e: DragEvent, source: IEditorGroup, target: IEditorGroup) {
private isMoveOperation(e: DragEvent, source: IEditorGroup) {
const isCopy = (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
return !isCopy || source.id === target.id;
return !isCopy || source.id === this.group.id;
}
dispose(): void {
......
......@@ -16,9 +16,7 @@ import * as arrays from 'vs/base/common/arrays';
import { IEditorStacksModel, IEditorIdentifier, EditorInput, toResource, IEditorCommandsContext, IEditorGroup, EditorOptions } from 'vs/workbench/common/editor';
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
......@@ -37,7 +35,8 @@ import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension } from 'vs/base/browser/dom';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { INextEditorGroupsService, Direction } from 'vs/workbench/services/editor/common/nextEditorGroupsService';
import { INextEditorGroupsService, Direction, INextEditorGroup } from 'vs/workbench/services/editor/common/nextEditorGroupsService';
import { IEditorInput } from 'vs/platform/editor/common/editor';
export interface IToolbarActions {
primary: IAction[];
......@@ -46,9 +45,11 @@ export interface IToolbarActions {
export interface INextTitleAreaControl extends IDisposable {
openEditor(input: EditorInput, options?: EditorOptions): void;
openEditor(editor: EditorInput, options?: EditorOptions): void;
closeEditor(editor: EditorInput): void;
moveEditor(editor: EditorInput, targetIndex: number): void;
setActive(isActive: boolean): void;
pinEditor(input: EditorInput): void;
pinEditor(editor: EditorInput): void;
layout(dimension: Dimension): void;
}
......@@ -79,8 +80,6 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
protected group: IEditorGroup,
@IContextMenuService protected contextMenuService: IContextMenuService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
@IEditorGroupService protected editorGroupService: IEditorGroupService,
@INextEditorGroupsService protected nextEditorGroupsService: INextEditorGroupsService,
@IContextKeyService protected contextKeyService: IContextKeyService,
@IKeybindingService protected keybindingService: IKeybindingService,
......@@ -93,7 +92,6 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
) {
super(themeService);
this.stacks = editorGroupService.getStacksModel();
this.mapActionsToEditors = Object.create(null);
this.titleAreaUpdateScheduler = new RunOnceScheduler(() => this.onSchedule(), 0);
......@@ -156,6 +154,10 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
}
protected get groupController(): INextEditorGroup {
return this.nextEditorGroupsService.getGroup(this.group.id);
}
protected doUpdate(): void {
this.doRefresh();
}
......@@ -198,13 +200,12 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
}
protected actionItemProvider(action: Action): IActionItem {
const position = this.stacks.positionOfGroup(this.group);
const editor = this.editorService.getVisibleEditors()[position];
const activeControl = this.groupController.activeControl;
// Check Active Editor
let actionItem: IActionItem;
if (editor instanceof BaseEditor) {
actionItem = editor.getActionItem(action);
if (activeControl instanceof BaseEditor) {
actionItem = activeControl.getActionItem(action);
}
// Check extensions
......@@ -220,20 +221,19 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
const secondary: IAction[] = [];
const { group } = identifier;
const position = this.stacks.positionOfGroup(group);
// Update the resource context
this.resourceContext.set(group && toResource(group.activeEditor, { supportSideBySide: true }));
// Editor actions require the editor control to be there, so we retrieve it via service
const control = this.editorService.getVisibleEditors()[position];
if (control instanceof BaseEditor && control.input && typeof control.position === 'number') {
const activeControl = this.groupController.activeControl;
if (activeControl instanceof BaseEditor && activeControl.input && typeof activeControl.position === 'number') {
// Editor Control Actions
let editorActions = this.mapActionsToEditors[control.getId()];
let editorActions = this.mapActionsToEditors[activeControl.getId()];
if (!editorActions) {
editorActions = { primary: control.getActions(), secondary: control.getSecondaryActions() };
this.mapActionsToEditors[control.getId()] = editorActions;
editorActions = { primary: activeControl.getActions(), secondary: activeControl.getSecondaryActions() };
this.mapActionsToEditors[activeControl.getId()] = editorActions;
}
primary.push(...editorActions.primary);
secondary.push(...editorActions.secondary);
......@@ -243,7 +243,7 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
// use the correct context key service per editor only once. Don't
// take this code as sample of how to work with menus
this.disposeOnEditorActions = dispose(this.disposeOnEditorActions);
const widget = control.getControl();
const widget = activeControl.getControl();
const codeEditor = isCodeEditor(widget) && widget || isDiffEditor(widget) && widget.getModifiedEditor();
const scopedContextKeyService = codeEditor && codeEditor.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
......@@ -264,7 +264,7 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
protected updateEditorActionsToolbar(): void {
const editor = this.group.activeEditor;
const isActive = this.nextEditorGroupsService.isGroupActive(this.group.id);
const isActive = this.groupController.isActive;
// Update Editor Actions Toolbar
let primaryEditorActions: IAction[] = [];
......@@ -295,8 +295,7 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
secondaryEditorActions = prepareActions(editorActions.secondary);
const tabOptions = this.editorGroupService.getTabOptions();
tabOptions.showTabs = true; // TODO@grid support real options
const tabOptions = { showTabs: true }; // TODO@grid support real options (this.editorGroupService.getTabOptions();)
const primaryEditorActionIds = primaryEditorActions.map(a => a.id);
if (!tabOptions.showTabs) {
......@@ -328,11 +327,11 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
this.currentSecondaryEditorActionIds = [];
}
protected onContextMenu(identifier: IEditorIdentifier, e: Event, node: HTMLElement): void {
protected onContextMenu(editor: IEditorInput, e: Event, node: HTMLElement): void {
// Update the resource context
const currentContext = this.resourceContext.get();
this.resourceContext.set(toResource(identifier.editor, { supportSideBySide: true }));
this.resourceContext.set(toResource(editor, { supportSideBySide: true }));
// Find target anchor
let anchor: HTMLElement | { x: number, y: number } = node;
......@@ -349,18 +348,15 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(actions),
getActionsContext: () => ({ groupId: identifier.group.id, editorIndex: identifier.group.indexOf(identifier.editor) } as IEditorCommandsContext),
getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.indexOf(editor) } as IEditorCommandsContext),
getKeyBinding: (action) => this.getKeybinding(action),
onHide: (cancel) => {
// restore previous context
this.resourceContext.set(currentContext);
// restore focus to active editor if any
const editor = this.editorService.getActiveEditor();
if (editor) {
editor.focus();
}
// restore focus to active group
this.nextEditorGroupsService.activeGroup.focus();
}
});
}
......@@ -385,11 +381,19 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre
//#region INextTitleAreaControl
openEditor(input: EditorInput, options?: EditorOptions): void {
openEditor(editor: EditorInput, options?: EditorOptions): void {
this.doScheduleRefresh(true); // TODO@grid optimize if possible
}
pinEditor(input: EditorInput): void {
closeEditor(editor: EditorInput): void {
this.doScheduleRefresh(); // TODO@grid optimize if possible
}
moveEditor(editor: EditorInput, targetIndex: number): void {
this.doScheduleUpdate(true); // TODO@grid optimize if possible
}
pinEditor(editor: EditorInput): void {
this.doScheduleUpdate(true); // TODO@grid optimize if possible
}
......
......@@ -789,7 +789,7 @@ export interface IEditorGroup {
}
export interface IEditorIdentifier {
group: IEditorGroup;
group: IEditorGroup; // TODO@grid this should be the group identifier instead
editor: IEditorInput;
}
......
......@@ -597,8 +597,8 @@ export class Workbench implements IPartService {
// Editor service (next editor part)
this.editorPart = this.instantiationService.createInstance(NextEditorPart, Identifiers.EDITOR_PART /*, !this.hasFilesToCreateOpenOrDiff*/);
this.toUnbind.push(toDisposable(() => this.editorPart.shutdown()));
serviceCollection.set(INextEditorService, new SyncDescriptor(NextEditorService, this.editorPart));
serviceCollection.set(INextEditorGroupsService, this.editorPart);
serviceCollection.set(INextEditorService, new SyncDescriptor(NextEditorService));
// Legacy Editor Services
this.noOpEditorPart = new NoOpEditorPart(this.instantiationService);
......
......@@ -5,26 +5,280 @@
'use strict';
import { INextEditorService } from 'vs/workbench/services/editor/common/nextEditorService';
import { NextEditorPart } from 'vs/workbench/browser/parts/editor2/nextEditorPart';
// import { ResourceMap } from 'vs/base/common/map';
// import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
// import { IFileEditorInput, IFileInputFactory, IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
// import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput';
// import { Registry } from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorInput, IResourceInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditor, ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor';
import { GroupIdentifier, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput';
import { Registry } from 'vs/platform/registry/common/platform';
import { ResourceMap } from 'vs/base/common/map';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { Schemas } from 'vs/base/common/network';
import { getPathLabel } from 'vs/base/common/labels';
import { once } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { basename } from 'vs/base/common/paths';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { INextEditorGroupsService, INextEditorGroup } from 'vs/workbench/services/editor/common/nextEditorGroupsService';
import { INextEditorService, IResourceEditor, SIDE_BY_SIDE } from 'vs/workbench/services/editor/common/nextEditorService';
// type ICachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput;
type ICachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput;
export class NextEditorService implements INextEditorService {
public _serviceBrand: any;
_serviceBrand: any;
// private static CACHE: ResourceMap<ICachedEditorInput> = new ResourceMap<ICachedEditorInput>();
// private fileInputFactory: IFileInputFactory;
private static CACHE: ResourceMap<ICachedEditorInput> = new ResourceMap<ICachedEditorInput>();
private fileInputFactory: IFileInputFactory;
constructor(
/* private */ editorPart: NextEditorPart
@INextEditorGroupsService private nextEditorGroupsService: INextEditorGroupsService,
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IInstantiationService private instantiationService: IInstantiationService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IFileService private fileService: IFileService
) {
// this.fileInputFactory = Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).getFileInputFactory();
this.fileInputFactory = Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).getFileInputFactory();
}
openEditor(editor: IEditorInput, options?: IEditorOptions, group?: GroupIdentifier | SIDE_BY_SIDE): Thenable<IEditor>;
openEditor(editor: IResourceEditor, group?: GroupIdentifier | SIDE_BY_SIDE): Thenable<IEditor>;
openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | GroupIdentifier, group?: GroupIdentifier): Thenable<IEditor> {
// Typed Editor Support
if (editor instanceof EditorInput) {
return this.doOpenEditor(editor, this.toOptions(optionsOrGroup as IEditorOptions), group);
}
// Throw error for well known foreign resources (such as a http link) (TODO@ben remove me after this has been adopted)
const resourceInput = <IResourceInput>editor;
if (resourceInput.resource instanceof URI) {
const schema = resourceInput.resource.scheme;
if (schema === Schemas.http || schema === Schemas.https) {
return TPromise.wrapError(new Error('Invalid scheme http/https to open resource as editor. Use IOpenerService instead.'));
}
}
// Untyped Text Editor Support
const textInput = <IResourceEditor>editor;
const typedInput = this.createInput(textInput);
if (typedInput) {
return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), optionsOrGroup as GroupIdentifier);
}
return TPromise.wrap<IEditor>(null);
}
}
\ No newline at end of file
private doOpenEditor(input: IEditorInput, options?: EditorOptions, group?: GroupIdentifier | SIDE_BY_SIDE): Thenable<IEditor> {
let targetGroup: INextEditorGroup;
if (group === -1) {
group = void 0; // TODO@grid find correct side group based on active group
}
if (typeof group === 'number') {
targetGroup = this.nextEditorGroupsService.getGroup(group);
}
if (!targetGroup) {
targetGroup = this.nextEditorGroupsService.activeGroup;
}
return targetGroup.openEditor(input, options).then(() => targetGroup.activeControl);
}
private toOptions(options?: IEditorOptions | EditorOptions): EditorOptions {
if (!options || options instanceof EditorOptions) {
return options as EditorOptions;
}
const textOptions: ITextEditorOptions = options;
if (!!textOptions.selection) {
return TextEditorOptions.create(options);
}
return EditorOptions.create(options);
}
createInput(input: IEditorInput | IResourceEditor): EditorInput {
// Typed Editor Input Support
if (input instanceof EditorInput) {
return input;
}
// Side by Side Support
const resourceSideBySideInput = <IResourceSideBySideInput>input;
if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) {
const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource });
const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource });
return new SideBySideEditorInput(
resourceSideBySideInput.label || masterInput.getName(),
typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(),
detailInput,
masterInput
);
}
// Diff Editor Support
const resourceDiffInput = <IResourceDiffInput>input;
if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) {
const leftInput = this.createInput({ resource: resourceDiffInput.leftResource });
const rightInput = this.createInput({ resource: resourceDiffInput.rightResource });
const label = resourceDiffInput.label || localize('compareLabels', "{0} ↔ {1}", this.toDiffLabel(leftInput, this.workspaceContextService, this.environmentService), this.toDiffLabel(rightInput, this.workspaceContextService, this.environmentService));
return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput);
}
// Untitled file support
const untitledInput = <IUntitledResourceInput>input;
if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === Schemas.untitled)) {
return this.untitledEditorService.createOrGet(
untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource,
untitledInput.language,
untitledInput.contents,
untitledInput.encoding
);
}
// Resource Editor Support
const resourceInput = <IResourceInput>input;
if (resourceInput.resource instanceof URI) {
let label = resourceInput.label;
if (!label && resourceInput.resource.scheme !== Schemas.data) {
label = basename(resourceInput.resource.fsPath); // derive the label from the path (but not for data URIs)
}
return this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding) as EditorInput;
}
return null;
}
private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string, description: string, encoding?: string): ICachedEditorInput {
if (NextEditorService.CACHE.has(resource)) {
const input = NextEditorService.CACHE.get(resource);
if (input instanceof ResourceEditorInput) {
input.setName(label);
input.setDescription(description);
} else if (!(input instanceof DataUriEditorInput)) {
input.setPreferredEncoding(encoding);
}
return input;
}
let input: ICachedEditorInput;
// File
if (this.fileService.canHandleResource(resource)) {
input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService);
}
// Data URI
else if (resource.scheme === Schemas.data) {
input = instantiationService.createInstance(DataUriEditorInput, label, description, resource);
}
// Resource
else {
input = instantiationService.createInstance(ResourceEditorInput, label, description, resource);
}
NextEditorService.CACHE.set(resource, input);
once(input.onDispose)(() => {
NextEditorService.CACHE.delete(resource);
});
return input;
}
private toDiffLabel(input: EditorInput, context: IWorkspaceContextService, environment: IEnvironmentService): string {
const res = input.getResource();
// Do not try to extract any paths from simple untitled editors
if (res.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(res)) {
return input.getName();
}
// Otherwise: for diff labels prefer to see the path as part of the label
return getPathLabel(res.fsPath, context, environment);
}
}
//#region TODO@grid adopt legacy code to find a position based on options
// private findPosition(input: EditorInput, options ?: EditorOptions, sideBySide ?: boolean, ratio ?: number[]): Position;
// private findPosition(input: EditorInput, options ?: EditorOptions, desiredPosition ?: Position, ratio ?: number[]): Position;
// private findPosition(input: EditorInput, options ?: EditorOptions, arg1 ?: any, ratio ?: number[]): Position {
// // With defined ratios, always trust the provided position
// if (ratio && types.isNumber(arg1)) {
// return arg1;
// }
// // No editor open
// const visibleEditors = this.getVisibleEditors();
// const activeEditor = this.getActiveEditor();
// if (visibleEditors.length === 0 || !activeEditor) {
// return Position.ONE; // can only be ONE
// }
// // Ignore revealIfVisible/revealIfOpened option if we got instructed explicitly to
// // * open at a specific index
// // * open to the side
// // * open in a specific group
// const skipReveal = (options && options.index) || arg1 === true /* open to side */ || typeof arg1 === 'number' /* open specific group */;
// // Respect option to reveal an editor if it is already visible
// if (!skipReveal && options && options.revealIfVisible) {
// const group = this.stacks.findGroup(input, true);
// if (group) {
// return this.stacks.positionOfGroup(group);
// }
// }
// // Respect option to reveal an editor if it is open (not necessarily visible)
// if (!skipReveal && (this.revealIfOpen /* workbench.editor.revealIfOpen */ || (options && options.revealIfOpened))) {
// const group = this.stacks.findGroup(input);
// if (group) {
// return this.stacks.positionOfGroup(group);
// }
// }
// // Position is unknown: pick last active or ONE
// if (types.isUndefinedOrNull(arg1) || arg1 === false) {
// const lastActivePosition = this.editorGroupsControl.getActivePosition();
// return lastActivePosition || Position.ONE;
// }
// // Position is sideBySide: Find position relative to active editor
// if (arg1 === true) {
// switch (activeEditor.position) {
// case Position.ONE:
// return Position.TWO;
// case Position.TWO:
// return Position.THREE;
// case Position.THREE:
// return null; // Cannot open to the side of the right/bottom most editor
// }
// return null; // Prevent opening to the side
// }
// // Position is provided, validate it
// if (arg1 === Position.THREE && visibleEditors.length === 1) {
// return Position.TWO;
// }
// return arg1;
// }
//#endregion
\ No newline at end of file
......@@ -6,81 +6,25 @@
'use strict';
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IEditorInput, IResourceInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditor, IEditorOptions } from 'vs/platform/editor/common/editor';
import { GroupIdentifier } from 'vs/workbench/common/editor';
export const INextEditorService = createDecorator<INextEditorService>('nextEditorService');
export type IResourceEditor = IResourceInput | IUntitledResourceInput | IResourceDiffInput | IResourceSideBySideInput;
export type SIDE_BY_SIDE = -1;
// TODO@grid this should provide convinience methods on top of INextEditorGroupsService to make the 99%
// case of opening editors as simple as possible
// Candidates:
// - getVisibleEditors (text only?)
export interface INextEditorService {
_serviceBrand: ServiceIdentifier<any>;
}
// Legacy code to find a position based on options
// private findPosition(input: EditorInput, options ?: EditorOptions, sideBySide ?: boolean, ratio ?: number[]): Position;
// private findPosition(input: EditorInput, options ?: EditorOptions, desiredPosition ?: Position, ratio ?: number[]): Position;
// private findPosition(input: EditorInput, options ?: EditorOptions, arg1 ?: any, ratio ?: number[]): Position {
// // With defined ratios, always trust the provided position
// if (ratio && types.isNumber(arg1)) {
// return arg1;
// }
// // No editor open
// const visibleEditors = this.getVisibleEditors();
// const activeEditor = this.getActiveEditor();
// if (visibleEditors.length === 0 || !activeEditor) {
// return Position.ONE; // can only be ONE
// }
// // Ignore revealIfVisible/revealIfOpened option if we got instructed explicitly to
// // * open at a specific index
// // * open to the side
// // * open in a specific group
// const skipReveal = (options && options.index) || arg1 === true /* open to side */ || typeof arg1 === 'number' /* open specific group */;
// // Respect option to reveal an editor if it is already visible
// if (!skipReveal && options && options.revealIfVisible) {
// const group = this.stacks.findGroup(input, true);
// if (group) {
// return this.stacks.positionOfGroup(group);
// }
// }
// // Respect option to reveal an editor if it is open (not necessarily visible)
// if (!skipReveal && (this.revealIfOpen /* workbench.editor.revealIfOpen */ || (options && options.revealIfOpened))) {
// const group = this.stacks.findGroup(input);
// if (group) {
// return this.stacks.positionOfGroup(group);
// }
// }
// // Position is unknown: pick last active or ONE
// if (types.isUndefinedOrNull(arg1) || arg1 === false) {
// const lastActivePosition = this.editorGroupsControl.getActivePosition();
// return lastActivePosition || Position.ONE;
// }
// // Position is sideBySide: Find position relative to active editor
// if (arg1 === true) {
// switch (activeEditor.position) {
// case Position.ONE:
// return Position.TWO;
// case Position.TWO:
// return Position.THREE;
// case Position.THREE:
// return null; // Cannot open to the side of the right/bottom most editor
// }
// return null; // Prevent opening to the side
// }
// // Position is provided, validate it
// if (arg1 === Position.THREE && visibleEditors.length === 1) {
// return Position.TWO;
// }
// TODO@grid think about a better return type, is the IEditor needed always? Should it be ITextEditor?
openEditor(editor: IEditorInput, options?: IEditorOptions, group?: GroupIdentifier | SIDE_BY_SIDE): Thenable<IEditor>;
openEditor(editor: IResourceEditor, group?: GroupIdentifier | SIDE_BY_SIDE): Thenable<IEditor>;
// return arg1;
// }
\ No newline at end of file
createInput(input: IResourceEditor): IEditorInput;
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册