mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 01:00:29 -04:00
Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
This commit is contained in:
@@ -10,7 +10,6 @@ import Event, { Emitter } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
|
||||
import { ResourceViewer } from 'vs/base/browser/ui/resourceviewer/resourceViewer';
|
||||
import { EditorModel, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
|
||||
@@ -19,6 +18,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer';
|
||||
|
||||
/*
|
||||
* This class is only intended to be subclassed and not instantiated.
|
||||
@@ -29,6 +29,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
|
||||
private binaryContainer: Builder;
|
||||
private scrollbar: DomScrollableElement;
|
||||
private resourceViewerContext: ResourceViewerContext;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -87,7 +88,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
|
||||
// Render Input
|
||||
const model = <BinaryEditorModel>resolvedModel;
|
||||
ResourceViewer.show(
|
||||
this.resourceViewerContext = ResourceViewer.show(
|
||||
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() },
|
||||
this.binaryContainer,
|
||||
this.scrollbar,
|
||||
@@ -132,6 +133,9 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
// Pass on to Binary Container
|
||||
this.binaryContainer.size(dimension.width, dimension.height);
|
||||
this.scrollbar.scanDomNode();
|
||||
if (this.resourceViewerContext) {
|
||||
this.resourceViewerContext.layout(dimension);
|
||||
}
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
@@ -146,4 +150,4 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import {
|
||||
CloseEditorsInGroupAction, CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, KeepEditorAction, CloseOtherEditorsInGroupAction, OpenToSideAction, RevertAndCloseEditorAction,
|
||||
CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, OpenToSideAction, RevertAndCloseEditorAction,
|
||||
NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInGroupOneAction,
|
||||
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, NavigateLastAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, NAVIGATE_IN_GROUP_ONE_PREFIX,
|
||||
OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup, OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction,
|
||||
NAVIGATE_IN_GROUP_TWO_PREFIX, ShowEditorsInGroupThreeAction, NAVIGATE_IN_GROUP_THREE_PREFIX, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction, OpenLastEditorInGroup
|
||||
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, NavigateLastAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction,
|
||||
OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup, OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction,
|
||||
ShowEditorsInGroupThreeAction, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorToFirstGroupAction, MoveEditorToSecondGroupAction, MoveEditorToThirdGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction, OpenLastEditorInGroup
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -41,6 +41,7 @@ import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRe
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { GroupOnePicker, GroupTwoPicker, GroupThreePicker, AllEditorsPicker } from 'vs/workbench/browser/parts/editor/editorPicker';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
// Register String Editor
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
@@ -136,7 +137,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
|
||||
return instantiationService.invokeFunction<UntitledEditorInput>(accessor => {
|
||||
const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput);
|
||||
const resource = !!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource);
|
||||
const filePath = resource.scheme === 'file' ? resource.fsPath : void 0;
|
||||
const filePath = resource.scheme === Schemas.file ? resource.fsPath : void 0;
|
||||
const language = deserialized.modeId;
|
||||
const encoding = deserialized.encoding;
|
||||
|
||||
@@ -213,7 +214,7 @@ Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactor
|
||||
|
||||
// Register Editor Status
|
||||
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
|
||||
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(EditorStatus, StatusbarAlignment.RIGHT, 100 /* High Priority */));
|
||||
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(EditorStatus, StatusbarAlignment.RIGHT, 100 /* towards the left of the right hand side */));
|
||||
|
||||
// Register Status Actions
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
@@ -270,21 +271,21 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
|
||||
new QuickOpenHandlerDescriptor(
|
||||
GroupOnePicker,
|
||||
GroupOnePicker.ID,
|
||||
NAVIGATE_IN_GROUP_ONE_PREFIX,
|
||||
editorCommands.NAVIGATE_IN_GROUP_ONE_PREFIX,
|
||||
editorPickerContextKey,
|
||||
[
|
||||
{
|
||||
prefix: NAVIGATE_IN_GROUP_ONE_PREFIX,
|
||||
prefix: editorCommands.NAVIGATE_IN_GROUP_ONE_PREFIX,
|
||||
needsEditor: false,
|
||||
description: nls.localize('groupOnePicker', "Show Editors in First Group")
|
||||
},
|
||||
{
|
||||
prefix: NAVIGATE_IN_GROUP_TWO_PREFIX,
|
||||
prefix: editorCommands.NAVIGATE_IN_GROUP_TWO_PREFIX,
|
||||
needsEditor: false,
|
||||
description: nls.localize('groupTwoPicker', "Show Editors in Second Group")
|
||||
},
|
||||
{
|
||||
prefix: NAVIGATE_IN_GROUP_THREE_PREFIX,
|
||||
prefix: editorCommands.NAVIGATE_IN_GROUP_THREE_PREFIX,
|
||||
needsEditor: false,
|
||||
description: nls.localize('groupThreePicker', "Show Editors in Third Group")
|
||||
}
|
||||
@@ -296,7 +297,7 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
|
||||
new QuickOpenHandlerDescriptor(
|
||||
GroupTwoPicker,
|
||||
GroupTwoPicker.ID,
|
||||
NAVIGATE_IN_GROUP_TWO_PREFIX,
|
||||
editorCommands.NAVIGATE_IN_GROUP_TWO_PREFIX,
|
||||
editorPickerContextKey,
|
||||
[]
|
||||
)
|
||||
@@ -306,7 +307,7 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
|
||||
new QuickOpenHandlerDescriptor(
|
||||
GroupThreePicker,
|
||||
GroupThreePicker.ID,
|
||||
NAVIGATE_IN_GROUP_THREE_PREFIX,
|
||||
editorCommands.NAVIGATE_IN_GROUP_THREE_PREFIX,
|
||||
editorPickerContextKey,
|
||||
[]
|
||||
)
|
||||
@@ -316,11 +317,11 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
|
||||
new QuickOpenHandlerDescriptor(
|
||||
AllEditorsPicker,
|
||||
AllEditorsPicker.ID,
|
||||
NAVIGATE_ALL_EDITORS_GROUP_PREFIX,
|
||||
editorCommands.NAVIGATE_ALL_EDITORS_GROUP_PREFIX,
|
||||
editorPickerContextKey,
|
||||
[
|
||||
{
|
||||
prefix: NAVIGATE_ALL_EDITORS_GROUP_PREFIX,
|
||||
prefix: editorCommands.NAVIGATE_ALL_EDITORS_GROUP_PREFIX,
|
||||
needsEditor: false,
|
||||
description: nls.localize('allEditorsPicker', "Show All Opened Editors")
|
||||
}
|
||||
@@ -343,13 +344,8 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditor, OpenNe
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'File: Clear Recently Opened', nls.localize('file', "File"));
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(KeepEditorAction, KeepEditorAction.ID, KeepEditorAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter) }), 'View: Keep Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseRightEditorsInGroupAction, CloseRightEditorsInGroupAction.ID, CloseRightEditorsInGroupAction.LABEL), 'View: Close Editors to the Right', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseUnmodifiedEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction.ID, CloseUnmodifiedEditorsInGroupAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U) }), 'View: Close Unmodified Editors in Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInGroupAction, CloseEditorsInGroupAction.ID, CloseEditorsInGroupAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W) }), 'View: Close All Editors in Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseOtherEditorsInGroupAction, CloseOtherEditorsInGroupAction.ID, CloseOtherEditorsInGroupAction.LABEL, { primary: null, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T } }), 'View: Close Other Editors', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInOtherGroupsAction, CloseEditorsInOtherGroupsAction.ID, CloseEditorsInOtherGroupsAction.LABEL), 'View: Close Editors in Other Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, JoinTwoGroupsAction.ID, JoinTwoGroupsAction.LABEL), 'View: Join Editors of Two Groups', category);
|
||||
@@ -368,6 +364,9 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupLeftAction, M
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToFirstGroupAction, MoveEditorToFirstGroupAction.ID, MoveEditorToFirstGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_1 } }), 'View: Move Editor into First Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToSecondGroupAction, MoveEditorToSecondGroupAction.ID, MoveEditorToSecondGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_2, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_2 } }), 'View: Move Editor into Second Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToThirdGroupAction, MoveEditorToThirdGroupAction.ID, MoveEditorToThirdGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_3, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_3 } }), 'View: Move Editor into Third Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Previous Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Next Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward');
|
||||
@@ -410,12 +409,33 @@ editorCommands.setup();
|
||||
// Touch Bar
|
||||
if (isMacintosh) {
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconPath: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')).fsPath },
|
||||
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconPath: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')).fsPath } },
|
||||
group: 'navigation'
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconPath: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')).fsPath },
|
||||
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconPath: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')).fsPath } },
|
||||
group: 'navigation'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Editor Title Context Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close") }, group: '1_close', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOthers', "Close Others") }, group: '1_close', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRight', "Close to the Right") }, group: '1_close', order: 30, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '1_close', order: 40 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '1_close', order: 50 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepOpen', "Keep Open") }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
|
||||
|
||||
// Editor Title Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_INLINE_MODE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
|
||||
// Editor Commands for Command Palette
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepEditor', "Keep Editor"), category }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeEditorsInGroup', "Close All Editors in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOtherEditors', "Close Other Editors"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRightEditors', "Close Editors to the Right"), category } });
|
||||
@@ -9,19 +9,20 @@ import nls = require('vs/nls');
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { EditorInput, TextEditorOptions, EditorOptions, IEditorIdentifier, IEditorContext, ActiveEditorMoveArguments, ActiveEditorMovePositioning, EditorCommands, ConfirmResult } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, TextEditorOptions, EditorOptions, IEditorIdentifier, ActiveEditorMoveArguments, ActiveEditorMovePositioning, EditorCommands, ConfirmResult, IEditorCommandsContext } from 'vs/workbench/common/editor';
|
||||
import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { Position, IEditor, Direction, IResourceInput, IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { Position, IEditor, Direction, IResourceInput, IEditorInput, POSITIONS } from 'vs/platform/editor/common/editor';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IEditorGroupService, GroupArrangement } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, NAVIGATE_IN_GROUP_ONE_PREFIX, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, NAVIGATE_IN_GROUP_THREE_PREFIX, NAVIGATE_IN_GROUP_TWO_PREFIX } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
|
||||
export class SplitEditorAction extends Action {
|
||||
|
||||
@@ -37,10 +38,11 @@ export class SplitEditorAction extends Action {
|
||||
super(id, label, 'split-editor-action');
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
public run(context?: IEditorCommandsContext): TPromise<any> {
|
||||
let editorToSplit: IEditor;
|
||||
if (context) {
|
||||
editorToSplit = this.editorService.getVisibleEditors()[this.editorGroupService.getStacksModel().positionOfGroup(context.group)];
|
||||
const stacks = this.editorGroupService.getStacksModel();
|
||||
editorToSplit = this.editorService.getVisibleEditors()[stacks.positionOfGroup(stacks.getGroup(context.groupId))];
|
||||
} else {
|
||||
editorToSplit = this.editorService.getActiveEditor();
|
||||
}
|
||||
@@ -118,7 +120,7 @@ export class JoinTwoGroupsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
public run(context?: IEditorIdentifier): TPromise<any> {
|
||||
|
||||
const editorStacksModel = this.editorGroupService.getStacksModel();
|
||||
|
||||
@@ -531,38 +533,13 @@ export class CloseEditorAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
@ICommandService private commandService: ICommandService
|
||||
) {
|
||||
super(id, label, 'close-editor-action');
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
const position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null;
|
||||
|
||||
// Close Active Editor
|
||||
if (typeof position !== 'number') {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
if (activeEditor) {
|
||||
return this.editorService.closeEditor(activeEditor.position, activeEditor.input);
|
||||
}
|
||||
}
|
||||
|
||||
let input = context ? context.editor : null;
|
||||
if (!input) {
|
||||
|
||||
// Get Top Editor at Position
|
||||
const visibleEditors = this.editorService.getVisibleEditors();
|
||||
if (visibleEditors[position]) {
|
||||
input = visibleEditors[position].input;
|
||||
}
|
||||
}
|
||||
|
||||
if (input) {
|
||||
return this.editorService.closeEditor(position, input);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
public run(context?: IEditorCommandsContext): TPromise<any> {
|
||||
return this.commandService.executeCommand(CLOSE_EDITOR_COMMAND_ID, void 0, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,9 +562,14 @@ export class RevertAndCloseEditorAction extends Action {
|
||||
const input = activeEditor.input;
|
||||
const position = activeEditor.position;
|
||||
|
||||
return activeEditor.input.revert().then(ok =>
|
||||
this.editorService.closeEditor(position, input)
|
||||
);
|
||||
// first try a normal revert where the contents of the editor are restored
|
||||
return activeEditor.input.revert().then(() => this.editorService.closeEditor(position, input), error => {
|
||||
// if that fails, since we are about to close the editor, we accept that
|
||||
// the editor cannot be reverted and instead do a soft revert that just
|
||||
// enables us to close the editor. With this, a user can always close a
|
||||
// dirty editor even when reverting fails.
|
||||
return activeEditor.input.revert({ soft: true }).then(() => this.editorService.closeEditor(position, input));
|
||||
});
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
@@ -608,7 +590,7 @@ export class CloseLeftEditorsInGroupAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
public run(context?: IEditorIdentifier): TPromise<any> {
|
||||
const editor = getTarget(this.editorService, this.groupService, context);
|
||||
if (editor) {
|
||||
return this.editorService.closeEditors(editor.position, { except: editor.input, direction: Direction.LEFT });
|
||||
@@ -618,30 +600,6 @@ export class CloseLeftEditorsInGroupAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseRightEditorsInGroupAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.closeEditorsToTheRight';
|
||||
public static readonly LABEL = nls.localize('closeEditorsToTheRight', "Close Editors to the Right");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService private groupService: IEditorGroupService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
const editor = getTarget(this.editorService, this.groupService, context);
|
||||
if (editor) {
|
||||
return this.editorService.closeEditors(editor.position, { except: editor.input, direction: Direction.RIGHT });
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseAllEditorsAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.closeAllEditors';
|
||||
@@ -660,65 +618,33 @@ export class CloseAllEditorsAction extends Action {
|
||||
|
||||
// Just close all if there are no or one dirty editor
|
||||
if (this.textFileService.getDirty().length < 2) {
|
||||
return this.editorService.closeAllEditors();
|
||||
return this.editorService.closeEditors();
|
||||
}
|
||||
|
||||
// Otherwise ask for combined confirmation
|
||||
const confirm = this.textFileService.confirmSave();
|
||||
if (confirm === ConfirmResult.CANCEL) {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
let saveOrRevertPromise: TPromise<boolean>;
|
||||
if (confirm === ConfirmResult.DONT_SAVE) {
|
||||
saveOrRevertPromise = this.textFileService.revertAll(null, { soft: true }).then(() => true);
|
||||
} else {
|
||||
saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => r.success));
|
||||
}
|
||||
|
||||
return saveOrRevertPromise.then(success => {
|
||||
if (success) {
|
||||
return this.editorService.closeAllEditors();
|
||||
return this.textFileService.confirmSave().then(confirm => {
|
||||
if (confirm === ConfirmResult.CANCEL) {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
return void 0;
|
||||
let saveOrRevertPromise: TPromise<boolean>;
|
||||
if (confirm === ConfirmResult.DONT_SAVE) {
|
||||
saveOrRevertPromise = this.textFileService.revertAll(null, { soft: true }).then(() => true);
|
||||
} else {
|
||||
saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => r.success));
|
||||
}
|
||||
|
||||
return saveOrRevertPromise.then(success => {
|
||||
if (success) {
|
||||
return this.editorService.closeEditors();
|
||||
}
|
||||
|
||||
return void 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseUnmodifiedEditorsInGroupAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.closeUnmodifiedEditors';
|
||||
public static readonly LABEL = nls.localize('closeUnmodifiedEditors', "Close Unmodified Editors in Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
let position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null;
|
||||
|
||||
// If position is not passed in take the position of the active editor.
|
||||
if (typeof position !== 'number') {
|
||||
const active = this.editorService.getActiveEditor();
|
||||
if (active) {
|
||||
position = active.position;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof position === 'number') {
|
||||
return this.editorService.closeEditors(position, { unmodifiedOnly: true });
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseEditorsInOtherGroupsAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.closeEditorsInOtherGroups';
|
||||
@@ -733,7 +659,7 @@ export class CloseEditorsInOtherGroupsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
public run(context?: IEditorIdentifier): TPromise<any> {
|
||||
let position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null;
|
||||
if (typeof position !== 'number') {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
@@ -743,71 +669,7 @@ export class CloseEditorsInOtherGroupsAction extends Action {
|
||||
}
|
||||
|
||||
if (typeof position === 'number') {
|
||||
return this.editorService.closeAllEditors(position);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseOtherEditorsInGroupAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.closeOtherEditors';
|
||||
public static readonly LABEL = nls.localize('closeOtherEditorsInGroup', "Close Other Editors");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
let position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null;
|
||||
let input = context ? context.editor : null;
|
||||
|
||||
// If position or input are not passed in take the position and input of the active editor.
|
||||
const active = this.editorService.getActiveEditor();
|
||||
if (active) {
|
||||
position = typeof position === 'number' ? position : active.position;
|
||||
input = input ? input : <EditorInput>active.input;
|
||||
}
|
||||
|
||||
if (typeof position === 'number' && input) {
|
||||
return this.editorService.closeEditors(position, { except: input });
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseEditorsInGroupAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.closeEditorsInGroup';
|
||||
public static readonly LABEL = nls.localize('closeEditorsInGroup', "Close All Editors in Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
let position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null;
|
||||
if (typeof position !== 'number') {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
if (activeEditor) {
|
||||
position = activeEditor.position;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof position === 'number') {
|
||||
return this.editorService.closeEditors(position);
|
||||
return this.editorService.closeEditors(POSITIONS.filter(p => p !== position));
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
@@ -828,7 +690,7 @@ export class MoveGroupLeftAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
public run(context?: IEditorIdentifier): TPromise<any> {
|
||||
let position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null;
|
||||
if (typeof position !== 'number') {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
@@ -862,7 +724,7 @@ export class MoveGroupRightAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
public run(context?: IEditorIdentifier): TPromise<any> {
|
||||
let position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null;
|
||||
if (typeof position !== 'number') {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
@@ -941,31 +803,7 @@ export class MaximizeGroupAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class KeepEditorAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.keepEditor';
|
||||
public static readonly LABEL = nls.localize('keepEditor', "Keep Editor");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
const target = getTarget(this.editorService, this.editorGroupService, context);
|
||||
if (target) {
|
||||
this.editorGroupService.pinEditor(target.position, target.input);
|
||||
}
|
||||
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
function getTarget(editorService: IWorkbenchEditorService, editorGroupService: IEditorGroupService, context?: IEditorContext): { input: IEditorInput, position: Position } {
|
||||
function getTarget(editorService: IWorkbenchEditorService, editorGroupService: IEditorGroupService, context?: IEditorIdentifier): { input: IEditorInput, position: Position } {
|
||||
if (context) {
|
||||
return { input: context.editor, position: editorGroupService.getStacksModel().positionOfGroup(context.group) };
|
||||
}
|
||||
@@ -1185,8 +1023,6 @@ export class ClearRecentFilesAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export const NAVIGATE_IN_GROUP_ONE_PREFIX = 'edt one ';
|
||||
|
||||
export class ShowEditorsInGroupOneAction extends QuickOpenAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.showEditorsInFirstGroup';
|
||||
@@ -1203,8 +1039,6 @@ export class ShowEditorsInGroupOneAction extends QuickOpenAction {
|
||||
}
|
||||
}
|
||||
|
||||
export const NAVIGATE_IN_GROUP_TWO_PREFIX = 'edt two ';
|
||||
|
||||
export class ShowEditorsInGroupTwoAction extends QuickOpenAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.showEditorsInSecondGroup';
|
||||
@@ -1221,8 +1055,6 @@ export class ShowEditorsInGroupTwoAction extends QuickOpenAction {
|
||||
}
|
||||
}
|
||||
|
||||
export const NAVIGATE_IN_GROUP_THREE_PREFIX = 'edt three ';
|
||||
|
||||
export class ShowEditorsInGroupThreeAction extends QuickOpenAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.showEditorsInThirdGroup';
|
||||
@@ -1239,40 +1071,6 @@ export class ShowEditorsInGroupThreeAction extends QuickOpenAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowEditorsInGroupAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.showEditorsInGroup';
|
||||
public static readonly LABEL = nls.localize('showEditorsInGroup', "Show Editors in Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(context?: IEditorContext): TPromise<any> {
|
||||
const stacks = this.editorGroupService.getStacksModel();
|
||||
const groupCount = stacks.groups.length;
|
||||
if (groupCount <= 1 || !context) {
|
||||
return this.quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
|
||||
}
|
||||
|
||||
switch (stacks.positionOfGroup(context.group)) {
|
||||
case Position.TWO:
|
||||
return this.quickOpenService.show(NAVIGATE_IN_GROUP_TWO_PREFIX);
|
||||
case Position.THREE:
|
||||
return this.quickOpenService.show(NAVIGATE_IN_GROUP_THREE_PREFIX);
|
||||
}
|
||||
|
||||
return this.quickOpenService.show(NAVIGATE_IN_GROUP_ONE_PREFIX);
|
||||
}
|
||||
}
|
||||
|
||||
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
|
||||
|
||||
export class ShowAllEditorsAction extends QuickOpenAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.showAllEditors';
|
||||
@@ -1547,3 +1345,70 @@ export class MoveEditorToNextGroupAction extends Action {
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class MoveEditorToSpecificGroup extends Action {
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
private position: Position,
|
||||
private editorGroupService: IEditorGroupService,
|
||||
private editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
if (activeEditor && activeEditor.position !== this.position) {
|
||||
this.editorGroupService.moveEditor(activeEditor.input, activeEditor.position, this.position);
|
||||
}
|
||||
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class MoveEditorToFirstGroupAction extends MoveEditorToSpecificGroup {
|
||||
|
||||
public static readonly ID = 'workbench.action.moveEditorToFirstGroup';
|
||||
public static readonly LABEL = nls.localize('moveEditorToFirstGroup', "Move Editor into First Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label, Position.ONE, editorGroupService, editorService);
|
||||
}
|
||||
}
|
||||
|
||||
export class MoveEditorToSecondGroupAction extends MoveEditorToSpecificGroup {
|
||||
|
||||
public static readonly ID = 'workbench.action.moveEditorToSecondGroup';
|
||||
public static readonly LABEL = nls.localize('moveEditorToSecondGroup', "Move Editor into Second Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label, Position.TWO, editorGroupService, editorService);
|
||||
}
|
||||
}
|
||||
|
||||
export class MoveEditorToThirdGroupAction extends MoveEditorToSpecificGroup {
|
||||
|
||||
public static readonly ID = 'workbench.action.moveEditorToThirdGroup';
|
||||
public static readonly LABEL = nls.localize('moveEditorToThirdGroup', "Move Editor into Third Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label, Position.THREE, editorGroupService, editorService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,22 +8,40 @@ import * as types from 'vs/base/common/types';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ActiveEditorMoveArguments, ActiveEditorMovePositioning, ActiveEditorMovePositioningBy, EditorCommands, TextCompareEditorVisible } from 'vs/workbench/common/editor';
|
||||
import { ActiveEditorMoveArguments, ActiveEditorMovePositioning, ActiveEditorMovePositioningBy, EditorCommands, TextCompareEditorVisible, EditorInput, IEditorIdentifier, IEditorCommandsContext } from 'vs/workbench/common/editor';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditor, Position, POSITIONS } from 'vs/platform/editor/common/editor';
|
||||
import { IEditor, Position, POSITIONS, Direction, IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
|
||||
import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IMessageService, Severity, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { EditorStacksModel, EditorGroup } from 'vs/workbench/common/editor/editorStacksModel';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
|
||||
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
|
||||
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
|
||||
export const CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID = 'workbench.action.closeEditorsToTheRight';
|
||||
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.action.closeActiveEditor';
|
||||
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
|
||||
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
|
||||
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
|
||||
export const TOGGLE_DIFF_INLINE_MODE = 'toggle.diff.editorMode';
|
||||
|
||||
export const NAVIGATE_IN_GROUP_ONE_PREFIX = 'edt one ';
|
||||
export const NAVIGATE_IN_GROUP_TWO_PREFIX = 'edt two ';
|
||||
export const NAVIGATE_IN_GROUP_THREE_PREFIX = 'edt three ';
|
||||
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
|
||||
|
||||
export function setup(): void {
|
||||
registerActiveEditorMoveCommand();
|
||||
registerDiffEditorCommands();
|
||||
registerOpenEditorAtIndexCommands();
|
||||
handleCommandDeprecations();
|
||||
registerEditorCommands();
|
||||
}
|
||||
|
||||
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
|
||||
@@ -69,9 +87,8 @@ function registerActiveEditorMoveCommand(): void {
|
||||
}
|
||||
|
||||
function moveActiveEditor(args: ActiveEditorMoveArguments = {}, accessor: ServicesAccessor): void {
|
||||
const showTabs = accessor.get(IEditorGroupService).getTabOptions().showTabs;
|
||||
args.to = args.to || ActiveEditorMovePositioning.RIGHT;
|
||||
args.by = showTabs ? args.by || ActiveEditorMovePositioningBy.TAB : ActiveEditorMovePositioningBy.GROUP;
|
||||
args.by = args.by || ActiveEditorMovePositioningBy.TAB;
|
||||
args.value = types.isUndefined(args.value) ? 1 : args.value;
|
||||
|
||||
const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
|
||||
@@ -168,55 +185,30 @@ function registerDiffEditorCommands(): void {
|
||||
}
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: '_workbench.printStacksModel',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
console.log(`${accessor.get(IEditorGroupService).getStacksModel().toString()}\n\n`);
|
||||
},
|
||||
when: undefined,
|
||||
primary: undefined
|
||||
});
|
||||
id: TOGGLE_DIFF_INLINE_MODE,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: (accessor, resource, context: IEditorCommandsContext) => {
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: '_workbench.validateStacksModel',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
(<EditorStacksModel>accessor.get(IEditorGroupService).getStacksModel()).validate();
|
||||
},
|
||||
when: undefined,
|
||||
primary: undefined
|
||||
});
|
||||
}
|
||||
let editor: IEditor;
|
||||
if (context) {
|
||||
const position = positionAndInput(editorGroupService, editorService, context).position;
|
||||
editor = editorService.getVisibleEditors()[position];
|
||||
} else {
|
||||
editor = editorService.getActiveEditor();
|
||||
}
|
||||
|
||||
function handleCommandDeprecations(): void {
|
||||
const mapDeprecatedCommands = {
|
||||
'workbench.action.files.newFile': 'explorer.newFile',
|
||||
'workbench.action.files.newFolder': 'explorer.newFolder'
|
||||
};
|
||||
|
||||
Object.keys(mapDeprecatedCommands).forEach(deprecatedCommandId => {
|
||||
const newCommandId: string = mapDeprecatedCommands[deprecatedCommandId];
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: deprecatedCommandId,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
const messageService = accessor.get(IMessageService);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
messageService.show(Severity.Warning, {
|
||||
message: nls.localize('commandDeprecated', "Command **{0}** has been removed. You can use **{1}** instead", deprecatedCommandId, newCommandId),
|
||||
actions: [
|
||||
new Action('openKeybindings', nls.localize('openKeybindings', "Configure Keyboard Shortcuts"), null, true, () => {
|
||||
return commandService.executeCommand('workbench.action.openGlobalKeybindings');
|
||||
}),
|
||||
CloseAction
|
||||
]
|
||||
if (editor instanceof TextDiffEditor) {
|
||||
const control = editor.getControl();
|
||||
const isInlineMode = !control.renderSideBySide;
|
||||
control.updateOptions(<IDiffEditorOptions>{
|
||||
renderSideBySide: isInlineMode
|
||||
});
|
||||
},
|
||||
when: undefined,
|
||||
primary: undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -243,7 +235,7 @@ function registerOpenEditorAtIndexCommands(): void {
|
||||
const editor = group.getEditor(editorIndex);
|
||||
|
||||
if (editor) {
|
||||
return editorService.openEditor(editor);
|
||||
return editorService.openEditor(editor).then(() => void 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,4 +260,287 @@ function registerOpenEditorAtIndexCommands(): void {
|
||||
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function registerEditorCommands() {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_SAVED_EDITORS_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const model = editorGroupService.getStacksModel();
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
if (contexts.length === 0 && model.activeGroup) {
|
||||
// If command is triggered from the command palette use the active group
|
||||
contexts.push({ groupId: model.activeGroup.id });
|
||||
}
|
||||
|
||||
let positionOne: { savedOnly: boolean } = void 0;
|
||||
let positionTwo: { savedOnly: boolean } = void 0;
|
||||
let positionThree: { savedOnly: boolean } = void 0;
|
||||
contexts.forEach(c => {
|
||||
switch (model.positionOfGroup(model.getGroup(c.groupId))) {
|
||||
case Position.ONE: positionOne = { savedOnly: true }; break;
|
||||
case Position.TWO: positionTwo = { savedOnly: true }; break;
|
||||
case Position.THREE: positionThree = { savedOnly: true }; break;
|
||||
}
|
||||
});
|
||||
|
||||
return editorService.closeEditors({ positionOne, positionTwo, positionThree });
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
const distinctGroupIds = distinct(contexts.map(c => c.groupId));
|
||||
const model = editorGroupService.getStacksModel();
|
||||
|
||||
if (distinctGroupIds.length) {
|
||||
return editorService.closeEditors(distinctGroupIds.map(gid => model.positionOfGroup(model.getGroup(gid))));
|
||||
}
|
||||
const activeEditor = editorService.getActiveEditor();
|
||||
if (activeEditor) {
|
||||
return editorService.closeEditors(activeEditor.position);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
|
||||
win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
const model = editorGroupService.getStacksModel();
|
||||
|
||||
const editorsToClose = new Map<Position, IEditorInput[]>();
|
||||
|
||||
groupIds.forEach(groupId => {
|
||||
const group = model.getGroup(groupId);
|
||||
const position = model.positionOfGroup(group);
|
||||
if (position >= 0) {
|
||||
const inputs = contexts.map(c => {
|
||||
if (c && groupId === c.groupId && types.isNumber(c.editorIndex)) {
|
||||
return group.getEditor(c.editorIndex);
|
||||
}
|
||||
|
||||
return group.activeEditor;
|
||||
}).filter(input => !!input);
|
||||
|
||||
if (inputs.length) {
|
||||
editorsToClose.set(position, inputs);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (editorsToClose.size === 0) {
|
||||
const activeEditor = editorService.getActiveEditor();
|
||||
if (activeEditor) {
|
||||
return editorService.closeEditor(activeEditor.position, activeEditor.input);
|
||||
}
|
||||
}
|
||||
|
||||
return editorService.closeEditors({
|
||||
positionOne: editorsToClose.get(Position.ONE),
|
||||
positionTwo: editorsToClose.get(Position.TWO),
|
||||
positionThree: editorsToClose.get(Position.THREE)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
const model = editorGroupService.getStacksModel();
|
||||
|
||||
if (contexts.length === 0) {
|
||||
// Cover the case when run from command palette
|
||||
const activeGroup = model.activeGroup;
|
||||
const activeEditor = editorService.getActiveEditorInput();
|
||||
if (activeGroup && activeEditor) {
|
||||
contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.indexOf(activeEditor) });
|
||||
}
|
||||
}
|
||||
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
const editorsToClose = new Map<Position, IEditorInput[]>();
|
||||
groupIds.forEach(groupId => {
|
||||
const group = model.getGroup(groupId);
|
||||
const inputsToSkip = contexts.map(c => {
|
||||
if (c.groupId === groupId && types.isNumber(c.editorIndex)) {
|
||||
return group.getEditor(c.editorIndex);
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}).filter(input => !!input);
|
||||
|
||||
const toClose = group.getEditors().filter(input => inputsToSkip.indexOf(input) === -1);
|
||||
editorsToClose.set(model.positionOfGroup(group), toClose);
|
||||
});
|
||||
|
||||
return editorService.closeEditors({
|
||||
positionOne: editorsToClose.get(Position.ONE),
|
||||
positionTwo: editorsToClose.get(Position.TWO),
|
||||
positionThree: editorsToClose.get(Position.THREE)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
|
||||
const { position, input } = positionAndInput(editorGroupService, editorService, context);
|
||||
|
||||
if (typeof position === 'number' && input) {
|
||||
return editorService.closeEditors(position, { except: input, direction: Direction.RIGHT });
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: KEEP_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter),
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
|
||||
const { position, input } = positionAndInput(editorGroupService, editorService, context);
|
||||
|
||||
if (typeof position === 'number' && input) {
|
||||
return editorGroupService.pinEditor(position, input);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: SHOW_EDITORS_IN_GROUP,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const quickOpenService = accessor.get(IQuickOpenService);
|
||||
|
||||
const stacks = editorGroupService.getStacksModel();
|
||||
const groupCount = stacks.groups.length;
|
||||
if (groupCount <= 1) {
|
||||
return quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
|
||||
}
|
||||
|
||||
const { position } = positionAndInput(editorGroupService, editorService, context);
|
||||
|
||||
switch (position) {
|
||||
case Position.TWO:
|
||||
return quickOpenService.show(NAVIGATE_IN_GROUP_TWO_PREFIX);
|
||||
case Position.THREE:
|
||||
return quickOpenService.show(NAVIGATE_IN_GROUP_THREE_PREFIX);
|
||||
}
|
||||
|
||||
return quickOpenService.show(NAVIGATE_IN_GROUP_ONE_PREFIX);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: '_workbench.printStacksModel',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
console.log(`${accessor.get(IEditorGroupService).getStacksModel().toString()}\n\n`);
|
||||
},
|
||||
when: void 0,
|
||||
primary: void 0
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: '_workbench.validateStacksModel',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
(<EditorStacksModel>accessor.get(IEditorGroupService).getStacksModel()).validate();
|
||||
},
|
||||
when: void 0,
|
||||
primary: void 0
|
||||
});
|
||||
}
|
||||
|
||||
function positionAndInput(editorGroupService: IEditorGroupService, editorService: IWorkbenchEditorService, context?: IEditorCommandsContext): { position: Position, input: IEditorInput } {
|
||||
|
||||
// Resolve from context
|
||||
const model = editorGroupService.getStacksModel();
|
||||
const group = context ? model.getGroup(context.groupId) : undefined;
|
||||
let position = group ? model.positionOfGroup(group) : undefined;
|
||||
let input = group && types.isNumber(context.editorIndex) ? group.getEditor(context.editorIndex) : undefined;
|
||||
|
||||
// If position or input are not passed in take the position and input of the active editor.
|
||||
const active = editorService.getActiveEditor();
|
||||
if (active) {
|
||||
position = typeof position === 'number' ? position : active.position;
|
||||
input = input ? input : <EditorInput>active.input;
|
||||
}
|
||||
|
||||
return { position, input };
|
||||
}
|
||||
|
||||
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService): IEditorCommandsContext[] {
|
||||
// First check for a focused list to return the selected items from
|
||||
const list = listService.lastFocusedList;
|
||||
if (list instanceof List && list.isDOMFocused()) {
|
||||
const elementToContext = (element: IEditorIdentifier | EditorGroup) =>
|
||||
element instanceof EditorGroup ? { groupId: element.id, editorIndex: undefined } : { groupId: element.group.id, editorIndex: element.group.indexOf(element.editor) };
|
||||
const onlyEditorGroupAndEditor = (e: IEditorIdentifier | EditorGroup) => e instanceof EditorGroup || ('editor' in e && 'group' in e);
|
||||
|
||||
const focusedElements: (IEditorIdentifier | EditorGroup)[] = list.getFocusedElements().filter(onlyEditorGroupAndEditor);
|
||||
// need to take into account when editor context is { group: group }
|
||||
const focus = editorContext ? editorContext : focusedElements.length ? focusedElements.map(elementToContext)[0] : undefined;
|
||||
|
||||
if (focus) {
|
||||
const selection: (IEditorIdentifier | EditorGroup)[] = list.getSelectedElements().filter(onlyEditorGroupAndEditor);
|
||||
// Only respect selection if it contains focused element
|
||||
if (selection && selection.some(s => s instanceof EditorGroup ? s.id === focus.groupId : s.group.id === focus.groupId && s.group.indexOf(s.editor) === focus.editorIndex)) {
|
||||
return selection.map(elementToContext);
|
||||
}
|
||||
|
||||
return [focus];
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise go with passed in context
|
||||
return !!editorContext ? [editorContext] : [];
|
||||
}
|
||||
|
||||
@@ -24,22 +24,21 @@ import { IEditorGroupService, IEditorTabOptions, GroupArrangement, GroupOrientat
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
|
||||
import { TitleControl, ITitleAreaControl, handleWorkspaceExternalDrop } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import { ITitleAreaControl } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitleControl';
|
||||
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier } from 'vs/workbench/common/editor';
|
||||
import { extractResources } from 'vs/workbench/browser/editor';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier, EditorInput, PREFERENCES_EDITOR_ID, TEXT_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
|
||||
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { editorBackground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Themable, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, EDITOR_GROUP_BACKGROUND, EDITOR_GROUP_HEADER_TABS_BORDER } from 'vs/workbench/common/theme';
|
||||
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ResourcesDropHandler, LocalSelectionTransfer, DraggedEditorIdentifier } from 'vs/workbench/browser/dnd';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
|
||||
export enum Rochade {
|
||||
NONE,
|
||||
@@ -93,20 +92,30 @@ export interface IEditorGroupsControl {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
interface CenteredEditorLayoutData {
|
||||
leftMarginRatio: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to manage multiple side by side editors for the editor part.
|
||||
*/
|
||||
export class EditorGroupsControl extends Themable implements IEditorGroupsControl, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider {
|
||||
|
||||
private static readonly CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY = 'workbench.centerededitorlayout.data';
|
||||
|
||||
private static readonly TITLE_AREA_CONTROL_KEY = '__titleAreaControl';
|
||||
private static readonly PROGRESS_BAR_CONTROL_KEY = '__progressBar';
|
||||
private static readonly INSTANTIATION_SERVICE_KEY = '__instantiationService';
|
||||
|
||||
private static readonly GOLDEN_RATIO = 0.61;
|
||||
private static readonly MIN_EDITOR_WIDTH = 170;
|
||||
private static readonly MIN_EDITOR_HEIGHT = 70;
|
||||
|
||||
private static readonly EDITOR_TITLE_HEIGHT = 35;
|
||||
|
||||
private static readonly CENTERED_EDITOR_MIN_MARGIN = 10;
|
||||
|
||||
private static readonly SNAP_TO_MINIMIZED_THRESHOLD_WIDTH = 50;
|
||||
private static readonly SNAP_TO_MINIMIZED_THRESHOLD_HEIGHT = 20;
|
||||
|
||||
@@ -131,6 +140,22 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
private sashTwo: Sash;
|
||||
private startSiloThreeSize: number;
|
||||
|
||||
// if the centered editor layout is activated, the editor inside of silo ONE is centered
|
||||
// the silo will then contain:
|
||||
// [left margin]|[editor]|[right margin]
|
||||
// - The size of the editor is defined by centeredEditorSize
|
||||
// - The position is defined by the ratio centeredEditorLeftMarginRatio = left-margin/(left-margin + editor + right-margin).
|
||||
// - The two sashes can be used to control the size and position of the editor inside of the silo.
|
||||
// - In order to seperate the two sashes from the sashes that control the size of bordering widgets
|
||||
// CENTERED_EDITOR_MIN_MARGIN is forced as a minimum size for the two margins.
|
||||
private centeredEditorActive: boolean;
|
||||
private centeredEditorSashLeft: Sash;
|
||||
private centeredEditorSashRight: Sash;
|
||||
private centeredEditorPreferedSize: number;
|
||||
private centeredEditorLeftMarginRatio: number;
|
||||
private centeredEditorDragStartPosition: number;
|
||||
private centeredEditorDragStartSize: number;
|
||||
|
||||
private visibleEditors: BaseEditor[];
|
||||
|
||||
private lastActiveEditor: BaseEditor;
|
||||
@@ -143,20 +168,20 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
private onStacksChangeScheduler: RunOnceScheduler;
|
||||
private stacksChangedBuffer: IStacksModelChangeEvent[];
|
||||
|
||||
private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
|
||||
|
||||
constructor(
|
||||
parent: Builder,
|
||||
groupOrientation: GroupOrientation,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IPartService private partService: IPartService,
|
||||
@IStorageService private storageServise: IStorageService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IExtensionService private extensionService: IExtensionService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@IWindowsService private windowsService: IWindowsService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IWorkspacesService private workspacesService: IWorkspacesService
|
||||
@ITelemetryService private telemetryService: ITelemetryService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
@@ -431,13 +456,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
this.visibleEditorFocusTrackerDisposable[position].dispose();
|
||||
}
|
||||
|
||||
// Track focus on editor container
|
||||
const focusTracker = DOM.trackFocus(editor.getContainer().getHTMLElement());
|
||||
const listenerDispose = focusTracker.onDidFocus(() => {
|
||||
this.visibleEditorFocusTrackerDisposable[position] = editor.onDidFocus(() => {
|
||||
this.onFocusGained(editor);
|
||||
});
|
||||
|
||||
this.visibleEditorFocusTrackerDisposable[position] = combinedDisposable([focusTracker, listenerDispose]);
|
||||
}
|
||||
|
||||
private onFocusGained(editor: BaseEditor): void {
|
||||
@@ -979,41 +1000,74 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
|
||||
// For each position
|
||||
POSITIONS.forEach(position => {
|
||||
const silo = this.silos[position];
|
||||
|
||||
// Containers (they contain everything and can move between silos)
|
||||
const container = $(silo).div({ 'class': 'container' });
|
||||
|
||||
// InstantiationServices
|
||||
const instantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||
[IContextKeyService, this.contextKeyService.createScoped(container.getHTMLElement())]
|
||||
));
|
||||
container.setProperty(EditorGroupsControl.INSTANTIATION_SERVICE_KEY, instantiationService); // associate with container
|
||||
|
||||
// Title containers
|
||||
const titleContainer = $(container).div({ 'class': 'title' });
|
||||
if (this.tabOptions.showTabs) {
|
||||
titleContainer.addClass('tabs');
|
||||
}
|
||||
if (this.tabOptions.showIcons) {
|
||||
titleContainer.addClass('show-file-icons');
|
||||
}
|
||||
this.hookTitleDragListener(titleContainer);
|
||||
|
||||
// Title Control
|
||||
this.createTitleControl(this.stacks.groupAt(position), silo, titleContainer, instantiationService);
|
||||
|
||||
// Progress Bar
|
||||
const progressBar = new ProgressBar($(container));
|
||||
this.toUnbind.push(attachProgressBarStyler(progressBar, this.themeService));
|
||||
progressBar.getContainer().hide();
|
||||
container.setProperty(EditorGroupsControl.PROGRESS_BAR_CONTROL_KEY, progressBar); // associate with container
|
||||
this.createSilo(position);
|
||||
});
|
||||
|
||||
// Update Styles
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
private createSilo(position: Position): void {
|
||||
const silo = this.silos[position];
|
||||
|
||||
// Containers (they contain everything and can move between silos)
|
||||
const container = $(silo).div({ 'class': 'container' });
|
||||
|
||||
// InstantiationServices
|
||||
const instantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||
[IContextKeyService, this.contextKeyService.createScoped(container.getHTMLElement())]
|
||||
));
|
||||
container.setProperty(EditorGroupsControl.INSTANTIATION_SERVICE_KEY, instantiationService); // associate with container
|
||||
|
||||
// Title containers
|
||||
const titleContainer = $(container).div({ 'class': 'title' });
|
||||
if (this.tabOptions.showTabs) {
|
||||
titleContainer.addClass('tabs');
|
||||
}
|
||||
if (this.tabOptions.showIcons) {
|
||||
titleContainer.addClass('show-file-icons');
|
||||
}
|
||||
this.hookTitleDragListener(titleContainer);
|
||||
|
||||
// Title Control
|
||||
this.createTitleControl(this.stacks.groupAt(position), silo, titleContainer, instantiationService);
|
||||
|
||||
// Progress Bar
|
||||
const progressBar = new ProgressBar($(container));
|
||||
this.toUnbind.push(attachProgressBarStyler(progressBar, this.themeService));
|
||||
progressBar.getContainer().hide();
|
||||
container.setProperty(EditorGroupsControl.PROGRESS_BAR_CONTROL_KEY, progressBar); // associate with container
|
||||
|
||||
// Sash for first position to support centered editor layout
|
||||
if (position === Position.ONE) {
|
||||
|
||||
// Center Layout stuff
|
||||
const registerSashListeners = (sash: Sash) => {
|
||||
this.toUnbind.push(sash.onDidStart(() => this.onCenterSashDragStart()));
|
||||
this.toUnbind.push(sash.onDidChange((e: ISashEvent) => this.onCenterSashDrag(sash, e)));
|
||||
this.toUnbind.push(sash.onDidEnd(() => this.storeCenteredLayoutData()));
|
||||
this.toUnbind.push(sash.onDidReset(() => this.resetCenteredEditor()));
|
||||
};
|
||||
this.centeredEditorSashLeft = new Sash(container.getHTMLElement(), this, { baseSize: 5, orientation: Orientation.VERTICAL });
|
||||
this.centeredEditorSashRight = new Sash(container.getHTMLElement(), this, { baseSize: 5, orientation: Orientation.VERTICAL });
|
||||
registerSashListeners(this.centeredEditorSashLeft);
|
||||
registerSashListeners(this.centeredEditorSashRight);
|
||||
this.centeredEditorSashLeft.hide();
|
||||
this.centeredEditorSashRight.hide();
|
||||
|
||||
this.centeredEditorActive = false;
|
||||
this.centeredEditorLeftMarginRatio = 0.5;
|
||||
|
||||
// Restore centered layout position and size
|
||||
const centeredLayoutDataString = this.storageServise.get(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
|
||||
if (centeredLayoutDataString) {
|
||||
const centeredLayout = <CenteredEditorLayoutData>JSON.parse(centeredLayoutDataString);
|
||||
this.centeredEditorLeftMarginRatio = centeredLayout.leftMarginRatio;
|
||||
this.centeredEditorPreferedSize = centeredLayout.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
@@ -1073,8 +1127,17 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
return options;
|
||||
}
|
||||
|
||||
const isCopyDrag = (draggedEditor: IEditorIdentifier, e: DragEvent) => {
|
||||
if (draggedEditor && draggedEditor.editor instanceof EditorInput) {
|
||||
if (!draggedEditor.editor.supportsSplitEditor()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
|
||||
};
|
||||
|
||||
function onDrop(e: DragEvent, position: Position, splitTo?: Position): void {
|
||||
$this.updateFromDropping(node, false);
|
||||
$this.updateFromDragAndDrop(node, false);
|
||||
cleanUp();
|
||||
|
||||
const editorService = $this.editorService;
|
||||
@@ -1084,9 +1147,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
const freeGroup = (stacks.groups.length === 1) ? Position.TWO : Position.THREE;
|
||||
|
||||
// Check for transfer from title control
|
||||
const draggedEditor = TitleControl.getDraggedEditor();
|
||||
if (draggedEditor) {
|
||||
const isCopy = (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
|
||||
if ($this.transfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
const draggedEditor = $this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
|
||||
const isCopy = isCopyDrag(draggedEditor, e);
|
||||
|
||||
// Copy editor to new location
|
||||
if (isCopy) {
|
||||
@@ -1124,44 +1187,22 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
|
||||
// Check for URI transfer
|
||||
else {
|
||||
const droppedResources = extractResources(e).filter(r => r.resource.scheme === 'file' || r.resource.scheme === 'untitled');
|
||||
if (droppedResources.length) {
|
||||
handleWorkspaceExternalDrop(droppedResources, $this.fileService, $this.messageService, $this.windowsService, $this.windowService, $this.workspacesService).then(handled => {
|
||||
if (handled) {
|
||||
return;
|
||||
}
|
||||
const dropHandler = $this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
|
||||
dropHandler.handleDrop(e, () => {
|
||||
if (splitEditor && splitTo !== freeGroup) {
|
||||
groupService.moveGroup(freeGroup, splitTo);
|
||||
}
|
||||
|
||||
// Add external ones to recently open list
|
||||
const externalResources = droppedResources.filter(d => d.isExternal).map(d => d.resource);
|
||||
if (externalResources.length) {
|
||||
$this.windowsService.addRecentlyOpened(externalResources.map(resource => resource.fsPath));
|
||||
}
|
||||
|
||||
// Open in Editor
|
||||
$this.windowService.focusWindow()
|
||||
.then(() => editorService.openEditors(droppedResources.map(d => {
|
||||
return {
|
||||
input: { resource: d.resource, options: { pinned: true } },
|
||||
position: splitEditor ? freeGroup : position
|
||||
};
|
||||
}))).then(() => {
|
||||
if (splitEditor && splitTo !== freeGroup) {
|
||||
groupService.moveGroup(freeGroup, splitTo);
|
||||
}
|
||||
|
||||
groupService.focusGroup(splitEditor ? splitTo : position);
|
||||
})
|
||||
.done(null, errors.onUnexpectedError);
|
||||
});
|
||||
}
|
||||
groupService.focusGroup(splitEditor ? splitTo : position);
|
||||
}, splitEditor ? freeGroup : position);
|
||||
}
|
||||
}
|
||||
|
||||
function positionOverlay(e: DragEvent, groups: number, position: Position): void {
|
||||
const target = <HTMLElement>e.target;
|
||||
const overlayIsSplit = typeof overlay.getProperty(splitToPropertyKey) === 'number';
|
||||
const isCopy = (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
|
||||
const draggedEditor = TitleControl.getDraggedEditor();
|
||||
const draggedEditor = $this.transfer.hasData(DraggedEditorIdentifier.prototype) ? $this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier : void 0;
|
||||
const isCopy = isCopyDrag(draggedEditor, e);
|
||||
|
||||
const overlaySize = $this.layoutVertically ? target.clientWidth : target.clientHeight;
|
||||
const splitThreshold = overlayIsSplit ? overlaySize / 5 : overlaySize / 10;
|
||||
@@ -1266,7 +1307,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
|
||||
// update the dropEffect, otherwise it would look like a "move" operation. but only if we are
|
||||
// not dragging a tab actually because there we support both moving as well as copying
|
||||
if (!TabsTitleControl.getDraggedEditor()) {
|
||||
if (!$this.transfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
@@ -1300,14 +1341,14 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
DOM.EventHelper.stop(e, true);
|
||||
onDrop(e, Position.ONE);
|
||||
} else {
|
||||
this.updateFromDropping(node, false);
|
||||
this.updateFromDragAndDrop(node, false);
|
||||
}
|
||||
}));
|
||||
|
||||
// Drag enter
|
||||
let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470
|
||||
this.toUnbind.push(DOM.addDisposableListener(node, DOM.EventType.DRAG_ENTER, (e: DragEvent) => {
|
||||
if (!TitleControl.getDraggedEditor()) {
|
||||
if (!$this.transfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
// we used to check for the dragged resources here (via dnd.extractResources()) but this
|
||||
// seems to be not possible on Linux and Windows where during DRAG_ENTER the resources
|
||||
// are always undefined up until they are dropped when dragged from the tree. The workaround
|
||||
@@ -1318,7 +1359,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
}
|
||||
|
||||
counter++;
|
||||
this.updateFromDropping(node, true);
|
||||
this.updateFromDragAndDrop(node, true);
|
||||
|
||||
const target = <HTMLElement>e.target;
|
||||
if (target) {
|
||||
@@ -1328,7 +1369,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
createOverlay(target);
|
||||
|
||||
if (overlay) {
|
||||
this.updateFromDropping(node, false); // if we show an overlay, we can remove the drop feedback from the editor background
|
||||
this.updateFromDragAndDrop(node, false); // if we show an overlay, we can remove the drop feedback from the editor background
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -1337,7 +1378,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
this.toUnbind.push(DOM.addDisposableListener(node, DOM.EventType.DRAG_LEAVE, (e: DragEvent) => {
|
||||
counter--;
|
||||
if (counter === 0) {
|
||||
this.updateFromDropping(node, false);
|
||||
this.updateFromDragAndDrop(node, false);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -1345,7 +1386,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
[node, window].forEach(container => {
|
||||
this.toUnbind.push(DOM.addDisposableListener(container, DOM.EventType.DRAG_END, (e: DragEvent) => {
|
||||
counter = 0;
|
||||
this.updateFromDropping(node, false);
|
||||
this.updateFromDragAndDrop(node, false);
|
||||
cleanUp();
|
||||
}));
|
||||
});
|
||||
@@ -1552,6 +1593,14 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
|
||||
// Move to valid position if any
|
||||
if (moveTo !== null) {
|
||||
// TODO@Ben remove me after a while
|
||||
/* __GDPR__
|
||||
"editorGroupMoved" : {
|
||||
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('editorGroupMoved', { source: position, to: moveTo });
|
||||
this.editorGroupService.moveGroup(position, moveTo);
|
||||
}
|
||||
|
||||
@@ -1595,16 +1644,18 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
}
|
||||
}
|
||||
|
||||
private updateFromDropping(element: HTMLElement, isDropping: boolean): void {
|
||||
private updateFromDragAndDrop(element: HTMLElement, isDraggedOver: boolean): void {
|
||||
const groupCount = this.stacks.groups.length;
|
||||
const background = this.getColor(isDropping ? EDITOR_DRAG_AND_DROP_BACKGROUND : groupCount > 0 ? EDITOR_GROUP_BACKGROUND : null);
|
||||
const background = this.getColor(isDraggedOver ? EDITOR_DRAG_AND_DROP_BACKGROUND : groupCount > 0 ? EDITOR_GROUP_BACKGROUND : null);
|
||||
element.style.backgroundColor = background;
|
||||
|
||||
const activeContrastBorderColor = this.getColor(activeContrastBorder);
|
||||
element.style.outlineColor = isDropping ? activeContrastBorderColor : null;
|
||||
element.style.outlineStyle = isDropping && activeContrastBorderColor ? 'dashed' : null;
|
||||
element.style.outlineWidth = isDropping && activeContrastBorderColor ? '2px' : null;
|
||||
element.style.outlineOffset = isDropping && activeContrastBorderColor ? '-2px' : null;
|
||||
element.style.outlineColor = isDraggedOver ? activeContrastBorderColor : null;
|
||||
element.style.outlineStyle = isDraggedOver && activeContrastBorderColor ? 'dashed' : null;
|
||||
element.style.outlineWidth = isDraggedOver && activeContrastBorderColor ? '2px' : null;
|
||||
element.style.outlineOffset = isDraggedOver && activeContrastBorderColor ? '-2px' : null;
|
||||
|
||||
DOM.toggleClass(element, 'dragged-over', isDraggedOver);
|
||||
}
|
||||
|
||||
private posSilo(pos: number, leftTop: string | number, rightBottom?: string | number, borderLeftTopWidth?: string | number): void {
|
||||
@@ -1878,12 +1929,68 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
this.sashTwo.layout();
|
||||
}
|
||||
|
||||
private get centeredEditorAvailableSize(): number {
|
||||
return this.silosSize[Position.ONE] - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2;
|
||||
}
|
||||
|
||||
private get centeredEditorSize(): number {
|
||||
return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferedSize);
|
||||
}
|
||||
|
||||
private get centeredEditorPosition(): number {
|
||||
return EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN + this.centeredEditorLeftMarginRatio * (this.centeredEditorAvailableSize - this.centeredEditorSize);
|
||||
}
|
||||
|
||||
private onCenterSashDragStart(): void {
|
||||
this.centeredEditorDragStartPosition = this.centeredEditorPosition;
|
||||
this.centeredEditorDragStartSize = this.centeredEditorSize;
|
||||
}
|
||||
|
||||
private onCenterSashDrag(sash: Sash, e: ISashEvent): void {
|
||||
const sashesCoupled = !e.altKey;
|
||||
const delta = sash === this.centeredEditorSashLeft ? e.startX - e.currentX : e.currentX - e.startX;
|
||||
const size = this.centeredEditorDragStartSize + (sashesCoupled ? 2 * delta : delta);
|
||||
let position = this.centeredEditorDragStartPosition;
|
||||
if (sash === this.centeredEditorSashLeft || sashesCoupled) {
|
||||
position -= delta;
|
||||
}
|
||||
|
||||
if (size > 3 * this.minSize && size < this.centeredEditorAvailableSize) {
|
||||
this.centeredEditorPreferedSize = size;
|
||||
position -= EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN;
|
||||
position = Math.min(position, this.centeredEditorAvailableSize - this.centeredEditorSize);
|
||||
position = Math.max(0, position);
|
||||
this.centeredEditorLeftMarginRatio = position / (this.centeredEditorAvailableSize - this.centeredEditorSize);
|
||||
|
||||
this.layoutContainers();
|
||||
}
|
||||
}
|
||||
|
||||
private storeCenteredLayoutData(): void {
|
||||
const data: CenteredEditorLayoutData = {
|
||||
leftMarginRatio: this.centeredEditorLeftMarginRatio,
|
||||
size: this.centeredEditorSize
|
||||
};
|
||||
this.storageServise.store(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, JSON.stringify(data), StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
public getVerticalSashTop(sash: Sash): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public getVerticalSashLeft(sash: Sash): number {
|
||||
return sash === this.sashOne ? this.silosSize[Position.ONE] : this.silosSize[Position.TWO] + this.silosSize[Position.ONE];
|
||||
switch (sash) {
|
||||
case this.sashOne:
|
||||
return this.silosSize[Position.ONE];
|
||||
case this.sashTwo:
|
||||
return this.silosSize[Position.TWO] + this.silosSize[Position.ONE];
|
||||
case this.centeredEditorSashLeft:
|
||||
return this.centeredEditorPosition;
|
||||
case this.centeredEditorSashRight:
|
||||
return this.centeredEditorPosition + this.centeredEditorSize;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public getVerticalSashHeight(sash: Sash): number {
|
||||
@@ -2033,6 +2140,29 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
}
|
||||
});
|
||||
|
||||
// Layout centered Editor (only in vertical layout when one group is opened)
|
||||
const id = this.visibleEditors[Position.ONE] ? this.visibleEditors[Position.ONE].getId() : undefined;
|
||||
const doCentering = this.layoutVertically && this.stacks.groups.length === 1 && this.partService.isEditorLayoutCentered() && id !== PREFERENCES_EDITOR_ID && id !== TEXT_DIFF_EDITOR_ID;
|
||||
if (doCentering && !this.centeredEditorActive) {
|
||||
this.centeredEditorSashLeft.show();
|
||||
this.centeredEditorSashRight.show();
|
||||
|
||||
// no size set yet. Calculate a default value
|
||||
if (!this.centeredEditorPreferedSize) {
|
||||
this.resetCenteredEditor(false);
|
||||
}
|
||||
} else if (!doCentering && this.centeredEditorActive) {
|
||||
this.centeredEditorSashLeft.hide();
|
||||
this.centeredEditorSashRight.hide();
|
||||
}
|
||||
this.centeredEditorActive = doCentering;
|
||||
this.silos[Position.ONE].setClass('centered', doCentering);
|
||||
|
||||
if (this.centeredEditorActive) {
|
||||
this.centeredEditorSashLeft.layout();
|
||||
this.centeredEditorSashRight.layout();
|
||||
}
|
||||
|
||||
// Layout visible editors
|
||||
POSITIONS.forEach(position => {
|
||||
this.layoutEditor(position);
|
||||
@@ -2051,10 +2181,17 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
|
||||
private layoutEditor(position: Position): void {
|
||||
const editorSize = this.silosSize[position];
|
||||
if (editorSize && this.visibleEditors[position]) {
|
||||
const editor = this.visibleEditors[position];
|
||||
if (editorSize && editor) {
|
||||
let editorWidth = this.layoutVertically ? editorSize : this.dimension.width;
|
||||
let editorHeight = (this.layoutVertically ? this.dimension.height : this.silosSize[position]) - EditorGroupsControl.EDITOR_TITLE_HEIGHT;
|
||||
|
||||
let editorPosition = 0;
|
||||
if (this.centeredEditorActive) {
|
||||
editorWidth = this.centeredEditorSize;
|
||||
editorPosition = this.centeredEditorPosition;
|
||||
}
|
||||
|
||||
if (position !== Position.ONE) {
|
||||
if (this.layoutVertically) {
|
||||
editorWidth--; // accomodate for 1px left-border in containers TWO, THREE when laying out vertically
|
||||
@@ -2063,10 +2200,23 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
|
||||
}
|
||||
}
|
||||
|
||||
this.visibleEditors[position].layout(new Dimension(editorWidth, editorHeight));
|
||||
const editorContainer = editor.getContainer();
|
||||
editorContainer.style('margin-left', this.centeredEditorActive ? `${editorPosition}px` : null);
|
||||
editorContainer.style('width', this.centeredEditorActive ? `${editorWidth}px` : null);
|
||||
editorContainer.style('border-color', this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null);
|
||||
editor.layout(new Dimension(editorWidth, editorHeight));
|
||||
}
|
||||
}
|
||||
|
||||
private resetCenteredEditor(layout: boolean = true) {
|
||||
this.centeredEditorLeftMarginRatio = 0.5;
|
||||
this.centeredEditorPreferedSize = Math.floor(this.dimension.width * EditorGroupsControl.GOLDEN_RATIO);
|
||||
if (layout) {
|
||||
this.layoutContainers();
|
||||
}
|
||||
this.storageServise.remove(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
public getInstantiationService(position: Position): IInstantiationService {
|
||||
return this.getFromContainer(position, EditorGroupsControl.INSTANTIATION_SERVICE_KEY);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import { Position, POSITIONS, Direction, IEditor } from 'vs/platform/editor/comm
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IMessageService, IMessageWithAction, Severity } from 'vs/platform/message/common/message';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { EditorStacksModel, EditorGroup, EditorIdentifier, EditorCloseEvent } from 'vs/workbench/common/editor/editorStacksModel';
|
||||
@@ -41,12 +40,15 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { EDITOR_GROUP_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { createCSSRule, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import { createCSSRule } from 'vs/base/browser/dom';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { IEditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { ThrottledEmitter } from 'vs/base/common/async';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { INotificationService, Severity, INotificationActions } from 'vs/platform/notification/common/notification';
|
||||
import { isErrorWithActions } from 'vs/base/common/errors';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
|
||||
@@ -76,6 +78,10 @@ interface IEditorReplacement extends EditorIdentifier {
|
||||
options?: EditorOptions;
|
||||
}
|
||||
|
||||
export type ICloseEditorsFilter = { except?: EditorInput, direction?: Direction, savedOnly?: boolean };
|
||||
export type ICloseEditorsByFilterArgs = { positionOne?: ICloseEditorsFilter, positionTwo?: ICloseEditorsFilter, positionThree?: ICloseEditorsFilter };
|
||||
export type ICloseEditorsArgs = { positionOne?: EditorInput[], positionTwo?: EditorInput[], positionThree?: EditorInput[] };
|
||||
|
||||
/**
|
||||
* The editor part is the container for editors in the workbench. Based on the editor input being opened, it asks the registered
|
||||
* editor for the given input to show the contents. The editor part supports up to 3 side-by-side editors.
|
||||
@@ -124,7 +130,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
constructor(
|
||||
id: string,
|
||||
restoreFromStorage: boolean,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IPartService private partService: IPartService,
|
||||
@@ -543,24 +549,24 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
});
|
||||
}
|
||||
|
||||
private doHandleSetInputError(e: Error | IMessageWithAction, group: EditorGroup, editor: BaseEditor, input: EditorInput, options: EditorOptions, monitor: ProgressMonitor): void {
|
||||
private doHandleSetInputError(error: Error, group: EditorGroup, editor: BaseEditor, input: EditorInput, options: EditorOptions, monitor: ProgressMonitor): void {
|
||||
const position = this.stacks.positionOfGroup(group);
|
||||
|
||||
// Stop loading promise if any
|
||||
monitor.cancel();
|
||||
|
||||
// Report error only if this was not us restoring previous error state
|
||||
if (this.partService.isCreated() && !errors.isPromiseCanceledError(e)) {
|
||||
const errorMessage = nls.localize('editorOpenError', "Unable to open '{0}': {1}.", input.getName(), toErrorMessage(e));
|
||||
|
||||
let error: any;
|
||||
if (e && (<IMessageWithAction>e).actions && (<IMessageWithAction>e).actions.length) {
|
||||
error = errors.create(errorMessage, { actions: (<IMessageWithAction>e).actions }); // Support error actions from thrower
|
||||
} else {
|
||||
error = errorMessage;
|
||||
if (this.partService.isCreated() && !errors.isPromiseCanceledError(error)) {
|
||||
const actions: INotificationActions = { primary: [] };
|
||||
if (isErrorWithActions(error)) {
|
||||
actions.primary = error.actions;
|
||||
}
|
||||
|
||||
this.messageService.show(Severity.Error, types.isString(error) ? new Error(error) : error);
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: nls.localize('editorOpenError', "Unable to open '{0}': {1}.", input.getName(), toErrorMessage(error)),
|
||||
actions
|
||||
});
|
||||
}
|
||||
|
||||
this.editorGroupsControl.updateProgress(position, ProgressState.DONE);
|
||||
@@ -691,65 +697,199 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
this.textCompareEditorVisible.set(this.visibleEditors.some(e => e && e.isVisible() && e.getId() === TEXT_DIFF_EDITOR_ID));
|
||||
}
|
||||
|
||||
public closeAllEditors(except?: Position): TPromise<void> {
|
||||
public closeEditors(positions?: Position[]): TPromise<void>;
|
||||
public closeEditors(position: Position, filter?: ICloseEditorsFilter): TPromise<void>;
|
||||
public closeEditors(position: Position, editors: EditorInput[]): TPromise<void>;
|
||||
public closeEditors(editors: ICloseEditorsByFilterArgs): TPromise<void>;
|
||||
public closeEditors(editors: ICloseEditorsArgs): TPromise<void>;
|
||||
public closeEditors(positionsOrEditors?: Position[] | Position | ICloseEditorsByFilterArgs | ICloseEditorsArgs, filterOrEditors?: ICloseEditorsFilter | EditorInput[]): TPromise<void> {
|
||||
|
||||
// First check for specific position to close
|
||||
if (typeof positionsOrEditors === 'number') {
|
||||
return this.doCloseEditorsAtPosition(positionsOrEditors, filterOrEditors);
|
||||
}
|
||||
|
||||
// Then check for array of positions to close
|
||||
if (Array.isArray(positionsOrEditors) || isUndefinedOrNull(positionsOrEditors)) {
|
||||
return this.doCloseAllEditorsAtPositions(positionsOrEditors as Position[]);
|
||||
}
|
||||
|
||||
// Finally, close specific editors at multiple positions
|
||||
return this.doCloseEditorsAtPositions(positionsOrEditors);
|
||||
}
|
||||
|
||||
private doCloseEditorsAtPositions(editors: ICloseEditorsByFilterArgs | ICloseEditorsArgs): TPromise<void> {
|
||||
|
||||
// Extract editors to close for veto
|
||||
const editorsToClose: EditorIdentifier[] = [];
|
||||
let groupsWithEditorsToClose = 0;
|
||||
POSITIONS.forEach(position => {
|
||||
const details = (position === Position.ONE) ? editors.positionOne : (position === Position.TWO) ? editors.positionTwo : editors.positionThree;
|
||||
if (details && this.stacks.groupAt(position)) {
|
||||
groupsWithEditorsToClose++;
|
||||
editorsToClose.push(...this.extractCloseEditorDetails(position, details).editorsToClose);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for dirty and veto
|
||||
const ignoreDirtyIfOpenedInOtherGroup = (groupsWithEditorsToClose === 1);
|
||||
return this.handleDirty(editorsToClose, ignoreDirtyIfOpenedInOtherGroup).then(veto => {
|
||||
if (veto) {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// Close by positions starting from last to first to prevent issues when
|
||||
// editor groups close and thus move other editors around that are still open.
|
||||
[Position.THREE, Position.TWO, Position.ONE].forEach(position => {
|
||||
const details = (position === Position.ONE) ? editors.positionOne : (position === Position.TWO) ? editors.positionTwo : editors.positionThree;
|
||||
if (details && this.stacks.groupAt(position)) {
|
||||
const { group, editorsToClose, filter } = this.extractCloseEditorDetails(position, details);
|
||||
|
||||
// Close with filter
|
||||
if (filter) {
|
||||
this.doCloseEditorsWithFilter(group, filter);
|
||||
}
|
||||
|
||||
// Close without filter
|
||||
else {
|
||||
this.doCloseEditors(group, editorsToClose.map(e => e.editor));
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private doCloseAllEditorsAtPositions(positions?: Position[]): TPromise<void> {
|
||||
let groups = this.stacks.groups.reverse(); // start from the end to prevent layout to happen through rochade
|
||||
|
||||
// Remove position to exclude if we have any
|
||||
if (typeof except === 'number') {
|
||||
groups = groups.filter(group => this.stacks.positionOfGroup(group) !== except);
|
||||
// Remove positions that are not being asked for if provided
|
||||
if (Array.isArray(positions)) {
|
||||
groups = groups.filter(group => positions.indexOf(this.stacks.positionOfGroup(group)) >= 0);
|
||||
}
|
||||
|
||||
// Check for dirty and veto
|
||||
return this.handleDirty(arrays.flatten(groups.map(group => group.getEditors(true /* in MRU order */).map(editor => { return { group, editor }; })))).then(veto => {
|
||||
const ignoreDirtyIfOpenedInOtherGroup = (groups.length === 1);
|
||||
return this.handleDirty(arrays.flatten(groups.map(group => group.getEditors(true /* in MRU order */).map(editor => ({ group, editor })))), ignoreDirtyIfOpenedInOtherGroup).then(veto => {
|
||||
if (veto) {
|
||||
return;
|
||||
}
|
||||
|
||||
groups.forEach(group => this.doCloseEditors(group));
|
||||
groups.forEach(group => this.doCloseAllEditorsInGroup(group));
|
||||
});
|
||||
}
|
||||
|
||||
public closeEditors(position: Position, filter: { except?: EditorInput, direction?: Direction, unmodifiedOnly?: boolean } = Object.create(null)): TPromise<void> {
|
||||
private doCloseAllEditorsInGroup(group: EditorGroup): void {
|
||||
|
||||
// Update stacks model: remove all non active editors first to prevent opening the next editor in group
|
||||
group.closeEditors(group.activeEditor);
|
||||
|
||||
// Now close active editor in group which will close the group
|
||||
this.doCloseActiveEditor(group);
|
||||
}
|
||||
|
||||
private doCloseEditorsAtPosition(position: Position, filterOrEditors?: ICloseEditorsFilter | EditorInput[]): TPromise<void> {
|
||||
const closeEditorsDetails = this.extractCloseEditorDetails(position, filterOrEditors);
|
||||
if (!closeEditorsDetails) {
|
||||
return TPromise.wrap(void 0);
|
||||
}
|
||||
|
||||
const { group, editorsToClose, filter } = closeEditorsDetails;
|
||||
|
||||
// Check for dirty and veto
|
||||
return this.handleDirty(editorsToClose, true /* ignore if opened in other group */).then(veto => {
|
||||
if (veto) {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// Close with filter
|
||||
if (filter) {
|
||||
this.doCloseEditorsWithFilter(group, filter);
|
||||
}
|
||||
|
||||
// Close without filter
|
||||
else {
|
||||
this.doCloseEditors(group, editorsToClose.map(e => e.editor));
|
||||
}
|
||||
|
||||
return void 0;
|
||||
});
|
||||
}
|
||||
|
||||
private extractCloseEditorDetails(position: Position, filterOrEditors?: ICloseEditorsFilter | EditorInput[]): { group: EditorGroup, editorsToClose: EditorIdentifier[], filter?: ICloseEditorsFilter } {
|
||||
const group = this.stacks.groupAt(position);
|
||||
if (!group) {
|
||||
return TPromise.wrap<void>(null);
|
||||
return void 0;
|
||||
}
|
||||
|
||||
let editorsToClose = group.getEditors(true /* in MRU order */);
|
||||
let editorsToClose: EditorInput[];
|
||||
let filter: ICloseEditorsFilter;
|
||||
|
||||
// Filter: unmodified only
|
||||
if (filter.unmodifiedOnly) {
|
||||
editorsToClose = editorsToClose.filter(e => !e.isDirty());
|
||||
// Close: Specific Editors
|
||||
if (Array.isArray(filterOrEditors)) {
|
||||
editorsToClose = filterOrEditors;
|
||||
}
|
||||
|
||||
// Filter: direction (left / right)
|
||||
if (!types.isUndefinedOrNull(filter.direction)) {
|
||||
editorsToClose = (filter.direction === Direction.LEFT) ? editorsToClose.slice(0, group.indexOf(filter.except)) : editorsToClose.slice(group.indexOf(filter.except) + 1);
|
||||
}
|
||||
|
||||
// Filter: except
|
||||
// Close: By Filter or all
|
||||
else {
|
||||
editorsToClose = editorsToClose.filter(e => !filter.except || !e.matches(filter.except));
|
||||
}
|
||||
editorsToClose = group.getEditors(true /* in MRU order */);
|
||||
filter = filterOrEditors || Object.create(null);
|
||||
|
||||
// Check for dirty and veto
|
||||
return this.handleDirty(editorsToClose.map(editor => { return { group, editor }; }), true /* ignore if opened in other group */).then(veto => {
|
||||
if (veto) {
|
||||
return;
|
||||
// Filter: saved only
|
||||
if (filter.savedOnly) {
|
||||
editorsToClose = editorsToClose.filter(e => !e.isDirty());
|
||||
}
|
||||
|
||||
this.doCloseEditors(group, filter);
|
||||
});
|
||||
// Filter: direction (left / right)
|
||||
else if (!types.isUndefinedOrNull(filter.direction)) {
|
||||
editorsToClose = (filter.direction === Direction.LEFT) ? editorsToClose.slice(0, group.indexOf(filter.except)) : editorsToClose.slice(group.indexOf(filter.except) + 1);
|
||||
}
|
||||
|
||||
// Filter: except
|
||||
else if (filter.except) {
|
||||
editorsToClose = editorsToClose.filter(e => !e.matches(filter.except));
|
||||
}
|
||||
}
|
||||
|
||||
return { group, editorsToClose: editorsToClose.map(editor => ({ editor, group })), filter };
|
||||
}
|
||||
|
||||
private doCloseEditors(group: EditorGroup, filter: { except?: EditorInput, direction?: Direction, unmodifiedOnly?: boolean } = Object.create(null)): void {
|
||||
private doCloseEditors(group: EditorGroup, editors: EditorInput[]): void {
|
||||
|
||||
// Close all editors in group
|
||||
if (editors.length === group.count) {
|
||||
this.doCloseAllEditorsInGroup(group);
|
||||
}
|
||||
|
||||
// Close specific editors in group
|
||||
else {
|
||||
|
||||
// Editors to close are not active, so we can just close them
|
||||
if (!editors.some(editor => group.activeEditor.matches(editor))) {
|
||||
editors.forEach(editor => this.doCloseInactiveEditor(group, editor));
|
||||
}
|
||||
|
||||
// Active editor is also a candidate to close, thus we make the first
|
||||
// non-candidate editor active and then close the other ones
|
||||
else {
|
||||
const firstEditorToKeep = group.getEditors(true).filter(editorInGroup => !editors.some(editor => editor.matches(editorInGroup)))[0];
|
||||
|
||||
this.openEditor(firstEditorToKeep, null, this.stacks.positionOfGroup(group)).done(() => {
|
||||
editors.forEach(editor => this.doCloseInactiveEditor(group, editor));
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private doCloseEditorsWithFilter(group: EditorGroup, filter: { except?: EditorInput, direction?: Direction, savedOnly?: boolean }): void {
|
||||
|
||||
// Close all editors if there is no editor to except and
|
||||
// we either are not only closing unmodified editors or
|
||||
// we either are not only closing saved editors or
|
||||
// there are no dirty editors.
|
||||
let closeAllEditors = false;
|
||||
if (!filter.except) {
|
||||
if (!filter.unmodifiedOnly) {
|
||||
if (!filter.savedOnly) {
|
||||
closeAllEditors = true;
|
||||
} else {
|
||||
closeAllEditors = !group.getEditors().some(e => e.isDirty());
|
||||
@@ -758,18 +898,13 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
|
||||
// Close all editors in group
|
||||
if (closeAllEditors) {
|
||||
|
||||
// Update stacks model: remove all non active editors first to prevent opening the next editor in group
|
||||
group.closeEditors(group.activeEditor);
|
||||
|
||||
// Now close active editor in group which will close the group
|
||||
this.doCloseActiveEditor(group);
|
||||
this.doCloseAllEditorsInGroup(group);
|
||||
}
|
||||
|
||||
// Close unmodified editors in group
|
||||
else if (filter.unmodifiedOnly) {
|
||||
// Close saved editors in group
|
||||
else if (filter.savedOnly) {
|
||||
|
||||
// We can just close all unmodified editors around the currently active dirty one
|
||||
// We can just close all saved editors around the currently active dirty one
|
||||
if (group.activeEditor.isDirty()) {
|
||||
group.getEditors().filter(editor => !editor.isDirty() && !editor.matches(filter.except)).forEach(editor => this.doCloseInactiveEditor(group, editor));
|
||||
}
|
||||
@@ -777,10 +912,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
// Active editor is also a candidate to close, thus we make the first dirty editor
|
||||
// active and then close the other ones
|
||||
else {
|
||||
const firstDirtyEditor = group.getEditors().filter(editor => editor.isDirty())[0];
|
||||
const firstDirtyEditor = group.getEditors(true).filter(editor => editor.isDirty())[0];
|
||||
|
||||
this.openEditor(firstDirtyEditor, null, this.stacks.positionOfGroup(group)).done(() => {
|
||||
this.doCloseEditors(group, filter);
|
||||
this.doCloseEditorsWithFilter(group, filter);
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
@@ -801,7 +936,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
// being the expected one, otherwise we end up in an endless loop trying to open the
|
||||
// editor
|
||||
if (filter.except.matches(group.activeEditor)) {
|
||||
this.doCloseEditors(group, filter);
|
||||
this.doCloseEditorsWithFilter(group, filter);
|
||||
}
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
@@ -830,14 +965,30 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
|
||||
// Switch to editor that we want to handle
|
||||
return this.openEditor(identifier.editor, null, this.stacks.positionOfGroup(identifier.group)).then(() => {
|
||||
return this.ensureEditorOpenedBeforePrompt().then(() => {
|
||||
const res = editor.confirmSave();
|
||||
return editor.confirmSave().then(res => {
|
||||
|
||||
// It could be that the editor saved meanwhile, so we check again
|
||||
// to see if anything needs to happen before closing for good.
|
||||
// This can happen for example if autoSave: onFocusChange is configured
|
||||
// so that the save happens when the dialog opens.
|
||||
if (!editor.isDirty()) {
|
||||
return res === ConfirmResult.CANCEL ? true : false;
|
||||
}
|
||||
|
||||
// Otherwise, handle accordingly
|
||||
switch (res) {
|
||||
case ConfirmResult.SAVE:
|
||||
return editor.save().then(ok => !ok);
|
||||
|
||||
case ConfirmResult.DONT_SAVE:
|
||||
return editor.revert().then(ok => !ok);
|
||||
// first try a normal revert where the contents of the editor are restored
|
||||
return editor.revert().then(ok => !ok, error => {
|
||||
// if that fails, since we are about to close the editor, we accept that
|
||||
// the editor cannot be reverted and instead do a soft revert that just
|
||||
// enables us to close the editor. With this, a user can always close a
|
||||
// dirty editor even when reverting fails.
|
||||
return editor.revert({ soft: true }).then(ok => !ok);
|
||||
});
|
||||
|
||||
case ConfirmResult.CANCEL:
|
||||
return true; // veto
|
||||
@@ -846,27 +997,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
});
|
||||
}
|
||||
|
||||
private ensureEditorOpenedBeforePrompt(): TPromise<void> {
|
||||
|
||||
// Force title area update
|
||||
this.editorGroupsControl.updateTitleAreas(true /* refresh active group */);
|
||||
|
||||
// TODO@Ben our dialogs currently use the sync API, which means they block the JS
|
||||
// thread when showing. As such, any UI update will not happen unless we wait a little
|
||||
// bit. We wait for 2 request animation frames before showing the confirm. The first
|
||||
// frame is where the UI is updating and the second is good enough to bring up the dialog.
|
||||
// See also https://github.com/Microsoft/vscode/issues/39536
|
||||
return new TPromise<void>(c => {
|
||||
scheduleAtNextAnimationFrame(() => {
|
||||
// Here the UI is updating
|
||||
scheduleAtNextAnimationFrame(() => {
|
||||
// Here we can show a blocking dialog
|
||||
c(void 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private countEditors(editor: EditorInput): number {
|
||||
const editors = [editor];
|
||||
if (editor instanceof SideBySideEditorInput) {
|
||||
@@ -1123,7 +1253,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
return res;
|
||||
}
|
||||
|
||||
public openEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[]): TPromise<IEditor[]> {
|
||||
public openEditors(editors: { input: EditorInput, position?: Position, options?: EditorOptions }[]): TPromise<IEditor[]>;
|
||||
public openEditors(editors: { input: EditorInput, options?: EditorOptions }[], sideBySide?: boolean): TPromise<IEditor[]>;
|
||||
public openEditors(editors: { input: EditorInput, position?: Position, options?: EditorOptions }[], sideBySide?: boolean): TPromise<IEditor[]> {
|
||||
if (!editors.length) {
|
||||
return TPromise.as<IEditor[]>([]);
|
||||
}
|
||||
@@ -1135,7 +1267,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
|
||||
const ratio = this.editorGroupsControl.getRatio();
|
||||
|
||||
return this.doOpenEditors(editors, activePosition, ratio);
|
||||
return this.doOpenEditors(editors, activePosition, ratio, sideBySide);
|
||||
}
|
||||
|
||||
public hasEditorsToRestore(): boolean {
|
||||
@@ -1166,7 +1298,15 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
return this._onEditorsChanged.throttle(this.doOpenEditors(editors, activePosition, editorState && editorState.ratio));
|
||||
}
|
||||
|
||||
private doOpenEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[], activePosition?: number, ratio?: number[]): TPromise<IEditor[]> {
|
||||
private doOpenEditors(editors: { input: EditorInput, position?: Position, options?: EditorOptions }[], activePosition?: number, ratio?: number[], sideBySide?: boolean): TPromise<IEditor[]> {
|
||||
|
||||
// Find position if not provided already from calling side
|
||||
editors.forEach(editor => {
|
||||
if (typeof editor.position !== 'number') {
|
||||
editor.position = this.findPosition(editor.input, editor.options, sideBySide);
|
||||
}
|
||||
});
|
||||
|
||||
const positionOneEditors = editors.filter(e => e.position === Position.ONE);
|
||||
const positionTwoEditors = editors.filter(e => e.position === Position.TWO);
|
||||
const positionThreeEditors = editors.filter(e => e.position === Position.THREE);
|
||||
@@ -1422,8 +1562,14 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
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 (options && options.revealIfVisible) {
|
||||
if (!skipReveal && options && options.revealIfVisible) {
|
||||
const group = this.stacks.findGroup(input, true);
|
||||
if (group) {
|
||||
return this.stacks.positionOfGroup(group);
|
||||
@@ -1431,8 +1577,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
||||
}
|
||||
|
||||
// Respect option to reveal an editor if it is open (not necessarily visible)
|
||||
const skipRevealIfOpen = (options && options.index) || arg1 === true /* open to side */ || typeof arg1 === 'number' /* open specific group */;
|
||||
if (!skipRevealIfOpen && (this.revealIfOpen || (options && options.revealIfOpened))) {
|
||||
if (!skipReveal && (this.revealIfOpen || (options && options.revealIfOpened))) {
|
||||
const group = this.stacks.findGroup(input);
|
||||
if (group) {
|
||||
return this.stacks.positionOfGroup(group);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import nls = require('vs/nls');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import { IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
@@ -39,7 +39,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
this.stacks = editorGroupService.getStacksModel();
|
||||
}
|
||||
|
||||
public getLabelOptions(): IIconLabelOptions {
|
||||
public getLabelOptions(): IIconLabelValueOptions {
|
||||
return {
|
||||
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()),
|
||||
italic: this._group.isPreview(this.editor)
|
||||
|
||||
@@ -23,7 +23,8 @@ import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorIn
|
||||
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IEditorAction, EndOfLineSequence, IModel } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorAction } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model';
|
||||
import { IModelLanguageChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/linesOperations';
|
||||
import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/indentation';
|
||||
@@ -49,15 +50,17 @@ import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/con
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { attachStylerCallback, attachButtonStyler } from 'vs/platform/theme/common/styler';
|
||||
import { widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
// TODO@Sandeep layer breaker
|
||||
// tslint:disable-next-line:import-patterns
|
||||
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { QueryEditorService } from 'sql/parts/query/services/queryEditorService';
|
||||
@@ -768,7 +771,7 @@ function isWritableBaseEditor(e: IBaseEditor): boolean {
|
||||
|
||||
export class ShowLanguageExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.action.showLanguageExtensions';
|
||||
static readonly ID = 'workbench.action.showLanguageExtensions';
|
||||
|
||||
constructor(
|
||||
private fileExtension: string,
|
||||
@@ -816,7 +819,7 @@ export class ChangeModeAction extends Action {
|
||||
const resource = toResource(activeEditor.input, { supportSideBySide: true });
|
||||
|
||||
let hasLanguageSupport = !!resource;
|
||||
if (resource.scheme === 'untitled' && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
|
||||
if (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
|
||||
hasLanguageSupport = false; // no configuration for untitled resources (e.g. "Untitled-1")
|
||||
}
|
||||
|
||||
@@ -888,7 +891,7 @@ export class ChangeModeAction extends Action {
|
||||
picks.unshift(autoDetectMode);
|
||||
}
|
||||
|
||||
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode") }).then(pick => {
|
||||
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }).then(pick => {
|
||||
if (!pick) {
|
||||
return;
|
||||
}
|
||||
@@ -913,7 +916,7 @@ export class ChangeModeAction extends Action {
|
||||
// Change mode for active editor
|
||||
activeEditor = this.editorService.getActiveEditor();
|
||||
const codeOrDiffEditor = getCodeOrDiffEditor(activeEditor);
|
||||
const models: IModel[] = [];
|
||||
const models: ITextModel[] = [];
|
||||
if (codeOrDiffEditor.codeEditor) {
|
||||
const codeEditorModel = codeOrDiffEditor.codeEditor.getModel();
|
||||
if (codeEditorModel) {
|
||||
@@ -1285,19 +1288,24 @@ class ScreenReaderDetectedExplanation {
|
||||
const question = $('p.question', {}, nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate VS Code?"));
|
||||
domNode.appendChild(question);
|
||||
|
||||
const yesBtn = $('div.button', {}, nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"));
|
||||
this._toDispose.push(addDisposableListener(yesBtn, 'click', () => {
|
||||
const buttonContainer = $('div.buttons');
|
||||
domNode.appendChild(buttonContainer);
|
||||
|
||||
const yesBtn = new Button(buttonContainer);
|
||||
yesBtn.label = nls.localize('screenReaderDetectedExplanation.answerYes', "Yes");
|
||||
this._toDispose.push(attachButtonStyler(yesBtn, this.themeService));
|
||||
this._toDispose.push(yesBtn.onDidClick(e => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
|
||||
this.contextViewService.hideContextView();
|
||||
}));
|
||||
domNode.appendChild(yesBtn);
|
||||
|
||||
const noBtn = $('div.button', {}, nls.localize('screenReaderDetectedExplanation.answerNo', "No"));
|
||||
this._toDispose.push(addDisposableListener(noBtn, 'click', () => {
|
||||
const noBtn = new Button(buttonContainer);
|
||||
noBtn.label = nls.localize('screenReaderDetectedExplanation.answerNo', "No");
|
||||
this._toDispose.push(attachButtonStyler(noBtn, this.themeService));
|
||||
this._toDispose.push(noBtn.onDidClick(e => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
|
||||
this.contextViewService.hideContextView();
|
||||
}));
|
||||
domNode.appendChild(noBtn);
|
||||
|
||||
const clear = $('div');
|
||||
clear.style.clear = 'both';
|
||||
@@ -1320,7 +1328,7 @@ class ScreenReaderDetectedExplanation {
|
||||
this._toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground }, colors => {
|
||||
domNode.style.backgroundColor = colors.editorWidgetBackground;
|
||||
if (colors.widgetShadow) {
|
||||
domNode.style.boxShadow = `0 2px 8px ${colors.widgetShadow}`;
|
||||
domNode.style.boxShadow = `0 5px 8px ${colors.widgetShadow}`;
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>Paragraph_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,1V4H12V16H6V9.973A4.5,4.5,0,0,1,6.5,1Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M12,2V3H11V15H10V3H8V15H7V8.95A3.588,3.588,0,0,1,6.5,9a3.5,3.5,0,0,1,0-7Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 552 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>Paragraph_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,1V4H12V16H6V9.973A4.5,4.5,0,0,1,6.5,1Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M12,2V3H11V15H10V3H8V15H7V8.95A3.588,3.588,0,0,1,6.5,9a3.5,3.5,0,0,1,0-7Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 552 B |
@@ -7,6 +7,11 @@
|
||||
display: none; /* hide sashes while dragging editors around */
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor .one-editor-silo.centered .editor-container {
|
||||
border-style: none solid;
|
||||
border-width: 0px 1px;
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-move-overlay,
|
||||
#monaco-workbench-editor-drop-overlay {
|
||||
position: absolute;
|
||||
@@ -84,4 +89,4 @@
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo > .container > .editor-container {
|
||||
height: calc(100% - 35px); /* Editor is below editor title */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: .5em 0 0;
|
||||
padding: .5em;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
@@ -56,20 +55,18 @@
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation p.question {
|
||||
font-size: 1.4em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation .button {
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background-color: #007ACC;
|
||||
.monaco-shell .screen-reader-detected-explanation .buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation .buttons a {
|
||||
font-size: 13px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
border: 4px solid #007ACC;
|
||||
border-radius: 4px;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
max-width: fit-content;
|
||||
}
|
||||
|
||||
.monaco-shell.vs .screen-reader-detected-explanation .cancel {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-resource-viewer:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer {
|
||||
padding: 5px 0 0 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer.image {
|
||||
padding: 0;
|
||||
background-position: 0 0, 8px 8px;
|
||||
background-size: 16px 16px;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vs .monaco-resource-viewer.image {
|
||||
background-image:
|
||||
linear-gradient(45deg, rgb(230, 230, 230) 25%, transparent 25%, transparent 75%, rgb(230, 230, 230) 75%, rgb(230, 230, 230)),
|
||||
linear-gradient(45deg, rgb(230, 230, 230) 25%, transparent 25%, transparent 75%, rgb(230, 230, 230) 75%, rgb(230, 230, 230));
|
||||
}
|
||||
|
||||
.vs-dark .monaco-resource-viewer.image {
|
||||
background-image:
|
||||
linear-gradient(45deg, rgb(20, 20, 20) 25%, transparent 25%, transparent 75%, rgb(20, 20, 20) 75%, rgb(20, 20, 20)),
|
||||
linear-gradient(45deg, rgb(20, 20, 20) 25%, transparent 25%, transparent 75%, rgb(20, 20, 20) 75%, rgb(20, 20, 20));
|
||||
}
|
||||
|
||||
.monaco-resource-viewer img.pixelated {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer img.scale-to-fit {
|
||||
max-width: calc(100% - 20px);
|
||||
max-height: calc(100% - 20px);
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer img {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer.zoom-in {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer.zoom-out {
|
||||
cursor: zoom-out;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer .open-external,
|
||||
.monaco-resource-viewer .open-external:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -87,11 +87,33 @@
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink .tab-label {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .tab-label::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 5px;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
overflow: visible; /* fixes https://github.com/Microsoft/vscode/issues/20182 */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before {
|
||||
height: 16px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
@@ -19,4 +19,22 @@
|
||||
.vs-dark .monaco-workbench .textdiff-editor-action.previous,
|
||||
.hc-black .monaco-workbench .textdiff-editor-action.previous {
|
||||
background: url('previous-diff-inverse.svg') center center no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.vs .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace {
|
||||
opacity: 1;
|
||||
background: url('Paragraph_16x_nohalo.svg') center center no-repeat;
|
||||
}
|
||||
.vs-dark .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace,
|
||||
.hc-black .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace {
|
||||
opacity: 1;
|
||||
background: url('Paragraph_16x_nohalo_inversep.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace.is-checked {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.vs-dark .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace.is-checked,
|
||||
.hc-black .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace.is-checked {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo.centered > .container > .title .title-label {
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a {
|
||||
text-decoration: none;
|
||||
@@ -104,4 +109,4 @@
|
||||
.vs-dark .monaco-workbench .show-group-editors-action,
|
||||
.hc-black .monaco-workbench .show-group-editors-action {
|
||||
background: url('stackview-inverse.svg') center center no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import 'vs/css!./media/notabstitle';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import { IEditorGroup, toResource } from 'vs/workbench/common/editor';
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import { ResourceLabel } from 'vs/workbench/browser/labels';
|
||||
@@ -19,12 +19,6 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
private titleContainer: HTMLElement;
|
||||
private editorLabel: ResourceLabel;
|
||||
|
||||
public setContext(group: IEditorGroup): void {
|
||||
super.setContext(group);
|
||||
|
||||
this.editorActionsToolbar.context = { group };
|
||||
}
|
||||
|
||||
public create(parent: HTMLElement): void {
|
||||
super.create(parent);
|
||||
|
||||
@@ -87,14 +81,14 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
|
||||
// Close editor on middle mouse click
|
||||
if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) {
|
||||
this.closeEditorAction.run({ group, editor: group.activeEditor }).done(null, errors.onUnexpectedError);
|
||||
this.closeEditorAction.run({ groupId: group.id, editorIndex: group.indexOf(group.activeEditor) }).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
// Focus editor group unless:
|
||||
// - click on toolbar: should trigger actions within
|
||||
// - 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.target || e.srcElement) as HTMLElement, this.editorActionsToolbar.getContainer().getHTMLElement())) {
|
||||
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().getHTMLElement())) {
|
||||
this.editorGroupService.focusGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { TrackedRangeStickiness, IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
|
||||
|
||||
export interface IRangeHighlightDecoration {
|
||||
resource: URI;
|
||||
@@ -48,7 +48,7 @@ export class RangeHighlightDecorations implements IDisposable {
|
||||
|
||||
private doHighlightRange(editor: ICodeEditor, selectionRange: IRangeHighlightDecoration) {
|
||||
this.removeHighlightRange();
|
||||
editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => {
|
||||
editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {
|
||||
this.rangeHighlightDecorationId = changeAccessor.addDecoration(selectionRange.range, this.createRangeHighlightDecoration(selectionRange.isWholeLine));
|
||||
});
|
||||
this.setEditor(editor);
|
||||
@@ -93,13 +93,13 @@ export class RangeHighlightDecorations implements IDisposable {
|
||||
}
|
||||
|
||||
private static readonly _WHOLE_LINE_RANGE_HIGHLIGHT = ModelDecorationOptions.register({
|
||||
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: 'rangeHighlight',
|
||||
isWholeLine: true
|
||||
});
|
||||
|
||||
private static readonly _RANGE_HIGHLIGHT = ModelDecorationOptions.register({
|
||||
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: 'rangeHighlight'
|
||||
});
|
||||
|
||||
|
||||
590
src/vs/workbench/browser/parts/editor/resourceViewer.ts
Normal file
590
src/vs/workbench/browser/parts/editor/resourceViewer.ts
Normal file
@@ -0,0 +1,590 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/resourceviewer';
|
||||
import nls = require('vs/nls');
|
||||
import mimes = require('vs/base/common/mime');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { Builder, $, Dimension } from 'vs/base/browser/builder';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { clamp } from 'vs/base/common/numbers';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IStatusbarItem, StatusbarItemDescriptor, IStatusbarRegistry, Extensions, StatusbarAlignment } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
interface MapExtToMediaMimes {
|
||||
[index: string]: string;
|
||||
}
|
||||
|
||||
// Known media mimes that we can handle
|
||||
const mapExtToMediaMimes: MapExtToMediaMimes = {
|
||||
'.bmp': 'image/bmp',
|
||||
'.gif': 'image/gif',
|
||||
'.jpg': 'image/jpg',
|
||||
'.jpeg': 'image/jpg',
|
||||
'.jpe': 'image/jpg',
|
||||
'.png': 'image/png',
|
||||
'.tiff': 'image/tiff',
|
||||
'.tif': 'image/tiff',
|
||||
'.ico': 'image/x-icon',
|
||||
'.tga': 'image/x-tga',
|
||||
'.psd': 'image/vnd.adobe.photoshop',
|
||||
'.webp': 'image/webp',
|
||||
'.mid': 'audio/midi',
|
||||
'.midi': 'audio/midi',
|
||||
'.mp4a': 'audio/mp4',
|
||||
'.mpga': 'audio/mpeg',
|
||||
'.mp2': 'audio/mpeg',
|
||||
'.mp2a': 'audio/mpeg',
|
||||
'.mp3': 'audio/mpeg',
|
||||
'.m2a': 'audio/mpeg',
|
||||
'.m3a': 'audio/mpeg',
|
||||
'.oga': 'audio/ogg',
|
||||
'.ogg': 'audio/ogg',
|
||||
'.spx': 'audio/ogg',
|
||||
'.aac': 'audio/x-aac',
|
||||
'.wav': 'audio/x-wav',
|
||||
'.wma': 'audio/x-ms-wma',
|
||||
'.mp4': 'video/mp4',
|
||||
'.mp4v': 'video/mp4',
|
||||
'.mpg4': 'video/mp4',
|
||||
'.mpeg': 'video/mpeg',
|
||||
'.mpg': 'video/mpeg',
|
||||
'.mpe': 'video/mpeg',
|
||||
'.m1v': 'video/mpeg',
|
||||
'.m2v': 'video/mpeg',
|
||||
'.ogv': 'video/ogg',
|
||||
'.qt': 'video/quicktime',
|
||||
'.mov': 'video/quicktime',
|
||||
'.webm': 'video/webm',
|
||||
'.mkv': 'video/x-matroska',
|
||||
'.mk3d': 'video/x-matroska',
|
||||
'.mks': 'video/x-matroska',
|
||||
'.wmv': 'video/x-ms-wmv',
|
||||
'.flv': 'video/x-flv',
|
||||
'.avi': 'video/x-msvideo',
|
||||
'.movie': 'video/x-sgi-movie'
|
||||
};
|
||||
|
||||
export interface IResourceDescriptor {
|
||||
resource: URI;
|
||||
name: string;
|
||||
size: number;
|
||||
etag: string;
|
||||
mime: string;
|
||||
}
|
||||
|
||||
class BinarySize {
|
||||
public static readonly KB = 1024;
|
||||
public static readonly MB = BinarySize.KB * BinarySize.KB;
|
||||
public static readonly GB = BinarySize.MB * BinarySize.KB;
|
||||
public static readonly TB = BinarySize.GB * BinarySize.KB;
|
||||
|
||||
public static formatSize(size: number): string {
|
||||
if (size < BinarySize.KB) {
|
||||
return nls.localize('sizeB', "{0}B", size);
|
||||
}
|
||||
|
||||
if (size < BinarySize.MB) {
|
||||
return nls.localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < BinarySize.GB) {
|
||||
return nls.localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < BinarySize.TB) {
|
||||
return nls.localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2));
|
||||
}
|
||||
|
||||
return nls.localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
export interface ResourceViewerContext {
|
||||
layout(dimension: Dimension): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to actually render the given resource into the provided container. Will adjust scrollbar (if provided) automatically based on loading
|
||||
* progress of the binary resource.
|
||||
*/
|
||||
export class ResourceViewer {
|
||||
public static show(
|
||||
descriptor: IResourceDescriptor,
|
||||
container: Builder,
|
||||
scrollbar: DomScrollableElement,
|
||||
openExternal: (uri: URI) => void,
|
||||
metadataClb: (meta: string) => void
|
||||
): ResourceViewerContext {
|
||||
// Ensure CSS class
|
||||
$(container).setClass('monaco-resource-viewer');
|
||||
|
||||
if (ResourceViewer.isImageResource(descriptor)) {
|
||||
return ImageView.create(container, descriptor, scrollbar, openExternal, metadataClb);
|
||||
}
|
||||
|
||||
GenericBinaryFileView.create(container, metadataClb, descriptor, scrollbar);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static isImageResource(descriptor: IResourceDescriptor) {
|
||||
const mime = ResourceViewer.getMime(descriptor);
|
||||
return mime.indexOf('image/') >= 0;
|
||||
}
|
||||
|
||||
private static getMime(descriptor: IResourceDescriptor): string {
|
||||
let mime = descriptor.mime;
|
||||
if (!mime && descriptor.resource.scheme !== Schemas.data) {
|
||||
const ext = paths.extname(descriptor.resource.toString());
|
||||
if (ext) {
|
||||
mime = mapExtToMediaMimes[ext.toLowerCase()];
|
||||
}
|
||||
}
|
||||
return mime || mimes.MIME_BINARY;
|
||||
}
|
||||
}
|
||||
|
||||
class ImageView {
|
||||
private static readonly MAX_IMAGE_SIZE = BinarySize.MB; // showing images inline is memory intense, so we have a limit
|
||||
private static readonly BASE64_MARKER = 'base64,';
|
||||
|
||||
public static create(
|
||||
container: Builder,
|
||||
descriptor: IResourceDescriptor,
|
||||
scrollbar: DomScrollableElement,
|
||||
openExternal: (uri: URI) => void,
|
||||
metadataClb: (meta: string) => void
|
||||
): ResourceViewerContext | null {
|
||||
if (ImageView.shouldShowImageInline(descriptor)) {
|
||||
return InlineImageView.create(container, descriptor, scrollbar, metadataClb);
|
||||
}
|
||||
|
||||
LargeImageView.create(container, descriptor, openExternal);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static shouldShowImageInline(descriptor: IResourceDescriptor): boolean {
|
||||
let skipInlineImage: boolean;
|
||||
|
||||
// Data URI
|
||||
if (descriptor.resource.scheme === Schemas.data) {
|
||||
const base64MarkerIndex = descriptor.resource.path.indexOf(ImageView.BASE64_MARKER);
|
||||
const hasData = base64MarkerIndex >= 0 && descriptor.resource.path.substring(base64MarkerIndex + ImageView.BASE64_MARKER.length).length > 0;
|
||||
|
||||
skipInlineImage = !hasData || descriptor.size > ImageView.MAX_IMAGE_SIZE || descriptor.resource.path.length > ImageView.MAX_IMAGE_SIZE;
|
||||
}
|
||||
|
||||
// File URI
|
||||
else {
|
||||
skipInlineImage = typeof descriptor.size !== 'number' || descriptor.size > ImageView.MAX_IMAGE_SIZE;
|
||||
}
|
||||
|
||||
return !skipInlineImage;
|
||||
}
|
||||
}
|
||||
|
||||
class LargeImageView {
|
||||
public static create(
|
||||
container: Builder,
|
||||
descriptor: IResourceDescriptor,
|
||||
openExternal: (uri: URI) => void
|
||||
) {
|
||||
const imageContainer = $(container)
|
||||
.empty()
|
||||
.p({
|
||||
text: nls.localize('largeImageError', "The file size of the image is too large (>1MB) to display in the editor. ")
|
||||
});
|
||||
|
||||
if (descriptor.resource.scheme !== Schemas.data) {
|
||||
imageContainer.append($('a', {
|
||||
role: 'button',
|
||||
class: 'open-external',
|
||||
text: nls.localize('resourceOpenExternalButton', "Open image using external program?")
|
||||
}).on(DOM.EventType.CLICK, (e) => {
|
||||
openExternal(descriptor.resource);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GenericBinaryFileView {
|
||||
public static create(
|
||||
container: Builder,
|
||||
metadataClb: (meta: string) => void,
|
||||
descriptor: IResourceDescriptor,
|
||||
scrollbar: DomScrollableElement
|
||||
) {
|
||||
$(container)
|
||||
.empty()
|
||||
.span({
|
||||
text: nls.localize('nativeBinaryError', "The file will not be displayed in the editor because it is either binary, very large or uses an unsupported text encoding.")
|
||||
});
|
||||
if (metadataClb) {
|
||||
metadataClb(BinarySize.formatSize(descriptor.size));
|
||||
}
|
||||
scrollbar.scanDomNode();
|
||||
}
|
||||
}
|
||||
|
||||
type Scale = number | 'fit';
|
||||
|
||||
class ZoomStatusbarItem extends Themable implements IStatusbarItem {
|
||||
showTimeout: number;
|
||||
public static instance: ZoomStatusbarItem;
|
||||
|
||||
private statusBarItem: HTMLElement;
|
||||
|
||||
private onSelectScale?: (scale: Scale) => void;
|
||||
|
||||
constructor(
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
super(themeService);
|
||||
ZoomStatusbarItem.instance = this;
|
||||
this.toUnbind.push(editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
|
||||
}
|
||||
|
||||
private onEditorsChanged(): void {
|
||||
this.hide();
|
||||
this.onSelectScale = undefined;
|
||||
}
|
||||
|
||||
public show(scale: Scale, onSelectScale: (scale: number) => void) {
|
||||
clearTimeout(this.showTimeout);
|
||||
this.showTimeout = setTimeout(() => {
|
||||
this.onSelectScale = onSelectScale;
|
||||
this.statusBarItem.style.display = 'block';
|
||||
this.updateLabel(scale);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public hide() {
|
||||
this.statusBarItem.style.display = 'none';
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): IDisposable {
|
||||
if (!this.statusBarItem && container) {
|
||||
this.statusBarItem = $(container).a()
|
||||
.addClass('.zoom-statusbar-item')
|
||||
.on('click', () => {
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => container,
|
||||
getActions: () => TPromise.as(this.zoomActions)
|
||||
});
|
||||
})
|
||||
.getHTMLElement();
|
||||
this.statusBarItem.style.display = 'none';
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private updateLabel(scale: Scale) {
|
||||
this.statusBarItem.textContent = ZoomStatusbarItem.zoomLabel(scale);
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get zoomActions(): Action[] {
|
||||
const scales: Scale[] = [10, 5, 2, 1, 0.5, 0.2, 'fit'];
|
||||
return scales.map(scale =>
|
||||
new Action('zoom.' + scale, ZoomStatusbarItem.zoomLabel(scale), undefined, undefined, () => {
|
||||
if (this.onSelectScale) {
|
||||
this.onSelectScale(scale);
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
private static zoomLabel(scale: Scale): string {
|
||||
return scale === 'fit'
|
||||
? nls.localize('zoom.action.fit.label', 'Whole Image')
|
||||
: `${Math.round(scale * 100)}%`;
|
||||
}
|
||||
}
|
||||
|
||||
Registry.as<IStatusbarRegistry>(Extensions.Statusbar).registerStatusbarItem(
|
||||
new StatusbarItemDescriptor(ZoomStatusbarItem, StatusbarAlignment.RIGHT, 101 /* to the left of editor status (100) */)
|
||||
);
|
||||
|
||||
interface ImageState {
|
||||
scale: Scale;
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
}
|
||||
|
||||
class InlineImageView {
|
||||
private static readonly SCALE_PINCH_FACTOR = 0.075;
|
||||
private static readonly MAX_SCALE = 20;
|
||||
private static readonly MIN_SCALE = 0.1;
|
||||
|
||||
private static readonly zoomLevels: Scale[] = [
|
||||
0.1,
|
||||
0.2,
|
||||
0.3,
|
||||
0.4,
|
||||
0.5,
|
||||
0.6,
|
||||
0.7,
|
||||
0.8,
|
||||
0.9,
|
||||
1,
|
||||
1.5,
|
||||
2,
|
||||
3,
|
||||
5,
|
||||
7,
|
||||
10,
|
||||
15,
|
||||
20
|
||||
];
|
||||
|
||||
/**
|
||||
* Enable image-rendering: pixelated for images scaled by more than this.
|
||||
*/
|
||||
private static readonly PIXELATION_THRESHOLD = 3;
|
||||
|
||||
/**
|
||||
* Chrome is caching images very aggressively and so we use the ETag information to find out if
|
||||
* we need to bypass the cache or not. We could always bypass the cache everytime we show the image
|
||||
* however that has very bad impact on memory consumption because each time the image gets shown,
|
||||
* memory grows (see also https://github.com/electron/electron/issues/6275)
|
||||
*/
|
||||
private static IMAGE_RESOURCE_ETAG_CACHE = new LRUCache<string, { etag: string, src: string }>(100);
|
||||
|
||||
/**
|
||||
* Store the scale and position of an image so it can be restored when changing editor tabs
|
||||
*/
|
||||
private static readonly imageStateCache = new LRUCache<string, ImageState>(100);
|
||||
|
||||
public static create(
|
||||
container: Builder,
|
||||
descriptor: IResourceDescriptor,
|
||||
scrollbar: DomScrollableElement,
|
||||
metadataClb: (meta: string) => void
|
||||
) {
|
||||
const context = {
|
||||
layout(dimension: Dimension) { }
|
||||
};
|
||||
|
||||
const cacheKey = descriptor.resource.toString();
|
||||
|
||||
let ctrlPressed = false;
|
||||
let altPressed = false;
|
||||
|
||||
const initialState: ImageState = InlineImageView.imageStateCache.get(cacheKey) || { scale: 'fit', offsetX: 0, offsetY: 0 };
|
||||
let scale = initialState.scale;
|
||||
let img: Builder | null = null;
|
||||
let imgElement: HTMLImageElement | null = null;
|
||||
|
||||
function updateScale(newScale: Scale) {
|
||||
if (!img || !imgElement.parentElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newScale === 'fit') {
|
||||
scale = 'fit';
|
||||
img.addClass('scale-to-fit');
|
||||
img.removeClass('pixelated');
|
||||
img.style('min-width', 'auto');
|
||||
img.style('width', 'auto');
|
||||
InlineImageView.imageStateCache.set(cacheKey, null);
|
||||
} else {
|
||||
const oldWidth = imgElement.width;
|
||||
const oldHeight = imgElement.height;
|
||||
|
||||
scale = clamp(newScale, InlineImageView.MIN_SCALE, InlineImageView.MAX_SCALE);
|
||||
if (scale >= InlineImageView.PIXELATION_THRESHOLD) {
|
||||
img.addClass('pixelated');
|
||||
} else {
|
||||
img.removeClass('pixelated');
|
||||
}
|
||||
|
||||
const { scrollTop, scrollLeft } = imgElement.parentElement;
|
||||
const dx = (scrollLeft + imgElement.parentElement.clientWidth / 2) / imgElement.parentElement.scrollWidth;
|
||||
const dy = (scrollTop + imgElement.parentElement.clientHeight / 2) / imgElement.parentElement.scrollHeight;
|
||||
|
||||
img.removeClass('scale-to-fit');
|
||||
img.style('min-width', `${(imgElement.naturalWidth * scale)}px`);
|
||||
img.style('width', `${(imgElement.naturalWidth * scale)}px`);
|
||||
|
||||
const newWidth = imgElement.width;
|
||||
const scaleFactor = (newWidth - oldWidth) / oldWidth;
|
||||
|
||||
const newScrollLeft = ((oldWidth * scaleFactor * dx) + scrollLeft);
|
||||
const newScrollTop = ((oldHeight * scaleFactor * dy) + scrollTop);
|
||||
scrollbar.setScrollPosition({
|
||||
scrollLeft: newScrollLeft,
|
||||
scrollTop: newScrollTop,
|
||||
});
|
||||
|
||||
InlineImageView.imageStateCache.set(cacheKey, { scale: scale, offsetX: newScrollLeft, offsetY: newScrollTop });
|
||||
|
||||
}
|
||||
ZoomStatusbarItem.instance.show(scale, updateScale);
|
||||
scrollbar.scanDomNode();
|
||||
}
|
||||
|
||||
function firstZoom() {
|
||||
scale = imgElement.clientWidth / imgElement.naturalWidth;
|
||||
updateScale(scale);
|
||||
}
|
||||
|
||||
$(container)
|
||||
.on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent, c) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
ctrlPressed = e.ctrlKey;
|
||||
altPressed = e.altKey;
|
||||
|
||||
if (platform.isMacintosh ? altPressed : ctrlPressed) {
|
||||
c.removeClass('zoom-in').addClass('zoom-out');
|
||||
}
|
||||
})
|
||||
.on(DOM.EventType.KEY_UP, (e: KeyboardEvent, c) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctrlPressed = e.ctrlKey;
|
||||
altPressed = e.altKey;
|
||||
|
||||
if (!(platform.isMacintosh ? altPressed : ctrlPressed)) {
|
||||
c.removeClass('zoom-out').addClass('zoom-in');
|
||||
}
|
||||
})
|
||||
.on(DOM.EventType.CLICK, (e: MouseEvent) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// left click
|
||||
if (scale === 'fit') {
|
||||
firstZoom();
|
||||
}
|
||||
|
||||
if (!(platform.isMacintosh ? altPressed : ctrlPressed)) { // zoom in
|
||||
let i = 0;
|
||||
for (; i < InlineImageView.zoomLevels.length; ++i) {
|
||||
if (InlineImageView.zoomLevels[i] > scale) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateScale(InlineImageView.zoomLevels[i] || InlineImageView.MAX_SCALE);
|
||||
} else {
|
||||
let i = InlineImageView.zoomLevels.length - 1;
|
||||
for (; i >= 0; --i) {
|
||||
if (InlineImageView.zoomLevels[i] < scale) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateScale(InlineImageView.zoomLevels[i] || InlineImageView.MIN_SCALE);
|
||||
}
|
||||
})
|
||||
.on(DOM.EventType.WHEEL, (e: WheelEvent) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isScrollWhellKeyPressed = platform.isMacintosh ? altPressed : ctrlPressed;
|
||||
if (!isScrollWhellKeyPressed && !e.ctrlKey) { // pinching is reported as scroll wheel + ctrl
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (scale === 'fit') {
|
||||
firstZoom();
|
||||
}
|
||||
|
||||
let delta = e.deltaY < 0 ? 1 : -1;
|
||||
|
||||
// Pinching should increase the scale
|
||||
if (e.ctrlKey && !isScrollWhellKeyPressed) {
|
||||
delta *= -1;
|
||||
}
|
||||
updateScale(scale as number * (1 - delta * InlineImageView.SCALE_PINCH_FACTOR));
|
||||
})
|
||||
.on(DOM.EventType.SCROLL, () => {
|
||||
if (!imgElement || !imgElement.parentElement || scale === 'fit') {
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = InlineImageView.imageStateCache.get(cacheKey);
|
||||
if (entry) {
|
||||
const { scrollTop, scrollLeft } = imgElement.parentElement;
|
||||
InlineImageView.imageStateCache.set(cacheKey, { scale: entry.scale, offsetX: scrollLeft, offsetY: scrollTop });
|
||||
}
|
||||
});
|
||||
|
||||
$(container)
|
||||
.empty()
|
||||
.addClass('image', 'zoom-in')
|
||||
.img({ src: InlineImageView.imageSrc(descriptor) })
|
||||
.style('visibility', 'hidden')
|
||||
.addClass('scale-to-fit')
|
||||
.on(DOM.EventType.LOAD, (e, i) => {
|
||||
img = i;
|
||||
imgElement = img.getHTMLElement() as HTMLImageElement;
|
||||
metadataClb(nls.localize('imgMeta', '{0}x{1} {2}', imgElement.naturalWidth, imgElement.naturalHeight, BinarySize.formatSize(descriptor.size)));
|
||||
scrollbar.scanDomNode();
|
||||
img.style('visibility', 'visible');
|
||||
updateScale(scale);
|
||||
if (initialState.scale !== 'fit') {
|
||||
scrollbar.setScrollPosition({
|
||||
scrollLeft: initialState.offsetX,
|
||||
scrollTop: initialState.offsetY,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private static imageSrc(descriptor: IResourceDescriptor): string {
|
||||
if (descriptor.resource.scheme === Schemas.data) {
|
||||
return descriptor.resource.toString(true /* skip encoding */);
|
||||
}
|
||||
|
||||
const src = descriptor.resource.toString();
|
||||
|
||||
let cached = InlineImageView.IMAGE_RESOURCE_ETAG_CACHE.get(src);
|
||||
if (!cached) {
|
||||
cached = { etag: descriptor.etag, src };
|
||||
InlineImageView.IMAGE_RESOURCE_ETAG_CACHE.set(src, cached);
|
||||
}
|
||||
|
||||
if (cached.etag !== descriptor.etag) {
|
||||
cached.etag = descriptor.etag;
|
||||
cached.src = `${src}?${Date.now()}`; // bypass cache with this trick
|
||||
}
|
||||
|
||||
return cached.src;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/br
|
||||
|
||||
export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
public static ID: string = 'workbench.editor.sidebysideEditor';
|
||||
public static readonly ID: string = 'workbench.editor.sidebysideEditor';
|
||||
|
||||
private dimension: Dimension;
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { MIME_BINARY } from 'vs/base/common/mime';
|
||||
import { shorten, getPathLabel } from 'vs/base/common/labels';
|
||||
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 { IEditorGroup, toResource } from 'vs/workbench/common/editor';
|
||||
@@ -24,32 +23,30 @@ 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 { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { TitleControl, handleWorkspaceExternalDrop } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { extractResources } from 'vs/workbench/browser/editor';
|
||||
import { getOrSet } from 'vs/base/common/map';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER } from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_BACKGROUND, WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder, contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Dimension } from 'vs/base/browser/builder';
|
||||
import { scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import { ResourcesDropHandler, fillResourceDataTransfers, LocalSelectionTransfer, DraggedEditorIdentifier } from 'vs/workbench/browser/dnd';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
import { HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import * as QueryConstants from 'sql/parts/query/common/constants';
|
||||
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
|
||||
@@ -77,6 +74,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
private blockRevealActiveTab: boolean;
|
||||
private dimension: Dimension;
|
||||
private layoutScheduled: IDisposable;
|
||||
private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
|
||||
|
||||
constructor(
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@@ -86,22 +84,17 @@ export class TabsTitleControl extends TitleControl {
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService,
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@IWindowsService private windowsService: IWindowsService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IWorkspacesService private workspacesService: IWorkspacesService,
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
@IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService,
|
||||
@IConnectionManagementService private connectionService: IConnectionManagementService,
|
||||
@IQueryEditorService private queryEditorService: IQueryEditorService,
|
||||
@IObjectExplorerService private objectExplorerService: IObjectExplorerService,
|
||||
@IWorkbenchEditorService private workbenchEditorService: IWorkbenchEditorService,
|
||||
@IObjectExplorerService private objectExplorerService: IObjectExplorerService
|
||||
) {
|
||||
super(contextMenuService, instantiationService, editorService, editorGroupService, contextKeyService, keybindingService, telemetryService, messageService, menuService, quickOpenService, themeService);
|
||||
super(contextMenuService, instantiationService, editorService, editorGroupService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService);
|
||||
|
||||
this.tabDisposeables = [];
|
||||
this.editorLabels = [];
|
||||
@@ -135,12 +128,6 @@ export class TabsTitleControl extends TitleControl {
|
||||
return this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService]));
|
||||
}
|
||||
|
||||
public setContext(group: IEditorGroup): void {
|
||||
super.setContext(group);
|
||||
|
||||
this.editorActionsToolbar.context = { group };
|
||||
}
|
||||
|
||||
public create(parent: HTMLElement): void {
|
||||
super.create(parent);
|
||||
|
||||
@@ -196,7 +183,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Drag over
|
||||
this.toUnbind.push(DOM.addDisposableListener(this.tabsContainer, DOM.EventType.DRAG_OVER, (e: DragEvent) => {
|
||||
const draggedEditor = TabsTitleControl.getDraggedEditor();
|
||||
const draggedEditor = this.transfer.hasData(DraggedEditorIdentifier.prototype) ? this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier : void 0;
|
||||
|
||||
// update the dropEffect, otherwise it would look like a "move" operation. but only if we are
|
||||
// not dragging a tab actually because there we support both moving as well as copying
|
||||
@@ -309,7 +296,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
const isGroupActive = this.stacks.isActive(group);
|
||||
if (isGroupActive) {
|
||||
DOM.addClass(this.titleContainer, 'active');
|
||||
DOM.removeClass(this.titleContainer, 'inactive');
|
||||
} else {
|
||||
DOM.addClass(this.titleContainer, 'inactive');
|
||||
DOM.removeClass(this.titleContainer, 'active');
|
||||
}
|
||||
|
||||
@@ -583,7 +572,10 @@ export class TabsTitleControl extends TitleControl {
|
||||
DOM.addClass(tabCloseContainer, 'tab-close');
|
||||
tabContainer.appendChild(tabCloseContainer);
|
||||
|
||||
const bar = new ActionBar(tabCloseContainer, { ariaLabel: nls.localize('araLabelTabActions', "Tab actions"), actionRunner: new TabActionRunner(() => this.context, index) });
|
||||
const actionRunner = new TabActionRunner(() => this.context, index);
|
||||
this.tabDisposeables.push(actionRunner);
|
||||
|
||||
const bar = new ActionBar(tabCloseContainer, { ariaLabel: nls.localize('araLabelTabActions', "Tab actions"), actionRunner });
|
||||
bar.push(this.closeEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.closeEditorAction) });
|
||||
|
||||
// Eventing
|
||||
@@ -659,7 +651,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
private hookTabListeners(tab: HTMLElement, index: number): IDisposable {
|
||||
const disposables: IDisposable[] = [];
|
||||
|
||||
const handleClickOrTouch = (e: MouseEvent | GestureEvent) => {
|
||||
const handleClickOrTouch = (e: MouseEvent | GestureEvent): void => {
|
||||
tab.blur();
|
||||
|
||||
if (e instanceof MouseEvent && e.button !== 0) {
|
||||
@@ -670,8 +662,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
return void 0; // only for left mouse click
|
||||
}
|
||||
|
||||
const { editor, position } = this.toTabContext(index);
|
||||
if (!this.isTabActionBar((e.target || e.srcElement) as HTMLElement)) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -681,7 +673,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
const showContextMenu = (e: Event) => {
|
||||
DOM.EventHelper.stop(e);
|
||||
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.onContextMenu({ group, editor }, e, tab);
|
||||
};
|
||||
@@ -703,7 +695,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
tab.blur();
|
||||
|
||||
if (e.button === 1 /* Middle Button*/ && !this.isTabActionBar((e.target || e.srcElement) as HTMLElement)) {
|
||||
this.closeEditorAction.run(this.toTabContext(index)).done(null, errors.onUnexpectedError);
|
||||
this.closeEditorAction.run({ groupId: this.context.id, editorIndex: index }).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -725,7 +717,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
let handled = false;
|
||||
|
||||
const { group, position, editor } = this.toTabContext(index);
|
||||
const { group, position, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
// Run action on Enter/Space
|
||||
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
@@ -768,7 +760,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.DBLCLICK, (e: MouseEvent) => {
|
||||
DOM.EventHelper.stop(e);
|
||||
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.editorGroupService.pinEditor(group, editor);
|
||||
}));
|
||||
@@ -776,28 +768,23 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Context menu
|
||||
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.CONTEXT_MENU, (e: Event) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.onContextMenu({ group, editor }, 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.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.transfer.setData([new DraggedEditorIdentifier({ editor, group })], DraggedEditorIdentifier.prototype);
|
||||
|
||||
this.onEditorDragStart({ editor, group });
|
||||
e.dataTransfer.effectAllowed = 'copyMove';
|
||||
|
||||
// Insert transfer accordingly
|
||||
// Apply some datatransfer types to allow for dragging the element outside of the application
|
||||
const resource = toResource(editor, { supportSideBySide: true });
|
||||
if (resource) {
|
||||
const resourceStr = resource.toString();
|
||||
e.dataTransfer.setData('URL', resourceStr); // enables cross window DND of tabs
|
||||
e.dataTransfer.setData('text/plain', getPathLabel(resource)); // enables dropping tab resource path into text controls
|
||||
|
||||
if (resource.scheme === 'file') {
|
||||
e.dataTransfer.setData('DownloadURL', [MIME_BINARY, editor.getName(), resourceStr].join(':')); // enables support to drag a tab as file to desktop
|
||||
}
|
||||
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
|
||||
}
|
||||
|
||||
// Fixes https://github.com/Microsoft/vscode/issues/18733
|
||||
@@ -818,9 +805,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Find out if the currently dragged editor is this tab and in that
|
||||
// case we do not want to show any drop feedback
|
||||
let draggedEditorIsTab = false;
|
||||
const draggedEditor = TabsTitleControl.getDraggedEditor();
|
||||
const draggedEditor = this.transfer.hasData(DraggedEditorIdentifier.prototype) ? this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier : void 0;
|
||||
if (draggedEditor) {
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
if (draggedEditor.editor === editor && draggedEditor.group === group) {
|
||||
draggedEditorIsTab = true;
|
||||
}
|
||||
@@ -848,7 +835,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
DOM.removeClass(tab, 'dragged-over');
|
||||
this.updateDropFeedback(tab, false, index);
|
||||
|
||||
this.onEditorDragEnd();
|
||||
this.transfer.clearData();
|
||||
}));
|
||||
|
||||
// Drop
|
||||
@@ -857,7 +844,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
DOM.removeClass(tab, 'dragged-over');
|
||||
this.updateDropFeedback(tab, false, index);
|
||||
|
||||
const { group, position } = this.toTabContext(index);
|
||||
const { group, position } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.onDrop(e, group, position, index);
|
||||
}));
|
||||
@@ -869,7 +856,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
return !!DOM.findParentWithClass(element, 'monaco-action-bar', 'tab');
|
||||
}
|
||||
|
||||
private toTabContext(index: number): { group: IEditorGroup, position: Position, editor: IEditorInput } {
|
||||
private getGroupPositionAndEditor(index: number): { group: IEditorGroup, position: Position, editor: IEditorInput } {
|
||||
const group = this.context;
|
||||
const position = this.stacks.positionOfGroup(group);
|
||||
const editor = group.getEditor(index);
|
||||
@@ -878,13 +865,14 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
private onDrop(e: DragEvent, group: IEditorGroup, targetPosition: Position, targetIndex: number): void {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
|
||||
this.updateDropFeedback(this.tabsContainer, false);
|
||||
DOM.removeClass(this.tabsContainer, 'scroll');
|
||||
|
||||
// Local DND
|
||||
const draggedEditor = TabsTitleControl.getDraggedEditor();
|
||||
const draggedEditor = this.transfer.hasData(DraggedEditorIdentifier.prototype) ? this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier : void 0;
|
||||
if (draggedEditor) {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
|
||||
// Move editor to target position and index
|
||||
if (this.isMoveOperation(e, draggedEditor.group, group)) {
|
||||
@@ -896,42 +884,13 @@ export class TabsTitleControl extends TitleControl {
|
||||
this.editorService.openEditor(draggedEditor.editor, { pinned: true, index: targetIndex }, targetPosition).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
this.onEditorDragEnd();
|
||||
this.transfer.clearData();
|
||||
}
|
||||
|
||||
// External DND
|
||||
else {
|
||||
this.handleExternalDrop(e, targetPosition, targetIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private handleExternalDrop(e: DragEvent, targetPosition: Position, targetIndex: number): void {
|
||||
const droppedResources = extractResources(e).filter(r => r.resource.scheme === 'file' || r.resource.scheme === 'untitled');
|
||||
if (droppedResources.length) {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
|
||||
handleWorkspaceExternalDrop(droppedResources, this.fileService, this.messageService, this.windowsService, this.windowService, this.workspacesService).then(handled => {
|
||||
if (handled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add external ones to recently open list
|
||||
const externalResources = droppedResources.filter(d => d.isExternal).map(d => d.resource);
|
||||
if (externalResources.length) {
|
||||
this.windowsService.addRecentlyOpened(externalResources.map(resource => resource.fsPath));
|
||||
}
|
||||
|
||||
// Open in Editor
|
||||
this.windowService.focusWindow()
|
||||
.then(() => this.editorService.openEditors(droppedResources.map(d => {
|
||||
return {
|
||||
input: { resource: d.resource, options: { pinned: true, index: targetIndex } },
|
||||
position: targetPosition
|
||||
};
|
||||
}))).then(() => {
|
||||
this.editorGroupService.focusGroup(targetPosition);
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
});
|
||||
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false /* open workspace file as file if dropped */ });
|
||||
dropHandler.handleDrop(e, () => this.editorGroupService.focusGroup(targetPosition), targetPosition, targetIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,7 +929,10 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
class TabActionRunner extends ActionRunner {
|
||||
|
||||
constructor(private group: () => IEditorGroup, private index: number) {
|
||||
constructor(
|
||||
private group: () => IEditorGroup,
|
||||
private index: number
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -980,7 +942,7 @@ class TabActionRunner extends ActionRunner {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
return super.run(action, { group, editor: group.getEditor(this.index) });
|
||||
return super.run(action, { groupId: group.id, editorIndex: this.index });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1009,4 +971,134 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
// Hover Background
|
||||
const tabHoverBackground = theme.getColor(TAB_HOVER_BACKGROUND);
|
||||
if (tabHoverBackground) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover {
|
||||
background: ${tabHoverBackground} !important;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const tabUnfocusedHoverBackground = theme.getColor(TAB_UNFOCUSED_HOVER_BACKGROUND);
|
||||
if (tabUnfocusedHoverBackground) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.inactive .tabs-container > .tab:hover {
|
||||
background: ${tabUnfocusedHoverBackground} !important;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Hover Border
|
||||
const tabHoverBorder = theme.getColor(TAB_HOVER_BORDER);
|
||||
if (tabHoverBorder) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover {
|
||||
box-shadow: ${tabHoverBorder} 0 -1px inset !important;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const tabUnfocusedHoverBorder = theme.getColor(TAB_UNFOCUSED_HOVER_BORDER);
|
||||
if (tabUnfocusedHoverBorder) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.inactive .tabs-container > .tab:hover {
|
||||
box-shadow: ${tabUnfocusedHoverBorder} 0 -1px inset !important;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Fade out styles via linear gradient (when tabs are set to shrink)
|
||||
if (theme.type !== 'hc') {
|
||||
const workbenchBackground = WORKBENCH_BACKGROUND(theme);
|
||||
const editorBackgroundColor = theme.getColor(editorBackground);
|
||||
const editorGroupBackground = theme.getColor(EDITOR_GROUP_BACKGROUND);
|
||||
const editorGroupHeaderTabsBackground = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND);
|
||||
const editorDragAndDropBackground = theme.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
|
||||
|
||||
let adjustedTabBackground: Color;
|
||||
if (editorGroupHeaderTabsBackground && editorBackgroundColor && editorGroupBackground) {
|
||||
adjustedTabBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorGroupBackground, editorBackgroundColor, workbenchBackground);
|
||||
}
|
||||
|
||||
let adjustedTabDragBackground: Color;
|
||||
if (editorGroupHeaderTabsBackground && editorBackgroundColor && editorDragAndDropBackground && editorBackgroundColor) {
|
||||
adjustedTabDragBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorDragAndDropBackground, editorBackgroundColor, workbenchBackground);
|
||||
}
|
||||
|
||||
// Adjust gradient for (focused) hover background
|
||||
if (tabHoverBackground && adjustedTabBackground && adjustedTabDragBackground) {
|
||||
const adjustedColor = tabHoverBackground.flatten(adjustedTabBackground);
|
||||
const adjustedColorDrag = tabHoverBackground.flatten(adjustedTabDragBackground);
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content:not(.dragged-over) > .one-editor-silo > .container > .title.active .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColor}, transparent);
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.dragged-over > .one-editor-silo > .container > .title.active .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColorDrag}, transparent);
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Adjust gradient for unfocused hover background
|
||||
if (tabUnfocusedHoverBackground && adjustedTabBackground && adjustedTabDragBackground) {
|
||||
const adjustedColor = tabUnfocusedHoverBackground.flatten(adjustedTabBackground);
|
||||
const adjustedColorDrag = tabUnfocusedHoverBackground.flatten(adjustedTabDragBackground);
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content:not(.dragged-over) > .one-editor-silo > .container > .title.inactive .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColor}, transparent);
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.dragged-over > .one-editor-silo > .container > .title.inactive .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColorDrag}, transparent);
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Adjust gradient for drag and drop background
|
||||
if (editorDragAndDropBackground && adjustedTabDragBackground) {
|
||||
const adjustedColorDrag = editorDragAndDropBackground.flatten(adjustedTabDragBackground);
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content.dragged-over > .one-editor-silo > .container > .title.active .tabs-container > .tab.sizing-shrink.dragged-over:not(.active):not(.dragged) > .tab-label::after,
|
||||
.monaco-workbench > .part.editor > .content.dragged-over > .one-editor-silo > .container > .title.inactive .tabs-container > .tab.sizing-shrink.dragged-over:not(.dragged) > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColorDrag}, transparent);
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Adjust gradient for active tab background
|
||||
const tabActiveBackground = theme.getColor(TAB_ACTIVE_BACKGROUND);
|
||||
if (tabActiveBackground && adjustedTabBackground && adjustedTabDragBackground) {
|
||||
const adjustedColor = tabActiveBackground.flatten(adjustedTabBackground);
|
||||
const adjustedColorDrag = tabActiveBackground.flatten(adjustedTabDragBackground);
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content:not(.dragged-over) > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColor}, transparent);
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.dragged-over > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColorDrag}, transparent);
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Adjust gradient for inactive tab background
|
||||
const tabInactiveBackground = theme.getColor(TAB_INACTIVE_BACKGROUND);
|
||||
if (tabInactiveBackground && adjustedTabBackground && adjustedTabDragBackground) {
|
||||
const adjustedColor = tabInactiveBackground.flatten(adjustedTabBackground);
|
||||
const adjustedColorDrag = tabInactiveBackground.flatten(adjustedTabDragBackground);
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content:not(.dragged-over) > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink:not(.dragged) > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColor}, transparent);
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.dragged-over > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink:not(.dragged) > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColorDrag}, transparent);
|
||||
}
|
||||
`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ import types = require('vs/base/common/types');
|
||||
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IFileEditorInput } from 'vs/workbench/common/editor';
|
||||
import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator';
|
||||
@@ -32,8 +32,10 @@ import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/wo
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
/**
|
||||
* The text editor that leverages the diff text editor for the editing experience.
|
||||
@@ -45,18 +47,27 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
private diffNavigator: DiffNavigator;
|
||||
private nextDiffAction: NavigateAction;
|
||||
private previousDiffAction: NavigateAction;
|
||||
private toggleIgnoreTrimWhitespaceAction: ToggleIgnoreTrimWhitespaceAction;
|
||||
private _configurationListener: IDisposable;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
|
||||
@IConfigurationService private readonly _actualConfigurationService: IConfigurationService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITextFileService textFileService: ITextFileService
|
||||
) {
|
||||
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
|
||||
|
||||
this._configurationListener = this._actualConfigurationService.onDidChangeConfiguration((e) => {
|
||||
if (e.affectsConfiguration('diffEditor.ignoreTrimWhitespace')) {
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
@@ -72,6 +83,8 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
// Actions
|
||||
this.nextDiffAction = new NavigateAction(this, true);
|
||||
this.previousDiffAction = new NavigateAction(this, false);
|
||||
this.toggleIgnoreTrimWhitespaceAction = new ToggleIgnoreTrimWhitespaceAction(this._actualConfigurationService);
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
|
||||
// Support navigation within the diff editor by overriding the editor service within
|
||||
const delegatingEditorService = this.instantiationService.createInstance(DelegatingWorkbenchEditorService);
|
||||
@@ -163,6 +176,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
this.nextDiffAction.updateEnablement();
|
||||
this.previousDiffAction.updateEnablement();
|
||||
});
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
}, error => {
|
||||
|
||||
// In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff.
|
||||
@@ -176,6 +190,13 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
});
|
||||
}
|
||||
|
||||
private updateIgnoreTrimWhitespaceAction(): void {
|
||||
const ignoreTrimWhitespace = this.configurationService.getValue<boolean>(this.getResource(), 'diffEditor.ignoreTrimWhitespace');
|
||||
if (this.toggleIgnoreTrimWhitespaceAction) {
|
||||
this.toggleIgnoreTrimWhitespaceAction.updateClassName(ignoreTrimWhitespace);
|
||||
}
|
||||
}
|
||||
|
||||
private openAsBinary(input: EditorInput, options: EditorOptions): boolean {
|
||||
if (input instanceof DiffEditorInput) {
|
||||
const originalInput = input.originalInput;
|
||||
@@ -184,12 +205,13 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
const binaryDiffInput = new DiffEditorInput(input.getName(), input.getDescription(), originalInput, modifiedInput, true);
|
||||
|
||||
// Forward binary flag to input if supported
|
||||
if (types.isFunction(((originalInput as IEditorInput) as IFileEditorInput).setForceOpenAsBinary)) {
|
||||
((originalInput as IEditorInput) as IFileEditorInput).setForceOpenAsBinary();
|
||||
const fileInputFactory = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).getFileInputFactory();
|
||||
if (fileInputFactory.isFileInput(originalInput)) {
|
||||
originalInput.setForceOpenAsBinary();
|
||||
}
|
||||
|
||||
if (types.isFunction(((modifiedInput as IEditorInput) as IFileEditorInput).setForceOpenAsBinary)) {
|
||||
((modifiedInput as IEditorInput) as IFileEditorInput).setForceOpenAsBinary();
|
||||
if (fileInputFactory.isFileInput(modifiedInput)) {
|
||||
modifiedInput.setForceOpenAsBinary();
|
||||
}
|
||||
|
||||
this.editorService.openEditor(binaryDiffInput, options, this.position).done(null, onUnexpectedError);
|
||||
@@ -273,25 +295,12 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
|
||||
public getActions(): IAction[] {
|
||||
return [
|
||||
this.toggleIgnoreTrimWhitespaceAction,
|
||||
this.previousDiffAction,
|
||||
this.nextDiffAction
|
||||
];
|
||||
}
|
||||
|
||||
public getSecondaryActions(): IAction[] {
|
||||
const actions = super.getSecondaryActions();
|
||||
|
||||
// Action to toggle editor mode from inline to side by side
|
||||
const toggleEditorModeAction = new ToggleEditorModeAction(this);
|
||||
toggleEditorModeAction.order = 50; // Closer to the end
|
||||
|
||||
actions.push(...[
|
||||
toggleEditorModeAction
|
||||
]);
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
public getControl(): IDiffEditor {
|
||||
return super.getControl() as IDiffEditor;
|
||||
}
|
||||
@@ -303,6 +312,8 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
this.diffNavigator.dispose();
|
||||
}
|
||||
|
||||
this._configurationListener.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -340,33 +351,25 @@ class NavigateAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleEditorModeAction extends Action {
|
||||
private static readonly ID = 'toggle.diff.editorMode';
|
||||
private static readonly INLINE_LABEL = nls.localize('inlineDiffLabel', "Switch to Inline View");
|
||||
private static readonly SIDEBYSIDE_LABEL = nls.localize('sideBySideDiffLabel', "Switch to Side by Side View");
|
||||
class ToggleIgnoreTrimWhitespaceAction extends Action {
|
||||
static ID = 'workbench.action.compareEditor.toggleIgnoreTrimWhitespace';
|
||||
|
||||
constructor(private editor: TextDiffEditor) {
|
||||
super(ToggleEditorModeAction.ID);
|
||||
private _isChecked: boolean;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
super(ToggleIgnoreTrimWhitespaceAction.ID);
|
||||
this.label = nls.localize('toggleIgnoreTrimWhitespace.label', "Ignore Trim Whitespace");
|
||||
}
|
||||
|
||||
public get label(): string {
|
||||
return ToggleEditorModeAction.isInlineMode(this.editor) ? ToggleEditorModeAction.SIDEBYSIDE_LABEL : ToggleEditorModeAction.INLINE_LABEL;
|
||||
public updateClassName(ignoreTrimWhitespace: boolean): void {
|
||||
this._isChecked = ignoreTrimWhitespace;
|
||||
this.class = `textdiff-editor-action toggleIgnoreTrimWhitespace${this._isChecked ? ' is-checked' : ''}`;
|
||||
}
|
||||
|
||||
public run(): TPromise<boolean> {
|
||||
const inlineModeActive = ToggleEditorModeAction.isInlineMode(this.editor);
|
||||
|
||||
const control = this.editor.getControl();
|
||||
control.updateOptions(<IDiffEditorOptions>{
|
||||
renderSideBySide: inlineModeActive
|
||||
});
|
||||
|
||||
return TPromise.as(true);
|
||||
public run(): TPromise<any> {
|
||||
this._configurationService.updateValue(`diffEditor.ignoreTrimWhitespace`, !this._isChecked);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static isInlineMode(editor: TextDiffEditor): boolean {
|
||||
const control = editor.getControl();
|
||||
|
||||
return control && !control.renderSideBySide;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Scope } from 'vs/workbench/common/memento';
|
||||
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { getCodeEditor, getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ITextFileService, SaveReason, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
@@ -50,11 +50,11 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
constructor(
|
||||
id: string,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@ITextResourceConfigurationService private _configurationService: ITextResourceConfigurationService,
|
||||
@ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService,
|
||||
@IThemeService protected themeService: IThemeService,
|
||||
@ITextFileService private _textFileService: ITextFileService,
|
||||
@ITextFileService private readonly _textFileService: ITextFileService,
|
||||
@IEditorGroupService protected editorGroupService: IEditorGroupService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
@@ -230,48 +230,66 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the text editor view state under the given key.
|
||||
* Saves the text editor view state for the given resource.
|
||||
*/
|
||||
protected saveTextEditorViewState(key: string): void {
|
||||
protected saveTextEditorViewState(resource: URI): void {
|
||||
const editor = getCodeOrDiffEditor(this).codeEditor;
|
||||
if (!editor) {
|
||||
return; // not supported for diff editors
|
||||
}
|
||||
|
||||
const model = editor.getModel();
|
||||
if (!model) {
|
||||
return; // view state always needs a model
|
||||
}
|
||||
|
||||
const modelUri = model.uri;
|
||||
if (!modelUri) {
|
||||
return; // model URI is needed to make sure we save the view state correctly
|
||||
}
|
||||
|
||||
if (modelUri.toString() !== resource.toString()) {
|
||||
return; // prevent saving view state for a model that is not the expected one
|
||||
}
|
||||
|
||||
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
|
||||
|
||||
let textEditorViewStateMemento: { [key: string]: { [position: number]: IEditorViewState } } = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
|
||||
if (!textEditorViewStateMemento) {
|
||||
textEditorViewStateMemento = Object.create(null);
|
||||
memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY] = textEditorViewStateMemento;
|
||||
}
|
||||
|
||||
const editorViewState = this.getControl().saveViewState();
|
||||
|
||||
let lastKnownViewState = textEditorViewStateMemento[key];
|
||||
let lastKnownViewState = textEditorViewStateMemento[resource.toString()];
|
||||
if (!lastKnownViewState) {
|
||||
lastKnownViewState = Object.create(null);
|
||||
textEditorViewStateMemento[key] = lastKnownViewState;
|
||||
textEditorViewStateMemento[resource.toString()] = lastKnownViewState;
|
||||
}
|
||||
|
||||
if (typeof this.position === 'number') {
|
||||
lastKnownViewState[this.position] = editorViewState;
|
||||
lastKnownViewState[this.position] = editor.saveViewState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the text editor view state under the given key.
|
||||
* Clears the text editor view state for the given resources.
|
||||
*/
|
||||
protected clearTextEditorViewState(keys: string[]): void {
|
||||
protected clearTextEditorViewState(resources: URI[]): void {
|
||||
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
|
||||
const textEditorViewStateMemento: { [key: string]: { [position: number]: IEditorViewState } } = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
|
||||
if (textEditorViewStateMemento) {
|
||||
keys.forEach(key => delete textEditorViewStateMemento[key]);
|
||||
resources.forEach(resource => delete textEditorViewStateMemento[resource.toString()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the text editor view state for the given key and returns it.
|
||||
* Loads the text editor view state for the given resource and returns it.
|
||||
*/
|
||||
protected loadTextEditorViewState(key: string): IEditorViewState {
|
||||
protected loadTextEditorViewState(resource: URI): IEditorViewState {
|
||||
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
|
||||
const textEditorViewStateMemento: { [key: string]: { [position: number]: IEditorViewState } } = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
|
||||
if (textEditorViewStateMemento) {
|
||||
const viewState = textEditorViewStateMemento[key];
|
||||
const viewState = textEditorViewStateMemento[resource.toString()];
|
||||
if (viewState) {
|
||||
return viewState[this.position];
|
||||
}
|
||||
|
||||
@@ -28,11 +28,10 @@ import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
* An editor implementation that is capable of showing the contents of resource inputs. Uses
|
||||
* the TextEditor widget to show the contents.
|
||||
*/
|
||||
export class TextResourceEditor extends BaseTextEditor {
|
||||
|
||||
public static readonly ID = 'workbench.editors.textResourceEditor';
|
||||
export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@@ -41,7 +40,7 @@ export class TextResourceEditor extends BaseTextEditor {
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITextFileService textFileService: ITextFileService
|
||||
) {
|
||||
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
|
||||
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
@@ -108,7 +107,7 @@ export class TextResourceEditor extends BaseTextEditor {
|
||||
|
||||
protected restoreViewState(input: EditorInput) {
|
||||
if (input instanceof UntitledEditorInput || input instanceof ResourceEditorInput) {
|
||||
const viewState = this.loadTextEditorViewState(input.getResource().toString());
|
||||
const viewState = this.loadTextEditorViewState(input.getResource());
|
||||
if (viewState) {
|
||||
this.getControl().restoreViewState(viewState);
|
||||
}
|
||||
@@ -178,21 +177,38 @@ export class TextResourceEditor extends BaseTextEditor {
|
||||
return; // only enabled for untitled and resource inputs
|
||||
}
|
||||
|
||||
const key = input.getResource().toString();
|
||||
const resource = input.getResource();
|
||||
|
||||
// Clear view state if input is disposed
|
||||
if (input.isDisposed()) {
|
||||
super.clearTextEditorViewState([key]);
|
||||
super.clearTextEditorViewState([resource]);
|
||||
}
|
||||
|
||||
// Otherwise save it
|
||||
else {
|
||||
super.saveTextEditorViewState(key);
|
||||
super.saveTextEditorViewState(resource);
|
||||
|
||||
// Make sure to clean up when the input gets disposed
|
||||
once(input.onDispose)(() => {
|
||||
super.clearTextEditorViewState([key]);
|
||||
super.clearTextEditorViewState([resource]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TextResourceEditor extends AbstractTextResourceEditor {
|
||||
|
||||
public static readonly ID = 'workbench.editors.textResourceEditor';
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITextFileService textFileService: ITextFileService
|
||||
) {
|
||||
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, textFileService);
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,7 @@
|
||||
|
||||
import 'vs/css!./media/titlecontrol';
|
||||
import nls = require('vs/nls');
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Scope, IActionBarRegistry, Extensions, prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { IAction, Action, IRunEvent } from 'vs/base/common/actions';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
@@ -16,13 +15,12 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import arrays = require('vs/base/common/arrays');
|
||||
import { IEditorStacksModel, IEditorGroup, IEditorIdentifier, EditorInput, IStacksModelChangeEvent, toResource } from 'vs/workbench/common/editor';
|
||||
import { IActionItem, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IEditorStacksModel, IEditorGroup, IEditorIdentifier, EditorInput, IStacksModelChangeEvent, toResource, IEditorCommandsContext } 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 { IMessageService, Severity } from 'vs/platform/message/common/message';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -30,21 +28,16 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { CloseEditorsInGroupAction, SplitEditorAction, CloseEditorAction, KeepEditorAction, CloseOtherEditorsInGroupAction, CloseRightEditorsInGroupAction, ShowEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction } from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import { SplitEditorAction, CloseEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { createActionItem, fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { IMenuService, MenuId, IMenu, ExecuteCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IDraggedResource } from 'vs/workbench/browser/editor';
|
||||
import { WORKSPACE_EXTENSION, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { extname } from 'vs/base/common/paths';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Dimension } from 'vs/base/browser/builder';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export interface IToolbarActions {
|
||||
primary: IAction[];
|
||||
@@ -67,21 +60,13 @@ export interface ITitleAreaControl {
|
||||
|
||||
export abstract class TitleControl extends Themable implements ITitleAreaControl {
|
||||
|
||||
private static draggedEditor: IEditorIdentifier;
|
||||
|
||||
protected stacks: IEditorStacksModel;
|
||||
protected context: IEditorGroup;
|
||||
|
||||
protected dragged: boolean;
|
||||
|
||||
protected closeEditorAction: CloseEditorAction;
|
||||
protected pinEditorAction: KeepEditorAction;
|
||||
protected closeOtherEditorsAction: CloseOtherEditorsInGroupAction;
|
||||
protected closeRightEditorsAction: CloseRightEditorsInGroupAction;
|
||||
protected closeUnmodifiedEditorsInGroupAction: CloseUnmodifiedEditorsInGroupAction;
|
||||
protected closeEditorsInGroupAction: CloseEditorsInGroupAction;
|
||||
protected splitEditorAction: SplitEditorAction;
|
||||
protected showEditorsInGroupAction: ShowEditorsInGroupAction;
|
||||
|
||||
private parent: HTMLElement;
|
||||
|
||||
@@ -106,7 +91,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
@IContextKeyService protected contextKeyService: IContextKeyService,
|
||||
@IKeybindingService protected keybindingService: IKeybindingService,
|
||||
@ITelemetryService protected telemetryService: ITelemetryService,
|
||||
@IMessageService protected messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IMenuService protected menuService: IMenuService,
|
||||
@IQuickOpenService protected quickOpenService: IQuickOpenService,
|
||||
@IThemeService protected themeService: IThemeService
|
||||
@@ -128,22 +113,10 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public static getDraggedEditor(): IEditorIdentifier {
|
||||
return TitleControl.draggedEditor;
|
||||
}
|
||||
|
||||
public setDragged(dragged: boolean): void {
|
||||
this.dragged = dragged;
|
||||
}
|
||||
|
||||
protected onEditorDragStart(editor: IEditorIdentifier): void {
|
||||
TitleControl.draggedEditor = editor;
|
||||
}
|
||||
|
||||
protected onEditorDragEnd(): void {
|
||||
TitleControl.draggedEditor = void 0;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.stacks.onModelChanged(e => this.onStacksChanged(e)));
|
||||
}
|
||||
@@ -183,6 +156,8 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
|
||||
public setContext(group: IEditorGroup): void {
|
||||
this.context = group;
|
||||
|
||||
this.editorActionsToolbar.context = { groupId: group ? group.id : void 0 } as IEditorCommandsContext;
|
||||
}
|
||||
|
||||
public hasContext(): boolean {
|
||||
@@ -233,12 +208,6 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
|
||||
protected initActions(services: IInstantiationService): void {
|
||||
this.closeEditorAction = services.createInstance(CloseEditorAction, CloseEditorAction.ID, nls.localize('close', "Close"));
|
||||
this.closeOtherEditorsAction = services.createInstance(CloseOtherEditorsInGroupAction, CloseOtherEditorsInGroupAction.ID, nls.localize('closeOthers', "Close Others"));
|
||||
this.closeRightEditorsAction = services.createInstance(CloseRightEditorsInGroupAction, CloseRightEditorsInGroupAction.ID, nls.localize('closeRight', "Close to the Right"));
|
||||
this.closeEditorsInGroupAction = services.createInstance(CloseEditorsInGroupAction, CloseEditorsInGroupAction.ID, nls.localize('closeAll', "Close All"));
|
||||
this.closeUnmodifiedEditorsInGroupAction = services.createInstance(CloseUnmodifiedEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction.ID, nls.localize('closeAllUnmodified', "Close Unmodified"));
|
||||
this.pinEditorAction = services.createInstance(KeepEditorAction, KeepEditorAction.ID, nls.localize('keepOpen', "Keep Open"));
|
||||
this.showEditorsInGroupAction = services.createInstance(ShowEditorsInGroupAction, ShowEditorsInGroupAction.ID, nls.localize('showOpenedEditors', "Show Opened Editors"));
|
||||
this.splitEditorAction = services.createInstance(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL);
|
||||
}
|
||||
|
||||
@@ -255,7 +224,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
|
||||
// Check for Error
|
||||
if (e.error && !errors.isPromiseCanceledError(e.error)) {
|
||||
this.messageService.show(Severity.Error, e.error);
|
||||
this.notificationService.error(e.error);
|
||||
}
|
||||
|
||||
// Log in telemetry
|
||||
@@ -287,15 +256,9 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
actionItem = editor.getActionItem(action);
|
||||
}
|
||||
|
||||
// Check Registry
|
||||
if (!actionItem) {
|
||||
const actionBarRegistry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
|
||||
actionItem = actionBarRegistry.getActionItemForContext(Scope.EDITOR, { input: editor && editor.input, editor, position }, action);
|
||||
}
|
||||
|
||||
// Check extensions
|
||||
if (!actionItem) {
|
||||
actionItem = createActionItem(action, this.keybindingService, this.messageService);
|
||||
actionItem = createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
|
||||
}
|
||||
|
||||
return actionItem;
|
||||
@@ -335,7 +298,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
|
||||
this.disposeOnEditorActions.push(titleBarMenu, titleBarMenu.onDidChange(_ => this.update()));
|
||||
|
||||
fillInActions(titleBarMenu, { arg: this.resourceContext.get() }, { primary, secondary });
|
||||
fillInActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary }, this.contextMenuService);
|
||||
}
|
||||
|
||||
return { primary, secondary };
|
||||
@@ -353,26 +316,21 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
// Update Editor Actions Toolbar
|
||||
let primaryEditorActions: IAction[] = [];
|
||||
let secondaryEditorActions: IAction[] = [];
|
||||
|
||||
const editorActions = this.getEditorActions({ group, editor });
|
||||
|
||||
// Primary actions only for the active group
|
||||
if (isActive) {
|
||||
const editorActions = this.getEditorActions({ group, editor });
|
||||
primaryEditorActions = prepareActions(editorActions.primary);
|
||||
if (isActive && editor instanceof EditorInput && editor.supportsSplitEditor()) {
|
||||
if (editor instanceof EditorInput && editor.supportsSplitEditor()) {
|
||||
this.updateSplitActionEnablement();
|
||||
primaryEditorActions.push(this.splitEditorAction);
|
||||
}
|
||||
secondaryEditorActions = prepareActions(editorActions.secondary);
|
||||
}
|
||||
|
||||
secondaryEditorActions = prepareActions(editorActions.secondary);
|
||||
|
||||
const tabOptions = this.editorGroupService.getTabOptions();
|
||||
if (tabOptions.showTabs) {
|
||||
if (secondaryEditorActions.length > 0) {
|
||||
secondaryEditorActions.push(new Separator());
|
||||
}
|
||||
secondaryEditorActions.push(this.showEditorsInGroupAction);
|
||||
secondaryEditorActions.push(new Separator());
|
||||
secondaryEditorActions.push(this.closeUnmodifiedEditorsInGroupAction);
|
||||
secondaryEditorActions.push(this.closeEditorsInGroupAction);
|
||||
}
|
||||
|
||||
const primaryEditorActionIds = primaryEditorActions.map(a => a.id);
|
||||
if (!tabOptions.showTabs) {
|
||||
@@ -418,10 +376,15 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
anchor = { x: event.posx, y: event.posy };
|
||||
}
|
||||
|
||||
// Fill in contributed actions
|
||||
const actions: IAction[] = [];
|
||||
fillInActions(this.contextMenu, { shouldForwardArgs: true, arg: this.resourceContext.get() }, actions, this.contextMenuService);
|
||||
|
||||
// Show it
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => TPromise.as(this.getContextMenuActions(identifier)),
|
||||
getActionsContext: () => identifier,
|
||||
getActions: () => TPromise.as(actions),
|
||||
getActionsContext: () => ({ groupId: identifier.group.id, editorIndex: identifier.group.indexOf(identifier.editor) } as IEditorCommandsContext),
|
||||
getKeyBinding: (action) => this.getKeybinding(action),
|
||||
onHide: (cancel) => {
|
||||
|
||||
@@ -447,51 +410,13 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
return keybinding ? keybinding.getLabel() : void 0;
|
||||
}
|
||||
|
||||
protected getContextMenuActions(identifier: IEditorIdentifier): IAction[] {
|
||||
const { editor, group } = identifier;
|
||||
|
||||
// Enablement
|
||||
this.closeOtherEditorsAction.enabled = group.count > 1;
|
||||
this.pinEditorAction.enabled = !group.isPinned(editor);
|
||||
this.closeRightEditorsAction.enabled = group.indexOf(editor) !== group.count - 1;
|
||||
|
||||
// Actions: For all editors
|
||||
const actions: IAction[] = [
|
||||
this.closeEditorAction,
|
||||
this.closeOtherEditorsAction
|
||||
];
|
||||
const tabOptions = this.editorGroupService.getTabOptions();
|
||||
|
||||
if (tabOptions.showTabs) {
|
||||
actions.push(this.closeRightEditorsAction);
|
||||
}
|
||||
|
||||
actions.push(this.closeUnmodifiedEditorsInGroupAction);
|
||||
actions.push(this.closeEditorsInGroupAction);
|
||||
|
||||
if (tabOptions.previewEditors) {
|
||||
actions.push(new Separator(), this.pinEditorAction);
|
||||
}
|
||||
|
||||
// Fill in contributed actions
|
||||
fillInActions(this.contextMenu, { arg: this.resourceContext.get() }, actions);
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
// Actions
|
||||
[
|
||||
this.splitEditorAction,
|
||||
this.showEditorsInGroupAction,
|
||||
this.closeEditorAction,
|
||||
this.closeRightEditorsAction,
|
||||
this.closeUnmodifiedEditorsInGroupAction,
|
||||
this.closeOtherEditorsAction,
|
||||
this.closeEditorsInGroupAction,
|
||||
this.pinEditorAction
|
||||
this.closeEditorAction
|
||||
].forEach((action) => {
|
||||
action.dispose();
|
||||
});
|
||||
@@ -500,74 +425,3 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
this.editorActionsToolbar.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared function across some editor components to handle drag & drop of folders and workspace files
|
||||
* to open them in the window instead of the editor.
|
||||
*/
|
||||
export function handleWorkspaceExternalDrop(
|
||||
resources: IDraggedResource[],
|
||||
fileService: IFileService,
|
||||
messageService: IMessageService,
|
||||
windowsService: IWindowsService,
|
||||
windowService: IWindowService,
|
||||
workspacesService: IWorkspacesService
|
||||
): TPromise<boolean /* handled */> {
|
||||
|
||||
// Return early if there are no external resources
|
||||
const externalResources = resources.filter(d => d.isExternal).map(d => d.resource);
|
||||
if (!externalResources.length) {
|
||||
return TPromise.as(false);
|
||||
}
|
||||
|
||||
const externalWorkspaceResources: { workspaces: URI[], folders: URI[] } = {
|
||||
workspaces: [],
|
||||
folders: []
|
||||
};
|
||||
|
||||
return TPromise.join(externalResources.map(resource => {
|
||||
|
||||
// Check for Workspace
|
||||
if (extname(resource.fsPath) === `.${WORKSPACE_EXTENSION}`) {
|
||||
externalWorkspaceResources.workspaces.push(resource);
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// Check for Folder
|
||||
return fileService.resolveFile(resource).then(stat => {
|
||||
if (stat.isDirectory) {
|
||||
externalWorkspaceResources.folders.push(stat.resource);
|
||||
}
|
||||
}, error => void 0);
|
||||
})).then(_ => {
|
||||
const { workspaces, folders } = externalWorkspaceResources;
|
||||
|
||||
// Return early if no external resource is a folder or workspace
|
||||
if (workspaces.length === 0 && folders.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass focus to window
|
||||
windowService.focusWindow();
|
||||
|
||||
let workspacesToOpen: TPromise<string[]>;
|
||||
|
||||
// Open in separate windows if we drop workspaces or just one folder
|
||||
if (workspaces.length > 0 || folders.length === 1) {
|
||||
workspacesToOpen = TPromise.as([...workspaces, ...folders].map(resources => resources.fsPath));
|
||||
}
|
||||
|
||||
// Multiple folders: Create new workspace with folders and open
|
||||
else if (folders.length > 1) {
|
||||
workspacesToOpen = workspacesService.createWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [workspace.configPath]);
|
||||
}
|
||||
|
||||
// Open
|
||||
workspacesToOpen.then(workspaces => {
|
||||
windowsService.openWindow(workspaces, { forceReuseWindow: true });
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user