Merge VS Code 1.23.1 (#1520)

This commit is contained in:
Matt Irvine
2018-06-05 11:24:51 -07:00
committed by GitHub
parent e3baf5c443
commit 0c58f09e59
3651 changed files with 74249 additions and 48599 deletions

View File

@@ -5,7 +5,6 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Builder } from 'vs/base/browser/builder';
import { Panel } from 'vs/workbench/browser/panel';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { IEditor, Position } from 'vs/platform/editor/common/editor';
@@ -64,9 +63,9 @@ export abstract class BaseEditor extends Panel implements IEditor {
this._options = null;
}
public create(parent: Builder): void; // create is sync for editors
public create(parent: Builder): TPromise<void>;
public create(parent: Builder): TPromise<void> {
public create(parent: HTMLElement): void; // create is sync for editors
public create(parent: HTMLElement): TPromise<void>;
public create(parent: HTMLElement): TPromise<void> {
const res = super.create(parent);
// Create Editor
@@ -76,9 +75,16 @@ export abstract class BaseEditor extends Panel implements IEditor {
}
/**
* Called to create the editor in the parent builder.
* Called to create the editor in the parent HTMLElement.
*/
protected abstract createEditor(parent: Builder): void;
protected abstract createEditor(parent: HTMLElement): void;
/**
* Subclasses can set this to false if it does not make sense to center editor input.
*/
public supportsCenteredLayout(): boolean {
return true;
}
/**
* Overload this function to allow for passing in a position argument.
@@ -119,4 +125,4 @@ export abstract class BaseEditor extends Panel implements IEditor {
// Super Dispose
super.dispose();
}
}
}

View File

@@ -5,7 +5,7 @@
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';

View File

@@ -5,41 +5,51 @@
'use strict';
import nls = require('vs/nls');
import Event, { Emitter } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import { EditorModel, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { Builder, $ } from 'vs/base/browser/builder';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
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';
import URI from 'vs/base/common/uri';
import { Dimension } from 'vs/base/browser/dom';
export interface IOpenCallbacks {
openInternal: (input: EditorInput, options: EditorOptions) => void;
openExternal: (uri: URI) => void;
}
/*
* This class is only intended to be subclassed and not instantiated.
*/
export abstract class BaseBinaryResourceEditor extends BaseEditor {
private _onMetadataChanged: Emitter<void>;
private metadata: string;
private readonly _onMetadataChanged: Emitter<void>;
private callbacks: IOpenCallbacks;
private metadata: string;
private binaryContainer: Builder;
private scrollbar: DomScrollableElement;
private resourceViewerContext: ResourceViewerContext;
constructor(
id: string,
callbacks: IOpenCallbacks,
telemetryService: ITelemetryService,
themeService: IThemeService,
private windowsService: IWindowsService
themeService: IThemeService
) {
super(id, telemetryService, themeService);
this._onMetadataChanged = new Emitter<void>();
this.toUnbind.push(this._onMetadataChanged);
this.callbacks = callbacks;
}
public get onMetadataChanged(): Event<void> {
@@ -50,7 +60,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer");
}
protected createEditor(parent: Builder): void {
protected createEditor(parent: HTMLElement): void {
// Container for Binary
const binaryContainerElement = document.createElement('div');
@@ -61,7 +71,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Custom Scrollbars
this.scrollbar = new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto });
parent.getHTMLElement().appendChild(this.scrollbar.getDomNode());
parent.appendChild(this.scrollbar.getDomNode());
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -74,10 +84,10 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Otherwise set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then((resolvedModel: EditorModel) => {
return input.resolve(true).then(model => {
// Assert Model instance
if (!(resolvedModel instanceof BinaryEditorModel)) {
if (!(model instanceof BinaryEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
}
@@ -87,21 +97,14 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
}
// Render Input
const model = <BinaryEditorModel>resolvedModel;
this.resourceViewerContext = ResourceViewer.show(
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() },
this.binaryContainer,
this.binaryContainer.getHTMLElement(),
this.scrollbar,
(resource: URI) => {
this.windowsService.openExternal(resource.toString()).then(didOpen => {
if (!didOpen) {
return this.windowsService.showItemInFolder(resource.fsPath);
}
return void 0;
});
},
(meta) => this.handleMetadataChanged(meta));
resource => this.callbacks.openInternal(input, options),
resource => this.callbacks.openExternal(resource),
meta => this.handleMetadataChanged(meta)
);
return TPromise.as<void>(null);
});
@@ -117,6 +120,10 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
return this.metadata;
}
public supportsCenteredLayout(): boolean {
return false;
}
public clearInput(): void {
// Clear Meta

View File

@@ -5,7 +5,7 @@
'use strict';
import { Registry } from 'vs/platform/registry/common/platform';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Action, IAction } from 'vs/base/common/actions';
import { IEditorQuickOpenEntry, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
@@ -225,7 +225,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction,
export class QuickOpenActionContributor extends ActionBarContributor {
private openToSideActionInstance: OpenToSideAction;
constructor( @IInstantiationService private instantiationService: IInstantiationService) {
constructor(@IInstantiationService private instantiationService: IInstantiationService) {
super();
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { mixin } from 'vs/base/common/objects';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
@@ -543,6 +543,50 @@ export class CloseEditorAction extends Action {
}
}
export class CloseOneEditorAction extends Action {
public static readonly ID = 'workbench.action.closeActiveEditor';
public static readonly LABEL = nls.localize('closeOneEditor', "Close");
constructor(
id: string,
label: string,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService
) {
super(id, label, 'close-editor-action');
}
public run(context?: IEditorCommandsContext): TPromise<any> {
const model = this.editorGroupService.getStacksModel();
const group = context ? model.getGroup(context.groupId) : null;
const position = group ? model.positionOfGroup(group) : null;
// Close Active Editor
if (typeof position !== 'number') {
const activeEditor = this.editorService.getActiveEditor();
if (activeEditor) {
return this.editorService.closeEditor(activeEditor.position, activeEditor.input);
}
}
// Close Specific Editor
const editor = group && context && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : null;
if (editor) {
return this.editorService.closeEditor(position, editor);
}
// Close First Editor at Position
const visibleEditors = this.editorService.getVisibleEditors();
if (visibleEditors[position]) {
return this.editorService.closeEditor(position, visibleEditors[position].input);
}
return TPromise.as(false);
}
}
export class RevertAndCloseEditorAction extends Action {
public static readonly ID = 'workbench.action.revertAndCloseActiveEditor';

View File

@@ -70,7 +70,7 @@ function registerActiveEditorMoveCommand(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: EditorCommands.MoveActiveEditor,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: EditorContextKeys.textFocus,
when: EditorContextKeys.editorTextFocus,
primary: null,
handler: (accessor, args: any) => moveActiveEditor(args, accessor),
description: {
@@ -269,7 +269,7 @@ function registerEditorCommands() {
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const model = editorGroupService.getStacksModel();
const editorService = accessor.get(IWorkbenchEditorService);
@@ -299,7 +299,7 @@ function registerEditorCommands() {
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
@@ -324,7 +324,7 @@ function registerEditorCommands() {
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) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
@@ -373,7 +373,7 @@ function registerEditorCommands() {
when: void 0,
primary: void 0,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));

View File

@@ -6,16 +6,16 @@
'use strict';
import 'vs/css!./media/editorGroupsControl';
import arrays = require('vs/base/common/arrays');
import Event, { Emitter } from 'vs/base/common/event';
import * as arrays from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import types = require('vs/base/common/types');
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import * as types from 'vs/base/common/types';
import { Builder, $ } from 'vs/base/browser/builder';
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import DOM = require('vs/base/browser/dom');
import errors = require('vs/base/common/errors');
import * as DOM from 'vs/base/browser/dom';
import * as errors from 'vs/base/common/errors';
import { RunOnceScheduler } from 'vs/base/common/async';
import { isMacintosh } from 'vs/base/common/platform';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -29,7 +29,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
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, EditorInput, PREFERENCES_EDITOR_ID, TEXT_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier, EditorInput } 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';
@@ -40,6 +40,7 @@ import { ResourcesDropHandler, LocalSelectionTransfer, DraggedEditorIdentifier }
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPartService } from 'vs/workbench/services/part/common/partService';
export enum Rochade {
NONE,
TWO_TO_ONE,
@@ -74,7 +75,7 @@ export interface IEditorGroupsControl {
updateProgress(position: Position, state: ProgressState): void;
updateTitleAreas(refreshActive?: boolean): void;
layout(dimension: Dimension): void;
layout(dimension: DOM.Dimension): void;
layout(position: Position): void;
arrangeGroups(arrangement: GroupArrangement): void;
@@ -121,8 +122,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private stacks: IEditorStacksModel;
private parent: Builder;
private dimension: Dimension;
private parent: HTMLElement;
private dimension: DOM.Dimension;
private dragging: boolean;
private layoutVertically: boolean;
@@ -151,7 +152,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private centeredEditorActive: boolean;
private centeredEditorSashLeft: Sash;
private centeredEditorSashRight: Sash;
private centeredEditorPreferedSize: number;
private centeredEditorPreferredSize: number;
private centeredEditorLeftMarginRatio: number;
private centeredEditorDragStartPosition: number;
private centeredEditorDragStartSize: number;
@@ -163,7 +164,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private visibleEditorFocusTrackerDisposable: IDisposable[];
private _onGroupFocusChanged: Emitter<void>;
private readonly _onGroupFocusChanged: Emitter<void>;
private onStacksChangeScheduler: RunOnceScheduler;
private stacksChangedBuffer: IStacksModelChangeEvent[];
@@ -171,12 +172,12 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
constructor(
parent: Builder,
parent: HTMLElement,
groupOrientation: GroupOrientation,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@IPartService private partService: IPartService,
@IStorageService private storageServise: IStorageService,
@IStorageService private storageService: IStorageService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IExtensionService private extensionService: IExtensionService,
@IInstantiationService private instantiationService: IInstantiationService,
@@ -188,7 +189,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.stacks = editorGroupService.getStacksModel();
this.parent = parent;
this.dimension = new Dimension(0, 0);
this.dimension = new DOM.Dimension(0, 0);
this.silos = [];
this.silosSize = [];
@@ -291,6 +292,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
titleControl.dispose();
titleContainer.empty();
this.createTitleControl(this.stacks.groupAt(position), this.silos[position], titleContainer, this.getInstantiationService(position));
this.layoutTitleControl(position);
}
// Refresh title when layout options change
@@ -369,8 +371,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.trackFocus(editor, position);
// Find target container and build into
const target = this.silos[position].child();
editor.getContainer().build(target);
const target = this.silos[position].child().getHTMLElement();
target.appendChild(editor.getContainer());
// Adjust layout according to provided ratios (used when restoring multiple editors at once)
if (ratio && (ratio.length === 2 || ratio.length === 3)) {
@@ -442,7 +444,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
// Show editor container
editor.getContainer().show();
DOM.show(editor.getContainer());
}
private getVisibleEditorCount(): number {
@@ -554,7 +556,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.clearPosition(position);
// Take editor container offdom and hide
editor.getContainer().offDOM().hide();
const editorContainer = editor.getContainer();
if (editorContainer.parentNode) {
editorContainer.parentNode.removeChild(editorContainer);
}
DOM.hide(editorContainer);
// Adjust layout and rochade if instructed to do so
if (layoutAndRochade) {
@@ -780,10 +786,10 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.layoutVertically = (orientation !== 'horizontal');
// Editor Layout
const verticalLayouting = this.parent.hasClass('vertical-layout');
const verticalLayouting = DOM.hasClass(this.parent, 'vertical-layout');
if (verticalLayouting !== this.layoutVertically) {
this.parent.removeClass('vertical-layout', 'horizontal-layout');
this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
DOM.removeClasses(this.parent, 'vertical-layout', 'horizontal-layout');
DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
this.sashOne.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
this.sashTwo.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
@@ -968,16 +974,16 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private create(): void {
// Store layout as class property
this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
// Allow to drop into container to open
this.enableDropTarget(this.parent.getHTMLElement());
this.enableDropTarget(this.parent);
// Silo One
this.silos[Position.ONE] = $(this.parent).div({ class: 'one-editor-silo editor-one' });
// Sash One
this.sashOne = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.sashOne = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.toUnbind.push(this.sashOne.onDidStart(() => this.onSashOneDragStart()));
this.toUnbind.push(this.sashOne.onDidChange((e: ISashEvent) => this.onSashOneDrag(e)));
this.toUnbind.push(this.sashOne.onDidEnd(() => this.onSashOneDragEnd()));
@@ -988,7 +994,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.silos[Position.TWO] = $(this.parent).div({ class: 'one-editor-silo editor-two' });
// Sash Two
this.sashTwo = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.sashTwo = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.toUnbind.push(this.sashTwo.onDidStart(() => this.onSashTwoDragStart()));
this.toUnbind.push(this.sashTwo.onDidChange((e: ISashEvent) => this.onSashTwoDrag(e)));
this.toUnbind.push(this.sashTwo.onDidEnd(() => this.onSashTwoDragEnd()));
@@ -1033,9 +1039,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.createTitleControl(this.stacks.groupAt(position), silo, titleContainer, instantiationService);
// Progress Bar
const progressBar = new ProgressBar($(container));
const progressBar = new ProgressBar(container.getHTMLElement());
this.toUnbind.push(attachProgressBarStyler(progressBar, this.themeService));
progressBar.getContainer().hide();
progressBar.hide();
container.setProperty(EditorGroupsControl.PROGRESS_BAR_CONTROL_KEY, progressBar); // associate with container
// Sash for first position to support centered editor layout
@@ -1059,11 +1065,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.centeredEditorLeftMarginRatio = 0.5;
// Restore centered layout position and size
const centeredLayoutDataString = this.storageServise.get(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
const centeredLayoutDataString = this.storageService.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;
this.centeredEditorPreferredSize = centeredLayout.size;
}
}
}
@@ -1284,7 +1290,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
if (!overlay) {
const containers = $this.visibleEditors.filter(e => !!e).map(e => e.getContainer());
containers.forEach((container, index) => {
if (container && DOM.isAncestor(target, container.getHTMLElement())) {
if (container && DOM.isAncestor(target, container)) {
const activeContrastBorderColor = $this.getColor(activeContrastBorder);
overlay = $('div').style({
top: $this.tabOptions.showTabs ? `${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px` : 0,
@@ -1596,8 +1602,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
// TODO@Ben remove me after a while
/* __GDPR__
"editorGroupMoved" : {
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('editorGroupMoved', { source: position, to: moveTo });
@@ -1627,11 +1633,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
let borderColor = null;
if (isDragging) {
this.parent.addClass('dragging');
DOM.addClass(this.parent, 'dragging');
silo.addClass('dragging');
borderColor = this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder);
} else {
this.parent.removeClass('dragging');
DOM.removeClass(this.parent, 'dragging');
silo.removeClass('dragging');
}
@@ -1930,11 +1936,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
private get centeredEditorAvailableSize(): number {
return this.silosSize[Position.ONE] - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2;
return this.dimension.width - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2;
}
private get centeredEditorSize(): number {
return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferedSize);
return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferredSize);
}
private get centeredEditorPosition(): number {
@@ -1956,7 +1962,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
if (size > 3 * this.minSize && size < this.centeredEditorAvailableSize) {
this.centeredEditorPreferedSize = size;
this.centeredEditorPreferredSize = size;
position -= EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN;
position = Math.min(position, this.centeredEditorAvailableSize - this.centeredEditorSize);
position = Math.max(0, position);
@@ -1971,7 +1977,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
leftMarginRatio: this.centeredEditorLeftMarginRatio,
size: this.centeredEditorSize
};
this.storageServise.store(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, JSON.stringify(data), StorageScope.GLOBAL);
this.storageService.store(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, JSON.stringify(data), StorageScope.GLOBAL);
}
public getVerticalSashTop(sash: Sash): number {
@@ -2013,17 +2019,17 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
return this.dragging;
}
public layout(dimension: Dimension): void;
public layout(dimension: DOM.Dimension): void;
public layout(position: Position): void;
public layout(arg: any): void {
if (arg instanceof Dimension) {
this.layoutControl(<Dimension>arg);
if (arg instanceof DOM.Dimension) {
this.layoutControl(<DOM.Dimension>arg);
} else {
this.layoutEditor(<Position>arg);
}
}
private layoutControl(dimension: Dimension): void {
private layoutControl(dimension: DOM.Dimension): void {
let oldDimension = this.dimension;
this.dimension = dimension;
@@ -2141,14 +2147,14 @@ 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;
const doCentering = this.partService.isEditorLayoutCentered() && this.stacks.groups.length === 1 &&
this.visibleEditors[Position.ONE] && this.visibleEditors[Position.ONE].supportsCenteredLayout();
if (doCentering && !this.centeredEditorActive) {
this.centeredEditorSashLeft.show();
this.centeredEditorSashRight.show();
// no size set yet. Calculate a default value
if (!this.centeredEditorPreferedSize) {
if (!this.centeredEditorPreferredSize) {
this.resetCenteredEditor(false);
}
} else if (!doCentering && this.centeredEditorActive) {
@@ -2169,16 +2175,18 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
});
// Layout title controls
POSITIONS.forEach(position => {
const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;
this.getTitleAreaControl(position).layout(new Dimension(siloWidth, EditorGroupsControl.EDITOR_TITLE_HEIGHT));
});
POSITIONS.forEach(position => this.layoutTitleControl(position));
// Update minimized state
this.updateMinimizedState();
}
private layoutTitleControl(position: Position): void {
const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;
this.getTitleAreaControl(position).layout(new DOM.Dimension(siloWidth, EditorGroupsControl.EDITOR_TITLE_HEIGHT));
}
private layoutEditor(position: Position): void {
const editorSize = this.silosSize[position];
const editor = this.visibleEditors[position];
@@ -2201,20 +2209,20 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
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));
editorContainer.style.marginLeft = this.centeredEditorActive ? `${editorPosition}px` : null;
editorContainer.style.width = this.centeredEditorActive ? `${editorWidth}px` : null;
editorContainer.style.borderColor = this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null;
editor.layout(new DOM.Dimension(editorWidth, editorHeight));
}
}
private resetCenteredEditor(layout: boolean = true) {
this.centeredEditorLeftMarginRatio = 0.5;
this.centeredEditorPreferedSize = Math.floor(this.dimension.width * EditorGroupsControl.GOLDEN_RATIO);
this.centeredEditorPreferredSize = Math.floor(this.dimension.width * EditorGroupsControl.GOLDEN_RATIO);
if (layout) {
this.layoutContainers();
}
this.storageServise.remove(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
this.storageService.remove(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
}
public getInstantiationService(position: Position): IInstantiationService {
@@ -2269,13 +2277,13 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
switch (state) {
case ProgressState.INFINITE:
progressbar.infinite().getContainer().show();
progressbar.infinite().show();
break;
case ProgressState.DONE:
progressbar.done().getContainer().hide();
progressbar.done().hide();
break;
case ProgressState.STOP:
progressbar.stop().getContainer().hide();
progressbar.stop().hide();
break;
}
}

View File

@@ -9,12 +9,11 @@ import 'vs/css!./media/editorpart';
import 'vs/workbench/browser/parts/editor/editor.contribution';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import nls = require('vs/nls');
import strings = require('vs/base/common/strings');
import arrays = require('vs/base/common/arrays');
import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import * as nls from 'vs/nls';
import * as strings from 'vs/base/common/strings';
import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
import * as objects from 'vs/base/common/objects';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { toErrorMessage } from 'vs/base/common/errorMessage';
@@ -35,20 +34,19 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
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';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter, once } from 'vs/base/common/event';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
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 } from 'vs/base/browser/dom';
import { createCSSRule, Dimension, addClass, removeClass } 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';
import { dispose } from 'vs/base/common/lifecycle';
// {{SQL CARBON EDIT}}
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
@@ -107,16 +105,17 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
private forceHideTabs: boolean;
private doNotFireTabOptionsChanged: boolean;
private revealIfOpen: boolean;
private _onEditorsChanged: ThrottledEmitter<void>;
private _onEditorOpening: Emitter<IEditorOpeningEvent>;
private _onEditorGroupMoved: Emitter<void>;
private _onEditorOpenFail: Emitter<EditorInput>;
private _onGroupOrientationChanged: Emitter<void>;
private _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private ignoreOpenEditorErrors: boolean;
private textCompareEditorVisible: IContextKey<boolean>;
private readonly _onEditorsChanged: ThrottledEmitter<void>;
private readonly _onEditorOpening: Emitter<IEditorOpeningEvent>;
private readonly _onEditorGroupMoved: Emitter<void>;
private readonly _onEditorOpenFail: Emitter<EditorInput>;
private readonly _onGroupOrientationChanged: Emitter<void>;
private readonly _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private readonly _onLayout: Emitter<Dimension>;
// The following data structures are partitioned into array of Position as provided by Services.POSITION array
private visibleEditors: BaseEditor[];
private instantiatedEditors: BaseEditor[][];
@@ -124,9 +123,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
private pendingEditorInputsToClose: EditorIdentifier[];
private pendingEditorInputCloseTimeout: number;
private onLayoutEmitter = new Emitter<Dimension>();
public onLayout = this.onLayoutEmitter.event;
constructor(
id: string,
restoreFromStorage: boolean,
@@ -148,6 +144,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this._onEditorOpenFail = new Emitter<EditorInput>();
this._onGroupOrientationChanged = new Emitter<void>();
this._onTabOptionsChanged = new Emitter<IEditorTabOptions>();
this._onLayout = new Emitter<Dimension>();
this.visibleEditors = [];
@@ -298,6 +295,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.editorGroupsControl.resizeGroup(position, groupSizeChange);
}
public get onLayout(): Event<Dimension> {
return this._onLayout.event;
}
public get onEditorsChanged(): Event<void> {
return this._onEditorsChanged.event;
}
@@ -479,11 +480,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Create editor as needed
if (!editor.getContainer()) {
editor.create($().div({
'class': 'editor-container',
'role': 'tabpanel',
id: descriptor.getId()
}));
const editorContainer = document.createElement('div');
editorContainer.id = descriptor.getId();
addClass(editorContainer, 'editor-container');
editorContainer.setAttribute('role', 'tabpanel');
editor.create(editorContainer);
}
return editor;
@@ -555,18 +557,21 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// 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(error)) {
// Report error only if this was not us restoring previous error state or
// we are told to ignore errors that occur from opening an editor
if (this.partService.isCreated() && !errors.isPromiseCanceledError(error) && !this.ignoreOpenEditorErrors) {
const actions: INotificationActions = { primary: [] };
if (isErrorWithActions(error)) {
actions.primary = error.actions;
if (errors.isErrorWithActions(error)) {
actions.primary = (error as errors.IErrorWithActions).actions;
}
this.notificationService.notify({
const handle = this.notificationService.notify({
severity: Severity.Error,
message: nls.localize('editorOpenError', "Unable to open '{0}': {1}.", input.getName(), toErrorMessage(error)),
actions
});
once(handle.onDidClose)(() => dispose(actions.primary));
}
this.editorGroupsControl.updateProgress(position, ProgressState.DONE);
@@ -576,7 +581,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Recover by closing the active editor (if the input is still the active one)
if (group.activeEditor === input) {
this.doCloseActiveEditor(group, !(options && options.preserveFocus) /* still preserve focus as needed */);
this.doCloseActiveEditor(group, !(options && options.preserveFocus) /* still preserve focus as needed */, true /* from error */);
}
}
@@ -610,7 +615,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
}
private doCloseActiveEditor(group: EditorGroup, focusNext = true): void {
private doCloseActiveEditor(group: EditorGroup, focusNext = true, fromError?: boolean): void {
const position = this.stacks.positionOfGroup(group);
// Update stacks model
@@ -623,7 +628,22 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Otherwise open next active
else {
this.openEditor(group.activeEditor, !focusNext ? EditorOptions.create({ preserveFocus: true }) : null, position).done(null, errors.onUnexpectedError);
// When closing an editor due to an error we can end up in a loop where we continue closing
// editors that fail to open (e.g. when the file no longer exists). We do not want to show
// repeated errors in this case to the user. As such, if we open the next editor and we are
// in a scope of a previous editor failing, we silence the input errors until the editor is
// opened.
if (fromError) {
this.ignoreOpenEditorErrors = true;
}
this.openEditor(group.activeEditor, !focusNext ? EditorOptions.create({ preserveFocus: true }) : null, position).done(() => {
this.ignoreOpenEditorErrors = false;
}, error => {
errors.onUnexpectedError(error);
this.ignoreOpenEditorErrors = false;
});
}
}
@@ -710,7 +730,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
// Then check for array of positions to close
if (Array.isArray(positionsOrEditors) || isUndefinedOrNull(positionsOrEditors)) {
if (Array.isArray(positionsOrEditors) || types.isUndefinedOrNull(positionsOrEditors)) {
return this.doCloseAllEditorsAtPositions(positionsOrEditors as Position[]);
}
@@ -833,16 +853,18 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Close: By Filter or all
else {
editorsToClose = group.getEditors(true /* in MRU order */);
filter = filterOrEditors || Object.create(null);
const hasDirection = !types.isUndefinedOrNull(filter.direction);
editorsToClose = group.getEditors(!hasDirection /* in MRU order only if direction is not specified */);
// Filter: saved only
if (filter.savedOnly) {
editorsToClose = editorsToClose.filter(e => !e.isDirty());
}
// Filter: direction (left / right)
else if (!types.isUndefinedOrNull(filter.direction)) {
else if (hasDirection) {
editorsToClose = (filter.direction === Direction.LEFT) ? editorsToClose.slice(0, group.indexOf(filter.except)) : editorsToClose.slice(group.indexOf(filter.except) + 1);
}
@@ -1141,12 +1163,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return this.editorGroupsControl.getGroupOrientation();
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
// Content Container
const contentArea = $(parent)
.div()
.addClass('content');
const contentArea = document.createElement('div');
addClass(contentArea, 'content');
parent.appendChild(contentArea);
// get settings
this.memento = this.getMemento(this.storageService, MementoScope.WORKSPACE);
@@ -1164,19 +1186,19 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Part container
const container = this.getContainer();
container.style('background-color', this.getColor(editorBackground));
container.style.backgroundColor = this.getColor(editorBackground);
// Content area
const content = this.getContentArea();
const groupCount = this.stacks.groups.length;
if (groupCount > 1) {
content.addClass('multiple-groups');
addClass(content, 'multiple-groups');
} else {
content.removeClass('multiple-groups');
removeClass(content, 'multiple-groups');
}
content.style('background-color', groupCount > 0 ? this.getColor(EDITOR_GROUP_BACKGROUND) : null);
content.style.backgroundColor = groupCount > 0 ? this.getColor(EDITOR_GROUP_BACKGROUND) : null;
}
private onGroupFocusChanged(): void {
@@ -1474,7 +1496,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.dimension = sizes[1];
this.editorGroupsControl.layout(this.dimension);
this.onLayoutEmitter.fire(dimension);
this._onLayout.fire(dimension);
return sizes;
}
@@ -1512,6 +1534,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this._onEditorOpening.dispose();
this._onEditorGroupMoved.dispose();
this._onEditorOpenFail.dispose();
this._onGroupOrientationChanged.dispose();
this._onTabOptionsChanged.dispose();
this._onLayout.dispose();
// Reset Tokens
this.editorOpenToken = [];

View File

@@ -6,9 +6,9 @@
import 'vs/css!./media/editorpicker';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
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';

View File

@@ -6,14 +6,14 @@
'use strict';
import 'vs/css!./media/editorstatus';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener } from 'vs/base/browser/dom';
import strings = require('vs/base/common/strings');
import paths = require('vs/base/common/paths');
import types = require('vs/base/common/types');
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener, getDomNodePagePosition } from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings';
import * as paths from 'vs/base/common/paths';
import * as types from 'vs/base/common/types';
import uri from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Action } from 'vs/base/common/actions';
import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common/platform';
@@ -50,35 +50,58 @@ 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, attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground, foreground, darken, contrastBorder } 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';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Themable } from 'vs/workbench/common/theme';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
// TODO@Sandeep layer breaker
// tslint:disable-next-line:import-patterns
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
class SideBySideEditorEncodingSupport implements IEncodingSupport {
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
public getEncoding(): string {
return this.master.getEncoding(); // always report from modified (right hand) side
}
public setEncoding(encoding: string, mode: EncodingMode): void {
[this.master, this.details].forEach(s => s.setEncoding(encoding, mode));
}
}
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/parts/query/services/queryEditorService';
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport {
if (input instanceof SideBySideEditorInput) {
input = input.master;
}
// Untitled Editor
if (input instanceof UntitledEditorInput) {
return input;
}
// Side by Side (diff) Editor
if (input instanceof SideBySideEditorInput) {
const masterEncodingSupport = toEditorWithEncodingSupport(input.master);
const detailsEncodingSupport = toEditorWithEncodingSupport(input.details);
if (masterEncodingSupport && detailsEncodingSupport) {
return new SideBySideEditorEncodingSupport(masterEncodingSupport, detailsEncodingSupport);
}
return masterEncodingSupport;
}
// File or Resource Editor
let encodingSupport = input as IFileEditorInput;
if (types.areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) {
return encodingSupport;
}
// Unsupported for any other editor
return null;
}
@@ -246,16 +269,16 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus");
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized");
const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\".");
function _setDisplay(el: HTMLElement, desiredValue: string): void {
function setDisplay(el: HTMLElement, desiredValue: string): void {
if (el.style.display !== desiredValue) {
el.style.display = desiredValue;
}
}
function show(el: HTMLElement): void {
_setDisplay(el, '');
setDisplay(el, '');
}
function hide(el: HTMLElement): void {
_setDisplay(el, 'none');
setDisplay(el, 'none');
}
export class EditorStatus implements IStatusbarItem {
@@ -274,7 +297,7 @@ export class EditorStatus implements IStatusbarItem {
private activeEditorListeners: IDisposable[];
private delayedRender: IDisposable;
private toRender: StateChange;
private lastScreenReaderExplanation: ScreenReaderDetectedExplanation;
private screenReaderExplanation: ScreenReaderDetectedExplanation;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@@ -289,7 +312,6 @@ export class EditorStatus implements IStatusbarItem {
this.toDispose = [];
this.activeEditorListeners = [];
this.state = new State();
this.lastScreenReaderExplanation = null;
}
public render(container: HTMLElement): IDisposable {
@@ -313,7 +335,7 @@ export class EditorStatus implements IStatusbarItem {
hide(this.selectionElement);
this.indentationElement = append(this.element, $('a.editor-status-indentation'));
this.indentationElement.title = nls.localize('indentation', "Indentation");
this.indentationElement.title = nls.localize('selectIndentation', "Select Indentation");
this.indentationElement.onclick = () => this.onIndentationClick();
hide(this.indentationElement);
@@ -487,7 +509,18 @@ export class EditorStatus implements IStatusbarItem {
}
private onScreenReaderModeClick(): void {
this.lastScreenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation, this.screenRedearModeElement);
const showExplanation = !this.screenReaderExplanation || !this.screenReaderExplanation.visible;
if (!this.screenReaderExplanation) {
this.screenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation);
this.toDispose.push(this.screenReaderExplanation);
}
if (showExplanation) {
this.screenReaderExplanation.show(this.screenRedearModeElement);
} else {
this.screenReaderExplanation.hide();
}
}
private onSelectionClick(): void {
@@ -652,9 +685,8 @@ export class EditorStatus implements IStatusbarItem {
screenReaderMode = (editorWidget.getConfiguration().accessibilitySupport === AccessibilitySupport.Enabled);
}
if (screenReaderMode === false && this.lastScreenReaderExplanation) {
this.lastScreenReaderExplanation.hide();
this.lastScreenReaderExplanation = null;
if (screenReaderMode === false && this.screenReaderExplanation && this.screenReaderExplanation.visible) {
this.screenReaderExplanation.hide();
}
this.updateState({ screenReaderMode: screenReaderMode });
@@ -1229,113 +1261,148 @@ export class ChangeEncodingAction extends Action {
}
}
class ScreenReaderDetectedExplanation {
private _isDisposed: boolean;
private _toDispose: IDisposable[];
class ScreenReaderDetectedExplanation extends Themable {
private container: HTMLElement;
private hrElement: HTMLHRElement;
private _visible: boolean;
constructor(
anchorElement: HTMLElement,
@IThemeService private readonly themeService: IThemeService,
@IThemeService themeService: IThemeService,
@IContextViewService private readonly contextViewService: IContextViewService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
) {
this._isDisposed = false;
this._toDispose = [];
super(themeService);
}
public get visible(): boolean {
return this._visible;
}
protected updateStyles(): void {
if (this.container) {
const background = this.getColor(editorWidgetBackground);
this.container.style.backgroundColor = background ? background.toString() : null;
const widgetShadowColor = this.getColor(widgetShadow);
this.container.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
const contrastBorderColor = this.getColor(contrastBorder);
this.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : null;
const foregroundColor = this.getColor(foreground);
this.hrElement.style.backgroundColor = foregroundColor ? foregroundColor.toString() : null;
}
}
public show(anchorElement: HTMLElement): void {
this._visible = true;
this.contextViewService.showContextView({
getAnchor: () => anchorElement,
getAnchor: () => {
const res = getDomNodePagePosition(anchorElement);
return {
x: res.left,
y: res.top - 9, /* above the status bar */
width: res.width,
height: res.height
} as IAnchor;
},
render: (container) => {
return this.renderContents(container);
},
onDOMEvent: (e, activeElement) => {
},
onDOMEvent: (e, activeElement) => { },
onHide: () => {
this.dispose();
this._visible = false;
}
});
}
public dispose(): void {
this._isDisposed = true;
this._toDispose = dispose(this._toDispose);
}
public hide(): void {
if (this._isDisposed) {
return;
}
this.contextViewService.hideContextView();
}
protected renderContents(container: HTMLElement): IDisposable {
const domNode = $('div.screen-reader-detected-explanation', {
protected renderContents(parent: HTMLElement): IDisposable {
const toDispose: IDisposable[] = [];
this.container = $('div.screen-reader-detected-explanation', {
'aria-hidden': 'true'
});
const title = $('h2.title', {}, nls.localize('screenReaderDetectedExplanation.title', "Screen Reader Optimized"));
domNode.appendChild(title);
this.container.appendChild(title);
const closeBtn = $('div.cancel');
this._toDispose.push(addDisposableListener(closeBtn, 'click', () => {
toDispose.push(addDisposableListener(closeBtn, 'click', () => {
this.contextViewService.hideContextView();
}));
domNode.appendChild(closeBtn);
toDispose.push(addDisposableListener(closeBtn, 'mouseover', () => {
const theme = this.themeService.getTheme();
let darkenFactor: number;
switch (theme.type) {
case 'light':
darkenFactor = 0.1;
break;
case 'dark':
darkenFactor = 0.2;
break;
}
if (darkenFactor) {
closeBtn.style.backgroundColor = this.getColor(editorWidgetBackground, (color, theme) => darken(color, darkenFactor)(theme));
}
}));
toDispose.push(addDisposableListener(closeBtn, 'mouseout', () => {
closeBtn.style.backgroundColor = null;
}));
this.container.appendChild(closeBtn);
// {{SQL CARBON EDIT}}
const question = $('p.question', {}, nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate SQL Operations Studio?"));
domNode.appendChild(question);
this.container.appendChild(question);
const buttonContainer = $('div.buttons');
domNode.appendChild(buttonContainer);
this.container.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 => {
toDispose.push(attachButtonStyler(yesBtn, this.themeService));
toDispose.push(yesBtn.onDidClick(e => {
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
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 => {
toDispose.push(attachButtonStyler(noBtn, this.themeService));
toDispose.push(noBtn.onDidClick(e => {
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
const clear = $('div');
clear.style.clear = 'both';
domNode.appendChild(clear);
this.container.appendChild(clear);
const br = $('br');
domNode.appendChild(br);
this.container.appendChild(br);
const hr = $('hr');
domNode.appendChild(hr);
this.hrElement = $('hr');
this.container.appendChild(this.hrElement);
// {{SQL CARBON EDIT}}
const explanation1 = $('p.body1', {}, nls.localize('screenReaderDetectedExplanation.body1', "SQL Operations Studio is now optimized for usage with a screen reader."));
domNode.appendChild(explanation1);
this.container.appendChild(explanation1);
const explanation2 = $('p.body2', {}, nls.localize('screenReaderDetectedExplanation.body2', "Some editor features will have different behaviour: e.g. word wrapping, folding, etc."));
domNode.appendChild(explanation2);
this.container.appendChild(explanation2);
container.appendChild(domNode);
parent.appendChild(this.container);
this._toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground }, colors => {
domNode.style.backgroundColor = colors.editorWidgetBackground;
if (colors.widgetShadow) {
domNode.style.boxShadow = `0 5px 8px ${colors.widgetShadow}`;
}
}));
this.updateStyles();
return {
dispose: () => { this.dispose(); }
dispose: () => dispose(toDispose)
};
}
}

View File

@@ -3,6 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry.editor-preview {
.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry.editor-preview {
font-style: italic;
}

View File

@@ -22,7 +22,6 @@
cursor: default !important;
}
.monaco-shell .screen-reader-detected-explanation {
width: 420px;
top: 30px;
@@ -53,8 +52,9 @@
font-size: 1.2em;
}
.monaco-shell .screen-reader-detected-explanation p.question {
font-size: 1.4em;
.monaco-shell .screen-reader-detected-explanation hr {
border: 0;
height: 2px;
}
.monaco-shell .screen-reader-detected-explanation .buttons {
@@ -72,21 +72,8 @@
.monaco-shell.vs .screen-reader-detected-explanation .cancel {
background: url('close-big.svg') center center no-repeat;
}
.monaco-shell.vs .screen-reader-detected-explanation .cancel:hover {
background-color: #eaeaea;
}
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel,
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
background: url('close-big-dark.svg') center center no-repeat;
}
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel:hover {
background-color: rgba(30,30,30,0.8);
}
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
opacity: 0.6;
}
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel:hover {
opacity: 1;
}
}

View File

@@ -54,8 +54,9 @@
cursor: zoom-out;
}
.monaco-resource-viewer .open-external,
.monaco-resource-viewer .open-external:hover {
.monaco-resource-viewer .embedded-link,
.monaco-resource-viewer .embedded-link:hover {
cursor: pointer;
text-decoration: underline;
margin-left: 5px;
}

View File

@@ -62,15 +62,24 @@
max-width: fit-content;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after {
content: "";
display: flex;
flex: 0;
width: 5px; /* Reserve space to hide tab fade when close button is left or off (fixes https://github.com/Microsoft/vscode/issues/45728) */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-left {
min-width: 80px; /* make more room for close button when it shows to the left */
padding-right: 5px; /* we need less room when sizing is shrink */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged {
will-change: transform; /* forces tab to be drawn on a separate layer (fixes https://github.com/Microsoft/vscode/issues/18733) */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged-over * {
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged-over div {
pointer-events: none; /* prevents cursor flickering (fixes https://github.com/Microsoft/vscode/issues/38753) */
}
@@ -190,6 +199,10 @@
padding-right: 10px; /* give a little bit more room if close button is off */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-off {
padding-right: 5px; /* we need less room when sizing is shrink */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty {
background-repeat: no-repeat;
background-position-y: center;

View File

@@ -6,9 +6,9 @@
'use strict';
import 'vs/css!./media/notabstitle';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
import { toResource } from 'vs/workbench/common/editor';
import DOM = require('vs/base/browser/dom');
import * as DOM from 'vs/base/browser/dom';
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { Verbosity } from 'vs/platform/editor/common/editor';
@@ -81,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({ groupId: group.id, editorIndex: group.indexOf(group.activeEditor) }).done(null, errors.onUnexpectedError);
this.closeOneEditorAction.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 as GestureEvent).initialTarget || 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())) {
this.editorGroupService.focusGroup(group);
}
}

View File

@@ -5,7 +5,7 @@
import { IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
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';
@@ -25,10 +25,10 @@ export class RangeHighlightDecorations implements IDisposable {
private editor: ICodeEditor = null;
private editorDisposables: IDisposable[] = [];
private _onHighlightRemoved: Emitter<void> = new Emitter<void>();
private readonly _onHighlightRemoved: Emitter<void> = new Emitter<void>();
public readonly onHighlghtRemoved: Event<void> = this._onHighlightRemoved.event;
constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService) {
constructor(@IWorkbenchEditorService private editorService: IWorkbenchEditorService) {
}
public removeHighlightRange() {

View File

@@ -6,12 +6,12 @@
'use strict';
import 'vs/css!./media/resourceviewer';
import nls = require('vs/nls');
import mimes = require('vs/base/common/mime');
import * as nls from 'vs/nls';
import * as mimes from '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 * as paths from 'vs/base/common/paths';
import { Builder, $ } from 'vs/base/browser/builder';
import * as DOM from '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';
@@ -19,7 +19,6 @@ 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';
@@ -119,7 +118,7 @@ class BinarySize {
}
export interface ResourceViewerContext {
layout(dimension: Dimension): void;
layout(dimension: DOM.Dimension): void;
}
/**
@@ -127,26 +126,42 @@ export interface ResourceViewerContext {
* progress of the binary resource.
*/
export class ResourceViewer {
private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally
public static show(
descriptor: IResourceDescriptor,
container: Builder,
container: HTMLElement,
scrollbar: DomScrollableElement,
openExternal: (uri: URI) => void,
openInternalClb: (uri: URI) => void,
openExternalClb: (uri: URI) => void,
metadataClb: (meta: string) => void
): ResourceViewerContext {
): ResourceViewerContext | null {
// Ensure CSS class
$(container).setClass('monaco-resource-viewer');
// Images
if (ResourceViewer.isImageResource(descriptor)) {
return ImageView.create(container, descriptor, scrollbar, openExternal, metadataClb);
return ImageView.create(container, descriptor, scrollbar, openExternalClb, metadataClb);
}
// Large Files
if (descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) {
FileTooLargeFileView.create(container, descriptor, scrollbar, metadataClb);
}
// Seemingly Binary Files
else {
FileSeemsBinaryFileView.create(container, descriptor, scrollbar, openInternalClb, metadataClb);
}
GenericBinaryFileView.create(container, metadataClb, descriptor, scrollbar);
return null;
}
private static isImageResource(descriptor: IResourceDescriptor) {
const mime = ResourceViewer.getMime(descriptor);
return mime.indexOf('image/') >= 0;
}
@@ -158,6 +173,7 @@ export class ResourceViewer {
mime = mapExtToMediaMimes[ext.toLowerCase()];
}
}
return mime || mimes.MIME_BINARY;
}
}
@@ -167,17 +183,18 @@ class ImageView {
private static readonly BASE64_MARKER = 'base64,';
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
openExternal: (uri: URI) => void,
openExternalClb: (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);
LargeImageView.create(container, descriptor, openExternalClb);
return null;
}
@@ -203,43 +220,81 @@ class ImageView {
class LargeImageView {
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
openExternal: (uri: URI) => void
openExternalClb: (uri: URI) => void
) {
const size = BinarySize.formatSize(descriptor.size);
const imageContainer = $(container)
.empty()
.p({
text: nls.localize('largeImageError', "The file size of the image is too large (>1MB) to display in the editor. ")
text: nls.localize('largeImageError', "The image is not displayed in the editor because it is too large ({0}).", size)
});
if (descriptor.resource.scheme !== Schemas.data) {
imageContainer.append($('a', {
role: 'button',
class: 'open-external',
class: 'embedded-link',
text: nls.localize('resourceOpenExternalButton', "Open image using external program?")
}).on(DOM.EventType.CLICK, (e) => {
openExternal(descriptor.resource);
openExternalClb(descriptor.resource);
}));
}
}
}
class GenericBinaryFileView {
class FileTooLargeFileView {
public static create(
container: Builder,
metadataClb: (meta: string) => void,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement
scrollbar: DomScrollableElement,
metadataClb: (meta: string) => void
) {
const size = BinarySize.formatSize(descriptor.size);
$(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.")
text: nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size)
});
if (metadataClb) {
metadataClb(size);
}
scrollbar.scanDomNode();
}
}
class FileSeemsBinaryFileView {
public static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
openInternalClb: (uri: URI) => void,
metadataClb: (meta: string) => void
) {
const binaryContainer = $(container)
.empty()
.p({
text: nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding.")
});
if (descriptor.resource.scheme !== Schemas.data) {
binaryContainer.append($('a', {
role: 'button',
class: 'embedded-link',
text: nls.localize('openAsText', "Do you want to open it anyway?")
}).on(DOM.EventType.CLICK, (e) => {
openInternalClb(descriptor.resource);
}));
}
if (metadataClb) {
metadataClb(BinarySize.formatSize(descriptor.size));
}
scrollbar.scanDomNode();
}
}
@@ -266,7 +321,7 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
private onEditorsChanged(): void {
this.hide();
this.onSelectScale = undefined;
this.onSelectScale = void 0;
}
public show(scale: Scale, onSelectScale: (scale: number) => void) {
@@ -295,6 +350,7 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
.getHTMLElement();
this.statusBarItem.style.display = 'none';
}
return this;
}
@@ -306,10 +362,11 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
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, () => {
new Action(`zoom.${scale}`, ZoomStatusbarItem.zoomLabel(scale), void 0, void 0, () => {
if (this.onSelectScale) {
this.onSelectScale(scale);
}
return null;
}));
}
@@ -376,13 +433,13 @@ class InlineImageView {
private static readonly imageStateCache = new LRUCache<string, ImageState>(100);
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
metadataClb: (meta: string) => void
) {
const context = {
layout(dimension: Dimension) { }
layout(dimension: DOM.Dimension) { }
};
const cacheKey = descriptor.resource.toString();
@@ -585,6 +642,4 @@ class InlineImageView {
return cached.src;
}
}
}

View File

@@ -5,14 +5,11 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as DOM from 'vs/base/browser/dom';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorInput, EditorOptions, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorControl, Position, IEditor } from 'vs/platform/editor/common/editor';
import { VSash } from 'vs/base/browser/ui/sash/sash';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -23,7 +20,7 @@ export class SideBySideEditor extends BaseEditor {
public static readonly ID: string = 'workbench.editor.sidebysideEditor';
private dimension: Dimension;
private dimension: DOM.Dimension;
protected masterEditor: BaseEditor;
private masterEditorContainer: HTMLElement;
@@ -41,10 +38,9 @@ export class SideBySideEditor extends BaseEditor {
super(SideBySideEditor.ID, telemetryService, themeService);
}
protected createEditor(parent: Builder): void {
const parentElement = parent.getHTMLElement();
DOM.addClass(parentElement, 'side-by-side-editor');
this.createSash(parentElement);
protected createEditor(parent: HTMLElement): void {
DOM.addClass(parent, 'side-by-side-editor');
this.createSash(parent);
}
public setInput(newInput: SideBySideEditorInput, options?: EditorOptions): TPromise<void> {
@@ -90,7 +86,7 @@ export class SideBySideEditor extends BaseEditor {
}
}
public layout(dimension: Dimension): void {
public layout(dimension: DOM.Dimension): void {
this.dimension = dimension;
this.sash.setDimenesion(this.dimension);
}
@@ -110,6 +106,10 @@ export class SideBySideEditor extends BaseEditor {
return this.detailsEditor;
}
public supportsCenteredLayout(): boolean {
return false;
}
private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options?: EditorOptions): void {
if (!newInput.matches(oldInput)) {
if (oldInput) {
@@ -137,7 +137,7 @@ export class SideBySideEditor extends BaseEditor {
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editorInput);
const editor = descriptor.instantiate(this.instantiationService);
editor.create(new Builder(container));
editor.create(container);
editor.setVisible(this.isVisible(), this.position);
return editor;
@@ -151,7 +151,7 @@ export class SideBySideEditor extends BaseEditor {
}
private createEditorContainers(): void {
const parentElement = this.getContainer().getHTMLElement();
const parentElement = this.getContainer();
this.detailsEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container'));
this.detailsEditorContainer.style.position = 'absolute';
this.masterEditorContainer = DOM.append(parentElement, DOM.$('.master-editor-container'));
@@ -188,12 +188,12 @@ export class SideBySideEditor extends BaseEditor {
this.masterEditorContainer.style.height = `${this.dimension.height}px`;
this.masterEditorContainer.style.left = `${splitPoint}px`;
this.detailsEditor.layout(new Dimension(detailsEditorWidth, this.dimension.height));
this.masterEditor.layout(new Dimension(masterEditorWidth, this.dimension.height));
this.detailsEditor.layout(new DOM.Dimension(detailsEditorWidth, this.dimension.height));
this.masterEditor.layout(new DOM.Dimension(masterEditorWidth, this.dimension.height));
}
private disposeEditors(): void {
const parentContainer = this.getContainer().getHTMLElement();
const parentContainer = this.getContainer();
if (this.detailsEditor) {
this.detailsEditor.dispose();
this.detailsEditor = null;
@@ -216,4 +216,4 @@ export class SideBySideEditor extends BaseEditor {
this.disposeEditors();
super.dispose();
}
}
}

View File

@@ -6,10 +6,10 @@
'use strict';
import 'vs/css!./media/tabstitle';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import errors = require('vs/base/common/errors');
import DOM = require('vs/base/browser/dom');
import * as errors from 'vs/base/common/errors';
import * as DOM from 'vs/base/browser/dom';
import { isMacintosh } from 'vs/base/common/platform';
import { shorten } from 'vs/base/common/labels';
import { ActionRunner, IAction } from 'vs/base/common/actions';
@@ -38,8 +38,6 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
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, 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';
@@ -72,7 +70,7 @@ export class TabsTitleControl extends TitleControl {
private scrollbar: ScrollableElement;
private tabDisposeables: IDisposable[];
private blockRevealActiveTab: boolean;
private dimension: Dimension;
private dimension: DOM.Dimension;
private layoutScheduled: IDisposable;
private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
@@ -576,7 +574,7 @@ export class TabsTitleControl extends TitleControl {
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) });
bar.push(this.closeOneEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.closeOneEditorAction) });
// Eventing
const disposable = this.hookTabListeners(tabContainer, index);
@@ -586,7 +584,7 @@ export class TabsTitleControl extends TitleControl {
return tabContainer;
}
public layout(dimension: Dimension): void {
public layout(dimension: DOM.Dimension): void {
if (!this.activeTab || !dimension) {
return;
}
@@ -597,14 +595,14 @@ export class TabsTitleControl extends TitleControl {
// that can result in the browser doing a full page layout to validate them. To buffer
// this a little bit we try at least to schedule this work on the next animation frame.
if (!this.layoutScheduled) {
this.layoutScheduled = scheduleAtNextAnimationFrame(() => {
this.layoutScheduled = DOM.scheduleAtNextAnimationFrame(() => {
this.doLayout(this.dimension);
this.layoutScheduled = void 0;
});
}
}
private doLayout(dimension: Dimension): void {
private doLayout(dimension: DOM.Dimension): void {
const visibleContainerWidth = this.tabsContainer.offsetWidth;
const totalContainerWidth = this.tabsContainer.scrollWidth;
@@ -695,7 +693,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({ groupId: this.context.id, editorIndex: index }).done(null, errors.onUnexpectedError);
this.closeOneEditorAction.run({ groupId: this.context.id, editorIndex: index }).done(null, errors.onUnexpectedError);
}
}));
@@ -789,7 +787,7 @@ export class TabsTitleControl extends TitleControl {
// Fixes https://github.com/Microsoft/vscode/issues/18733
DOM.addClass(tab, 'dragged');
scheduleAtNextAnimationFrame(() => DOM.removeClass(tab, 'dragged'));
DOM.scheduleAtNextAnimationFrame(() => DOM.removeClass(tab, 'dragged'));
}));
// We need to keep track of DRAG_ENTER and DRAG_LEAVE events because a tab is not just a div without children,
@@ -977,7 +975,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
if (tabHoverBackground) {
collector.addRule(`
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover {
background: ${tabHoverBackground} !important;
background-color: ${tabHoverBackground} !important;
}
`);
}
@@ -986,7 +984,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
if (tabUnfocusedHoverBackground) {
collector.addRule(`
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.inactive .tabs-container > .tab:hover {
background: ${tabUnfocusedHoverBackground} !important;
background-color: ${tabUnfocusedHoverBackground} !important;
}
`);
}

View File

@@ -7,12 +7,11 @@
import 'vs/css!./media/textdiffeditor';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import objects = require('vs/base/common/objects');
import { Builder } from 'vs/base/browser/builder';
import * as nls from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { Action, IAction } from 'vs/base/common/actions';
import { onUnexpectedError } from 'vs/base/common/errors';
import types = require('vs/base/common/types');
import * as types from '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';
@@ -32,10 +31,13 @@ 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 { ScrollType } from 'vs/editor/common/editorCommon';
import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/common/editorCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import URI from 'vs/base/common/uri';
import { getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
import { once } from 'vs/base/common/event';
/**
* The text editor that leverages the diff text editor for the editing experience.
@@ -45,10 +47,10 @@ export class TextDiffEditor extends BaseTextEditor {
public static readonly ID = TEXT_DIFF_EDITOR_ID;
private diffNavigator: DiffNavigator;
private diffNavigatorDisposables: IDisposable[];
private nextDiffAction: NavigateAction;
private previousDiffAction: NavigateAction;
private toggleIgnoreTrimWhitespaceAction: ToggleIgnoreTrimWhitespaceAction;
private _configurationListener: IDisposable;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -63,11 +65,12 @@ export class TextDiffEditor extends BaseTextEditor {
) {
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
this._configurationListener = this._actualConfigurationService.onDidChangeConfiguration((e) => {
this.diffNavigatorDisposables = [];
this.toUnbind.push(this._actualConfigurationService.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('diffEditor.ignoreTrimWhitespace')) {
this.updateIgnoreTrimWhitespaceAction();
}
});
}));
}
public getTitle(): string {
@@ -78,7 +81,7 @@ export class TextDiffEditor extends BaseTextEditor {
return nls.localize('textDiffEditor', "Text Diff Editor");
}
public createEditorControl(parent: Builder, configuration: IEditorOptions): IDiffEditor {
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IDiffEditor {
// Actions
this.nextDiffAction = new NavigateAction(this, true);
@@ -118,7 +121,7 @@ export class TextDiffEditor extends BaseTextEditor {
// Create a special child of instantiator that will delegate all calls to openEditor() to the same diff editor if the input matches with the modified one
const diffEditorInstantiator = this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService]));
return diffEditorInstantiator.createInstance(DiffEditorWidget, parent.getHTMLElement(), configuration);
return diffEditorInstantiator.createInstance(DiffEditorWidget, parent, configuration);
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -137,9 +140,10 @@ export class TextDiffEditor extends BaseTextEditor {
}
// Dispose previous diff navigator
if (this.diffNavigator) {
this.diffNavigator.dispose();
}
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
// Remember view settings if input changes
this.saveTextDiffEditorViewState(this.input);
// Set input and resolve
return super.setInput(input, options).then(() => {
@@ -155,27 +159,32 @@ export class TextDiffEditor extends BaseTextEditor {
return null;
}
// Editor
// Set Editor Model
const diffEditor = <IDiffEditor>this.getControl();
diffEditor.setModel((<TextDiffEditorModel>resolvedModel).textDiffEditorModel);
// Handle TextOptions
let alwaysRevealFirst = true;
// Apply Options from TextOptions
let optionsGotApplied = false;
if (options && types.isFunction((<TextEditorOptions>options).apply)) {
const hadOptions = (<TextEditorOptions>options).apply(<IDiffEditor>diffEditor, ScrollType.Immediate);
if (hadOptions) {
alwaysRevealFirst = false; // Do not reveal if we are instructed to open specific line/col
}
optionsGotApplied = (<TextEditorOptions>options).apply(<IDiffEditor>diffEditor, ScrollType.Immediate);
}
// Otherwise restore View State
let hasPreviousViewState = false;
if (!optionsGotApplied) {
hasPreviousViewState = this.restoreTextDiffEditorViewState(input);
}
// Listen on diff updated changes to reveal the first change
this.diffNavigator = new DiffNavigator(diffEditor, {
alwaysRevealFirst
alwaysRevealFirst: !optionsGotApplied && !hasPreviousViewState // only reveal first change if we had no options or viewstate
});
this.diffNavigator.onDidUpdate(() => {
this.diffNavigatorDisposables.push(this.diffNavigator);
this.diffNavigatorDisposables.push(this.diffNavigator.onDidUpdate(() => {
this.nextDiffAction.updateEnablement();
this.previousDiffAction.updateEnablement();
});
}));
this.updateIgnoreTrimWhitespaceAction();
}, error => {
@@ -190,6 +199,26 @@ export class TextDiffEditor extends BaseTextEditor {
});
}
public supportsCenteredLayout(): boolean {
return false;
}
private restoreTextDiffEditorViewState(input: EditorInput): boolean {
if (input instanceof DiffEditorInput) {
const resource = this.toDiffEditorViewStateResource(input);
if (resource) {
const viewState = this.loadTextEditorViewState(resource);
if (viewState) {
this.getControl().restoreViewState(viewState);
return true;
}
}
}
return false;
}
private updateIgnoreTrimWhitespaceAction(): void {
const ignoreTrimWhitespace = this.configurationService.getValue<boolean>(this.getResource(), 'diffEditor.ignoreTrimWhitespace');
if (this.toggleIgnoreTrimWhitespaceAction) {
@@ -278,9 +307,10 @@ export class TextDiffEditor extends BaseTextEditor {
public clearInput(): void {
// Dispose previous diff navigator
if (this.diffNavigator) {
this.diffNavigator.dispose();
}
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
// Keep editor view state in settings to restore when coming back
this.saveTextDiffEditorViewState(this.input);
// Clear Model
this.getControl().setModel(null);
@@ -305,14 +335,85 @@ export class TextDiffEditor extends BaseTextEditor {
return super.getControl() as IDiffEditor;
}
public dispose(): void {
protected loadTextEditorViewState(resource: URI): IDiffEditorViewState {
return super.loadTextEditorViewState(resource) as IDiffEditorViewState; // overridden for text diff editor support
}
// Dispose previous diff navigator
if (this.diffNavigator) {
this.diffNavigator.dispose();
private saveTextDiffEditorViewState(input: EditorInput): void {
if (!(input instanceof DiffEditorInput)) {
return; // only supported for diff editor inputs
}
this._configurationListener.dispose();
const resource = this.toDiffEditorViewStateResource(input);
if (!resource) {
return; // unable to retrieve input resource
}
// Clear view state if input is disposed
if (input.isDisposed()) {
super.clearTextEditorViewState([resource]);
}
// Otherwise save it
else {
super.saveTextEditorViewState(resource);
// Make sure to clean up when the input gets disposed
once(input.onDispose)(() => {
super.clearTextEditorViewState([resource]);
});
}
}
protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState {
return this.retrieveTextDiffEditorViewState(resource); // overridden for text diff editor support
}
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState {
const editor = getCodeOrDiffEditor(this).diffEditor;
if (!editor) {
return null; // not supported for non-diff editors
}
const model = editor.getModel();
if (!model || !model.modified || !model.original) {
return null; // view state always needs a model
}
const modelUri = this.toDiffEditorViewStateResource(model);
if (!modelUri) {
return null; // model URI is needed to make sure we save the view state correctly
}
if (modelUri.toString() !== resource.toString()) {
return null; // prevent saving view state for a model that is not the expected one
}
return editor.saveViewState();
}
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI {
let original: URI;
let modified: URI;
if (modelOrInput instanceof DiffEditorInput) {
original = modelOrInput.originalInput.getResource();
modified = modelOrInput.modifiedInput.getResource();
} else {
original = modelOrInput.original.uri;
modified = modelOrInput.modified.uri;
}
if (!original || !modified) {
return null;
}
// create a URI that is the Base64 concatenation of original + modified resource
return URI.from({ scheme: 'diff', path: `${btoa(original.toString())}${btoa(modified.toString())}` });
}
public dispose(): void {
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
super.dispose();
}
@@ -372,4 +473,4 @@ class ToggleIgnoreTrimWhitespaceAction extends Action {
this._configurationService.updateValue(`diffEditor.ignoreTrimWhitespace`, !this._isChecked);
return null;
}
}
}

View File

@@ -5,16 +5,15 @@
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { Dimension, Builder } from 'vs/base/browser/builder';
import objects = require('vs/base/common/objects');
import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import DOM = require('vs/base/browser/dom');
import * as objects from 'vs/base/common/objects';
import * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
import * as DOM from 'vs/base/browser/dom';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { EditorInput, EditorOptions, EditorViewStateMemento } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorViewState, IEditor } from 'vs/editor/common/editorCommon';
import { Position } from 'vs/platform/editor/common/editor';
@@ -43,15 +42,16 @@ export interface IEditorConfiguration {
*/
export abstract class BaseTextEditor extends BaseEditor {
private editorControl: IEditor;
private _editorContainer: Builder;
private _editorContainer: HTMLElement;
private hasPendingConfigurationChange: boolean;
private lastAppliedEditorOptions: IEditorOptions;
private editorViewStateMemento: EditorViewStateMemento<IEditorViewState>;
constructor(
id: string,
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IStorageService private storageService: IStorageService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService,
@IThemeService protected themeService: IThemeService,
@ITextFileService private readonly _textFileService: ITextFileService,
@@ -59,6 +59,8 @@ export abstract class BaseTextEditor extends BaseEditor {
) {
super(id, telemetryService, themeService);
this.editorViewStateMemento = new EditorViewStateMemento<IEditorViewState>(this.getMemento(storageService, Scope.WORKSPACE), TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
}
@@ -123,7 +125,7 @@ export abstract class BaseTextEditor extends BaseEditor {
return overrides;
}
protected createEditor(parent: Builder): void {
protected createEditor(parent: HTMLElement): void {
// Editor for Text
this._editorContainer = parent;
@@ -177,10 +179,10 @@ export abstract class BaseTextEditor extends BaseEditor {
*
* The passed in configuration object should be passed to the editor control when creating it.
*/
protected createEditorControl(parent: Builder, configuration: IEditorOptions): IEditor {
protected createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IEditor {
// Use a getter for the instantiation service since some subclasses might use scoped instantiation services
return this.instantiationService.createInstance(CodeEditor, parent.getHTMLElement(), configuration);
return this.instantiationService.createInstance(CodeEditor, parent, configuration);
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -189,7 +191,7 @@ export abstract class BaseTextEditor extends BaseEditor {
// Update editor options after having set the input. We do this because there can be
// editor input specific options (e.g. an ARIA label depending on the input showing)
this.updateEditorConfiguration();
this._editorContainer.getHTMLElement().setAttribute('aria-label', this.computeAriaLabel());
this._editorContainer.setAttribute('aria-label', this.computeAriaLabel());
});
}
@@ -219,7 +221,7 @@ export abstract class BaseTextEditor extends BaseEditor {
this.editorControl.focus();
}
public layout(dimension: Dimension): void {
public layout(dimension: DOM.Dimension): void {
// Pass on to Editor
this.editorControl.layout(dimension);
@@ -233,69 +235,51 @@ export abstract class BaseTextEditor extends BaseEditor {
* Saves the text editor view state for the given resource.
*/
protected saveTextEditorViewState(resource: URI): void {
const editorViewState = this.retrieveTextEditorViewState(resource);
if (!editorViewState) {
return;
}
this.editorViewStateMemento.saveState(resource, this.position, editorViewState);
}
protected retrieveTextEditorViewState(resource: URI): IEditorViewState {
const editor = getCodeOrDiffEditor(this).codeEditor;
if (!editor) {
return; // not supported for diff editors
return null; // not supported for diff editors
}
const model = editor.getModel();
if (!model) {
return; // view state always needs a model
return null; // 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
return null; // 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
return null; // 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;
}
let lastKnownViewState = textEditorViewStateMemento[resource.toString()];
if (!lastKnownViewState) {
lastKnownViewState = Object.create(null);
textEditorViewStateMemento[resource.toString()] = lastKnownViewState;
}
if (typeof this.position === 'number') {
lastKnownViewState[this.position] = editor.saveViewState();
}
return editor.saveViewState();
}
/**
* Clears the text editor view state for the given resources.
*/
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) {
resources.forEach(resource => delete textEditorViewStateMemento[resource.toString()]);
}
resources.forEach(resource => {
this.editorViewStateMemento.clearState(resource);
});
}
/**
* Loads the text editor view state for the given resource and returns it.
*/
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[resource.toString()];
if (viewState) {
return viewState[this.position];
}
}
return null;
return this.editorViewStateMemento.loadState(resource, this.position);
}
private updateEditorConfiguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
@@ -337,6 +321,14 @@ export abstract class BaseTextEditor extends BaseEditor {
protected abstract getAriaLabel(): string;
protected saveMemento(): void {
// ensure to first save our view state memento
this.editorViewStateMemento.save();
super.saveMemento();
}
public dispose(): void {
this.lastAppliedEditorOptions = void 0;
this.editorControl.dispose();

View File

@@ -5,8 +5,8 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import types = require('vs/base/common/types');
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { TextEditorOptions, EditorModel, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
@@ -67,7 +67,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
}
// Remember view settings if input changes
this.saveTextEditorViewStateForInput(this.input);
this.saveTextResourceEditorViewState(this.input);
// Set input and resolve
return super.setInput(input, options).then(() => {
@@ -97,7 +97,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
// Otherwise restore View State
if (!optionsGotApplied) {
this.restoreViewState(input);
this.restoreTextResourceEditorViewState(input);
}
return void 0;
@@ -105,7 +105,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
});
}
protected restoreViewState(input: EditorInput) {
private restoreTextResourceEditorViewState(input: EditorInput) {
if (input instanceof UntitledEditorInput || input instanceof ResourceEditorInput) {
const viewState = this.loadTextEditorViewState(input.getResource());
if (viewState) {
@@ -153,7 +153,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
public clearInput(): void {
// Keep editor view state in settings to restore when coming back
this.saveTextEditorViewStateForInput(this.input);
this.saveTextResourceEditorViewState(this.input);
// Clear Model
this.getControl().setModel(null);
@@ -165,14 +165,14 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
// Save View State (only for untitled)
if (this.input instanceof UntitledEditorInput) {
this.saveTextEditorViewStateForInput(this.input);
this.saveTextResourceEditorViewState(this.input);
}
// Call Super
super.shutdown();
}
protected saveTextEditorViewStateForInput(input: EditorInput): void {
private saveTextResourceEditorViewState(input: EditorInput): void {
if (!(input instanceof UntitledEditorInput) && !(input instanceof ResourceEditorInput)) {
return; // only enabled for untitled and resource inputs
}

View File

@@ -6,15 +6,14 @@
'use strict';
import 'vs/css!./media/titlecontrol';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
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');
import * as errors from 'vs/base/common/errors';
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 * as arrays from 'vs/base/common/arrays';
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';
@@ -28,7 +27,7 @@ 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 { SplitEditorAction, CloseEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
import { SplitEditorAction, CloseOneEditorAction } 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';
@@ -36,8 +35,8 @@ import { ResourceContextKey } from 'vs/workbench/common/resources';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Themable } from 'vs/workbench/common/theme';
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Dimension } from 'vs/base/browser/builder';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension, findParentWithClass } from 'vs/base/browser/dom';
export interface IToolbarActions {
primary: IAction[];
@@ -65,7 +64,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
protected dragged: boolean;
protected closeEditorAction: CloseEditorAction;
protected closeOneEditorAction: CloseOneEditorAction;
protected splitEditorAction: SplitEditorAction;
private parent: HTMLElement;
@@ -75,7 +74,8 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
protected editorActionsToolbar: ToolBar;
private mapActionsToEditors: { [editorId: string]: IToolbarActions; };
private scheduler: RunOnceScheduler;
private titleAreaUpdateScheduler: RunOnceScheduler;
private titleAreaToolbarUpdateScheduler: RunOnceScheduler;
private refreshScheduled: boolean;
private resourceContext: ResourceContextKey;
@@ -101,8 +101,11 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
this.stacks = editorGroupService.getStacksModel();
this.mapActionsToEditors = Object.create(null);
this.scheduler = new RunOnceScheduler(() => this.onSchedule(), 0);
this.toUnbind.push(this.scheduler);
this.titleAreaUpdateScheduler = new RunOnceScheduler(() => this.onSchedule(), 0);
this.toUnbind.push(this.titleAreaUpdateScheduler);
this.titleAreaToolbarUpdateScheduler = new RunOnceScheduler(() => this.updateEditorActionsToolbar(), 0);
this.toUnbind.push(this.titleAreaToolbarUpdateScheduler);
this.resourceContext = instantiationService.createInstance(ResourceContextKey);
@@ -166,22 +169,26 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
public update(instant?: boolean): void {
if (instant) {
this.scheduler.cancel();
this.titleAreaUpdateScheduler.cancel();
this.onSchedule();
} else {
this.scheduler.schedule();
this.titleAreaUpdateScheduler.schedule();
}
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
}
public refresh(instant?: boolean) {
this.refreshScheduled = true;
if (instant) {
this.scheduler.cancel();
this.titleAreaUpdateScheduler.cancel();
this.onSchedule();
} else {
this.scheduler.schedule();
this.titleAreaUpdateScheduler.schedule();
}
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
}
public create(parent: HTMLElement): void {
@@ -203,11 +210,11 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
}
public allowDragging(element: HTMLElement): boolean {
return !DOM.findParentWithClass(element, 'monaco-action-bar', 'one-editor-silo');
return !findParentWithClass(element, 'monaco-action-bar', 'one-editor-silo');
}
protected initActions(services: IInstantiationService): void {
this.closeEditorAction = services.createInstance(CloseEditorAction, CloseEditorAction.ID, nls.localize('close', "Close"));
this.closeOneEditorAction = services.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL);
this.splitEditorAction = services.createInstance(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL);
}
@@ -296,7 +303,14 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
const codeEditor = isCodeEditor(widget) && widget || isDiffEditor(widget) && widget.getModifiedEditor();
const scopedContextKeyService = codeEditor && codeEditor.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
this.disposeOnEditorActions.push(titleBarMenu, titleBarMenu.onDidChange(_ => this.update()));
this.disposeOnEditorActions.push(titleBarMenu, titleBarMenu.onDidChange(_ => {
// schedule the update for the title area toolbar only if no other
// update to the title area is scheduled which will always also
// update the toolbar
if (!this.titleAreaUpdateScheduler.isScheduled()) {
this.titleAreaToolbarUpdateScheduler.schedule();
}
}));
fillInActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary }, this.contextMenuService);
}
@@ -334,11 +348,10 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
const primaryEditorActionIds = primaryEditorActions.map(a => a.id);
if (!tabOptions.showTabs) {
primaryEditorActionIds.push(this.closeEditorAction.id); // always show "Close" when tabs are disabled
primaryEditorActionIds.push(this.closeOneEditorAction.id); // always show "Close" when tabs are disabled
}
const secondaryEditorActionIds = secondaryEditorActions.map(a => a.id);
if (
!arrays.equals(primaryEditorActionIds, this.currentPrimaryEditorActionIds) ||
!arrays.equals(secondaryEditorActionIds, this.currentSecondaryEditorActionIds) ||
@@ -348,7 +361,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
this.editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)();
if (!tabOptions.showTabs) {
this.editorActionsToolbar.addPrimaryAction(this.closeEditorAction)();
this.editorActionsToolbar.addPrimaryAction(this.closeOneEditorAction)();
}
this.currentPrimaryEditorActionIds = primaryEditorActionIds;
@@ -416,7 +429,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
// Actions
[
this.splitEditorAction,
this.closeEditorAction
this.closeOneEditorAction
].forEach((action) => {
action.dispose();
});

View File

@@ -1,66 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import URI from 'vs/base/common/uri';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Scope } from 'vs/workbench/common/memento';
export interface HtmlPreviewEditorViewState {
scrollYPercentage: number;
}
/**
* This class is only intended to be subclassed and not instantiated.
*/
export abstract class BaseWebviewEditor extends BaseEditor {
constructor(
id: string,
telemetryService: ITelemetryService,
themeService: IThemeService,
private storageService: IStorageService
) {
super(id, telemetryService, themeService);
}
private get viewStateStorageKey(): string {
return this.getId() + '.editorViewState';
}
protected saveViewState(resource: URI | string, editorViewState: HtmlPreviewEditorViewState): void {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
let editorViewStateMemento: { [key: string]: { [position: number]: HtmlPreviewEditorViewState } } = memento[this.viewStateStorageKey];
if (!editorViewStateMemento) {
editorViewStateMemento = Object.create(null);
memento[this.viewStateStorageKey] = editorViewStateMemento;
}
let fileViewState = editorViewStateMemento[resource.toString()];
if (!fileViewState) {
fileViewState = Object.create(null);
editorViewStateMemento[resource.toString()] = fileViewState;
}
if (typeof this.position === 'number') {
fileViewState[this.position] = editorViewState;
}
}
protected loadViewState(resource: URI | string): HtmlPreviewEditorViewState | null {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
const editorViewStateMemento: { [key: string]: { [position: number]: HtmlPreviewEditorViewState } } = memento[this.viewStateStorageKey];
if (editorViewStateMemento) {
const fileViewState = editorViewStateMemento[resource.toString()];
if (fileViewState) {
return fileViewState[this.position];
}
}
return null;
}
}