Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -5,14 +5,10 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import types = require('vs/base/common/types');
import { Builder } from 'vs/base/browser/builder';
import { Registry } from 'vs/platform/registry/common/platform';
import { Panel } from 'vs/workbench/browser/panel';
import { EditorInput, EditorOptions, IEditorDescriptor, IEditorInputFactory, IEditorRegistry, Extensions, IFileInputFactory } from 'vs/workbench/common/editor';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { IEditor, Position } from 'vs/platform/editor/common/editor';
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
import { SyncDescriptor, AsyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -123,183 +119,4 @@ export abstract class BaseEditor extends Panel implements IEditor {
// Super Dispose
super.dispose();
}
}
/**
* A lightweight descriptor of an editor. The descriptor is deferred so that heavy editors
* can load lazily in the workbench.
*/
export class EditorDescriptor extends AsyncDescriptor<BaseEditor> implements IEditorDescriptor {
private id: string;
private name: string;
constructor(id: string, name: string, moduleId: string, ctorName: string) {
super(moduleId, ctorName);
this.id = id;
this.name = name;
}
public getId(): string {
return this.id;
}
public getName(): string {
return this.name;
}
public describes(obj: any): boolean {
return obj instanceof BaseEditor && (<BaseEditor>obj).getId() === this.id;
}
}
const INPUT_DESCRIPTORS_PROPERTY = '__$inputDescriptors';
class EditorRegistry implements IEditorRegistry {
private editors: EditorDescriptor[];
private instantiationService: IInstantiationService;
private fileInputFactory: IFileInputFactory;
private editorInputFactoryConstructors: { [editorInputId: string]: IConstructorSignature0<IEditorInputFactory> } = Object.create(null);
private editorInputFactoryInstances: { [editorInputId: string]: IEditorInputFactory } = Object.create(null);
constructor() {
this.editors = [];
}
public setInstantiationService(service: IInstantiationService): void {
this.instantiationService = service;
for (let key in this.editorInputFactoryConstructors) {
const element = this.editorInputFactoryConstructors[key];
this.createEditorInputFactory(key, element);
}
this.editorInputFactoryConstructors = {};
}
private createEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0<IEditorInputFactory>): void {
const instance = this.instantiationService.createInstance(ctor);
this.editorInputFactoryInstances[editorInputId] = instance;
}
public registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput>): void;
public registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput>[]): void;
public registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: any): void {
// Support both non-array and array parameter
let inputDescriptors: SyncDescriptor<EditorInput>[] = [];
if (!types.isArray(editorInputDescriptor)) {
inputDescriptors.push(editorInputDescriptor);
} else {
inputDescriptors = editorInputDescriptor;
}
// Register (Support multiple Editors per Input)
descriptor[INPUT_DESCRIPTORS_PROPERTY] = inputDescriptors;
this.editors.push(descriptor);
}
public getEditor(input: EditorInput): EditorDescriptor {
const findEditorDescriptors = (input: EditorInput, byInstanceOf?: boolean): EditorDescriptor[] => {
const matchingDescriptors: EditorDescriptor[] = [];
for (let i = 0; i < this.editors.length; i++) {
const editor = this.editors[i];
const inputDescriptors = <SyncDescriptor<EditorInput>[]>editor[INPUT_DESCRIPTORS_PROPERTY];
for (let j = 0; j < inputDescriptors.length; j++) {
const inputClass = inputDescriptors[j].ctor;
// Direct check on constructor type (ignores prototype chain)
if (!byInstanceOf && input.constructor === inputClass) {
matchingDescriptors.push(editor);
break;
}
// Normal instanceof check
else if (byInstanceOf && input instanceof inputClass) {
matchingDescriptors.push(editor);
break;
}
}
}
// If no descriptors found, continue search using instanceof and prototype chain
if (!byInstanceOf && matchingDescriptors.length === 0) {
return findEditorDescriptors(input, true);
}
if (byInstanceOf) {
return matchingDescriptors;
}
return matchingDescriptors;
};
const descriptors = findEditorDescriptors(input);
if (descriptors && descriptors.length > 0) {
// Ask the input for its preferred Editor
const preferredEditorId = input.getPreferredEditorId(descriptors.map(d => d.getId()));
if (preferredEditorId) {
return this.getEditorById(preferredEditorId);
}
// Otherwise, first come first serve
return descriptors[0];
}
return null;
}
public getEditorById(editorId: string): EditorDescriptor {
for (let i = 0; i < this.editors.length; i++) {
const editor = this.editors[i];
if (editor.getId() === editorId) {
return editor;
}
}
return null;
}
public getEditors(): EditorDescriptor[] {
return this.editors.slice(0);
}
public setEditors(editorsToSet: EditorDescriptor[]): void {
this.editors = editorsToSet;
}
public getEditorInputs(): any[] {
const inputClasses: any[] = [];
for (let i = 0; i < this.editors.length; i++) {
const editor = this.editors[i];
const editorInputDescriptors = <SyncDescriptor<EditorInput>[]>editor[INPUT_DESCRIPTORS_PROPERTY];
inputClasses.push(...editorInputDescriptors.map(descriptor => descriptor.ctor));
}
return inputClasses;
}
public registerFileInputFactory(factory: IFileInputFactory): void {
this.fileInputFactory = factory;
}
public getFileInputFactory(): IFileInputFactory {
return this.fileInputFactory;
}
public registerEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0<IEditorInputFactory>): void {
if (!this.instantiationService) {
this.editorInputFactoryConstructors[editorInputId] = ctor;
} else {
this.createEditorInputFactory(editorInputId, ctor);
}
}
public getEditorInputFactory(editorInputId: string): IEditorInputFactory {
return this.editorInputFactoryInstances[editorInputId];
}
}
Registry.add(Extensions.Editors, new EditorRegistry());
}

View File

@@ -64,48 +64,46 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
const oldInput = this.input;
super.setInput(input, options);
// Detect options
// Return early for same input unless we force to open
const forceOpen = options && options.forceOpen;
// Same Input
if (!forceOpen && input.matches(oldInput)) {
if (!forceOpen && input.matches(this.input)) {
return TPromise.as<void>(null);
}
// Different Input (Reload)
return input.resolve(true).then((resolvedModel: EditorModel) => {
// Otherwise set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then((resolvedModel: EditorModel) => {
// Assert Model instance
if (!(resolvedModel instanceof BinaryEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
}
// Assert Model instance
if (!(resolvedModel instanceof BinaryEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// Render Input
const model = <BinaryEditorModel>resolvedModel;
ResourceViewer.show(
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag() },
this.binaryContainer,
this.scrollbar,
(resource: URI) => {
this.windowsService.openExternal(resource.toString()).then(didOpen => {
if (!didOpen) {
return this.windowsService.showItemInFolder(resource.fsPath);
}
// Render Input
const model = <BinaryEditorModel>resolvedModel;
ResourceViewer.show(
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag() },
this.binaryContainer,
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));
return void 0;
});
},
(meta) => this.handleMetadataChanged(meta));
return TPromise.as<void>(null);
return TPromise.as<void>(null);
});
});
}

View File

@@ -10,8 +10,8 @@ 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';
import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorInput, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
@@ -22,15 +22,15 @@ import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import { ChangeEncodingAction, ChangeEOLAction, ChangeModeAction, EditorStatus } from 'vs/workbench/browser/parts/editor/editorStatus';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import {
CloseEditorsInGroupAction, CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, KeepEditorAction, CloseOtherEditorsInGroupAction, OpenToSideAction, RevertAndCloseEditorAction,
NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInGroupOneAction,
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, NAVIGATE_IN_GROUP_ONE_PREFIX,
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, NavigateLastAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, NAVIGATE_IN_GROUP_ONE_PREFIX,
OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup, OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction,
NAVIGATE_IN_GROUP_TWO_PREFIX, ShowEditorsInGroupThreeAction, NAVIGATE_IN_GROUP_THREE_PREFIX, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction
} from 'vs/workbench/browser/parts/editor/editorActions';
@@ -39,14 +39,15 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { getQuickNavigateHandler, inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { isMacintosh } from 'vs/base/common/platform';
import { GroupOnePicker, GroupTwoPicker, GroupThreePicker, AllEditorsPicker } from 'vs/workbench/browser/parts/editor/editorPicker';
// Register String Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
TextResourceEditor,
TextResourceEditor.ID,
nls.localize('textEditor', "Text Editor"),
'vs/workbench/browser/parts/editor/textResourceEditor',
'TextResourceEditor'
),
[
new SyncDescriptor(UntitledEditorInput),
@@ -57,10 +58,9 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
// Register Text Diff Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
TextDiffEditor,
TextDiffEditor.ID,
nls.localize('textDiffEditor', "Text Diff Editor"),
'vs/workbench/browser/parts/editor/textDiffEditor',
'TextDiffEditor'
nls.localize('textDiffEditor', "Text Diff Editor")
),
[
new SyncDescriptor(DiffEditorInput)
@@ -70,10 +70,9 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
// Register Binary Resource Diff Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
BinaryResourceDiffEditor,
BinaryResourceDiffEditor.ID,
nls.localize('binaryDiffEditor', "Binary Diff Editor"),
'vs/workbench/browser/parts/editor/binaryDiffEditor',
'BinaryResourceDiffEditor'
nls.localize('binaryDiffEditor', "Binary Diff Editor")
),
[
new SyncDescriptor(DiffEditorInput)
@@ -82,10 +81,9 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
SideBySideEditor,
SideBySideEditor.ID,
nls.localize('sideBySideEditor', "Side by Side Editor"),
'vs/workbench/browser/parts/editor/sideBySideEditor',
'SideBySideEditor'
nls.localize('sideBySideEditor', "Side by Side Editor")
),
[
new SyncDescriptor(SideBySideEditorInput)
@@ -115,7 +113,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
const untitledEditorInput = <UntitledEditorInput>editorInput;
// {{SQL CARBON EDIT}}
if (!untitledEditorInput.getResource) {
if (!untitledEditorInput.getResource()) {
return null;
}
@@ -147,7 +145,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
}
}
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(UntitledEditorInput.ID, UntitledEditorInputFactory);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(UntitledEditorInput.ID, UntitledEditorInputFactory);
interface ISerializedSideBySideEditorInput {
name: string;
@@ -167,7 +165,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
const input = <SideBySideEditorInput>editorInput;
if (input.details && input.master) {
const registry = Registry.as<IEditorRegistry>(EditorExtensions.Editors);
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId());
const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId());
@@ -194,7 +192,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
const registry = Registry.as<IEditorRegistry>(EditorExtensions.Editors);
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
const detailsInputFactory = registry.getEditorInputFactory(deserialized.detailsTypeId);
const masterInputFactory = registry.getEditorInputFactory(deserialized.masterTypeId);
@@ -211,7 +209,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
}
}
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(SideBySideEditorInput.ID, SideBySideEditorInputFactory);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(SideBySideEditorInput.ID, SideBySideEditorInputFactory);
// Register Editor Status
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
@@ -270,8 +268,8 @@ const editorPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExp
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/browser/parts/editor/editorPicker',
'GroupOnePicker',
GroupOnePicker,
GroupOnePicker.ID,
NAVIGATE_IN_GROUP_ONE_PREFIX,
editorPickerContextKey,
[
@@ -296,8 +294,8 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/browser/parts/editor/editorPicker',
'GroupTwoPicker',
GroupTwoPicker,
GroupTwoPicker.ID,
NAVIGATE_IN_GROUP_TWO_PREFIX,
editorPickerContextKey,
[]
@@ -306,8 +304,8 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/browser/parts/editor/editorPicker',
'GroupThreePicker',
GroupThreePicker,
GroupThreePicker.ID,
NAVIGATE_IN_GROUP_THREE_PREFIX,
editorPickerContextKey,
[]
@@ -316,8 +314,8 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/browser/parts/editor/editorPicker',
'AllEditorsPicker',
AllEditorsPicker,
AllEditorsPicker.ID,
NAVIGATE_ALL_EDITORS_GROUP_PREFIX,
editorPickerContextKey,
[
@@ -343,7 +341,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupThre
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET] } }), 'View: Open Next Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'View: Clear Recently Opened', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'File: Clear Recently Opened', nls.localize('file', "File"));
registry.registerWorkbenchAction(new SyncActionDescriptor(KeepEditorAction, KeepEditorAction.ID, KeepEditorAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter) }), 'View: Keep Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left', category);
@@ -373,6 +371,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, Fo
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Next Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward');
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back');
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLastAction, NavigateLastAction.ID, NavigateLastAction.LABEL), 'Go Last');
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History');
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History');
registry.registerWorkbenchAction(new SyncActionDescriptor(RevertAndCloseEditorAction, RevertAndCloseEditorAction.ID, RevertAndCloseEditorAction.LABEL), 'View: Revert and Close Editor', category);
@@ -380,8 +379,8 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(RevertAndCloseEditorAc
// Register Editor Picker Actions including quick navigate support
const openNextEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } };
const openPreviousEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } };
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, openNextEditorKeybinding), 'Open Next Recently Used Editor in Group');
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, openPreviousEditorKeybinding), 'Open Previous Recently Used Editor in Group');
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, openNextEditorKeybinding), 'View: Open Next Recently Used Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, openPreviousEditorKeybinding), 'View: Open Previous Recently Used Editor in Group', category);
const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker';
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -405,4 +404,17 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
// Editor Commands
editorCommands.setup();
editorCommands.setup();
// Touch Bar
if (isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconPath: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')).fsPath },
group: 'navigation'
});
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconPath: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')).fsPath },
group: 'navigation'
});
}

View File

@@ -9,7 +9,7 @@ import nls = require('vs/nls');
import { Action } from 'vs/base/common/actions';
import { mixin } from 'vs/base/common/objects';
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
import { EditorInput, hasResource, TextEditorOptions, EditorOptions, IEditorIdentifier, IEditorContext, ActiveEditorMoveArguments, ActiveEditorMovePositioning, EditorCommands, ConfirmResult } from 'vs/workbench/common/editor';
import { EditorInput, TextEditorOptions, EditorOptions, IEditorIdentifier, IEditorContext, ActiveEditorMoveArguments, ActiveEditorMovePositioning, EditorCommands, ConfirmResult } from 'vs/workbench/common/editor';
import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -255,16 +255,13 @@ export class FocusFirstGroupAction extends Action {
// Since no editor is currently opened, try to open last history entry to the target side
const history = this.historyService.getHistory();
for (let input of history) {
// For now only support to open files from history to the side
if (history.length > 0) {
const input = history[0];
if (input instanceof EditorInput) {
if (hasResource(input, { filter: ['file', 'untitled'] })) {
return this.editorService.openEditor(input, null, Position.ONE);
}
} else {
return this.editorService.openEditor(input as IResourceInput, Position.ONE);
return this.editorService.openEditor(input, null, Position.ONE);
}
return this.editorService.openEditor(input as IResourceInput, Position.ONE);
}
return TPromise.as(true);
@@ -327,15 +324,11 @@ export abstract class BaseFocusSideGroupAction extends Action {
else if (referenceEditor) {
const history = this.historyService.getHistory();
for (let input of history) {
// For now only support to open files from history to the side
if (input instanceof EditorInput) {
if (hasResource(input, { filter: ['file', 'untitled'] })) {
return this.editorService.openEditor(input, { pinned: true }, this.getTargetEditorSide());
}
} else {
return this.editorService.openEditor({ resource: (input as IResourceInput).resource, options: { pinned: true } }, this.getTargetEditorSide());
if (input instanceof EditorInput && input.supportsSplitEditor()) {
return this.editorService.openEditor(input, { pinned: true }, this.getTargetEditorSide());
}
return this.editorService.openEditor({ resource: (input as IResourceInput).resource, options: { pinned: true } }, this.getTargetEditorSide());
}
}
@@ -1116,6 +1109,22 @@ export class NavigateBackwardsAction extends Action {
}
}
export class NavigateLastAction extends Action {
public static ID = 'workbench.action.navigateLast';
public static LABEL = nls.localize('navigateLast', "Go Last");
constructor(id: string, label: string, @IHistoryService private historyService: IHistoryService) {
super(id, label);
}
public run(): TPromise<any> {
this.historyService.last();
return TPromise.as(null);
}
}
export class ReopenClosedEditorAction extends Action {
public static ID = 'workbench.action.reopenClosedEditor';

View File

@@ -199,7 +199,7 @@ function handleCommandDeprecations(): void {
};
Object.keys(mapDeprecatedCommands).forEach(deprecatedCommandId => {
const newCommandId = mapDeprecatedCommands[deprecatedCommandId];
const newCommandId: string = mapDeprecatedCommands[deprecatedCommandId];
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: deprecatedCommandId,

View File

@@ -20,7 +20,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { isMacintosh } from 'vs/base/common/platform';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Position, POSITIONS } from 'vs/platform/editor/common/editor';
import { IEditorGroupService, ITabOptions, GroupArrangement, GroupOrientation } from 'vs/workbench/services/group/common/groupService';
import { IEditorGroupService, IEditorTabOptions, GroupArrangement, GroupOrientation } from 'vs/workbench/services/group/common/groupService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@@ -73,6 +73,7 @@ export interface IEditorGroupsControl {
getInstantiationService(position: Position): IInstantiationService;
getProgressBar(position: Position): ProgressBar;
updateProgress(position: Position, state: ProgressState): void;
updateTitleAreas(refreshActive?: boolean): void;
layout(dimension: Dimension): void;
layout(position: Position): void;
@@ -115,7 +116,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private layoutVertically: boolean;
private tabOptions: ITabOptions;
private tabOptions: IEditorTabOptions;
private silos: Builder[];
private silosSize: number[];
@@ -225,7 +226,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.extensionService.onReady().then(() => this.onExtensionsReady());
}
private updateTabOptions(tabOptions: ITabOptions, refresh?: boolean): void {
private updateTabOptions(tabOptions: IEditorTabOptions, refresh?: boolean): void {
const tabCloseButton = this.tabOptions ? this.tabOptions.tabCloseButton : 'right';
this.tabOptions = tabOptions;
@@ -443,6 +444,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
// Log this fact in telemetry
if (this.telemetryService) {
/* __GDPR__
"workbenchEditorMaximized" : {}
*/
this.telemetryService.publicLog('workbenchEditorMaximized');
}
@@ -2077,6 +2081,32 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
return silo ? silo.child().getProperty(key) : void 0;
}
public updateTitleAreas(refreshActive?: boolean): void {
POSITIONS.forEach(position => {
const group = this.stacks.groupAt(position);
if (!group) {
return;
}
const titleControl = this.getTitleAreaControl(position);
if (!titleControl) {
return;
}
// Make sure the active group is shown in the title
// and refresh it if we are instructed to refresh it
if (refreshActive && group.isActive) {
titleControl.setContext(group);
titleControl.refresh(true);
}
// Otherwise, just refresh the toolbar
else {
titleControl.updateEditorActionsToolbar();
}
});
}
public updateProgress(position: Position, state: ProgressState): void {
const progressbar = this.getProgressBar(position);
if (!progressbar) {

View File

@@ -20,17 +20,17 @@ import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { Scope as MementoScope } from 'vs/workbench/common/memento';
import { Part } from 'vs/workbench/browser/part';
import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorRegistry, Extensions as EditorExtensions, EditorInput, EditorOptions, ConfirmResult, IWorkbenchEditorConfiguration, IEditorDescriptor, TextEditorOptions, SideBySideEditorInput, TextCompareEditorVisible, TEXT_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorInput, EditorOptions, ConfirmResult, IWorkbenchEditorConfiguration, TextEditorOptions, SideBySideEditorInput, TextCompareEditorVisible, TEXT_DIFF_EDITOR_ID, EditorOpeningEvent, IEditorOpeningEvent } from 'vs/workbench/common/editor';
import { EditorGroupsControl, Rochade, IEditorGroupsControl, ProgressState } from 'vs/workbench/browser/parts/editor/editorGroupsControl';
import { WorkbenchProgressService } from 'vs/workbench/services/progress/browser/progressService';
import { IEditorGroupService, GroupOrientation, GroupArrangement, ITabOptions, IMoveOptions } from 'vs/workbench/services/group/common/groupService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEditorPart } from 'vs/workbench/services/editor/browser/editorService';
import { IEditorGroupService, GroupOrientation, GroupArrangement, IEditorTabOptions, IMoveOptions } from 'vs/workbench/services/group/common/groupService';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { IEditorPart } from 'vs/workbench/services/editor/common/editorService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { Position, POSITIONS, Direction } from 'vs/platform/editor/common/editor';
import { Position, POSITIONS, Direction, IEditor } from 'vs/platform/editor/common/editor';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IMessageService, IMessageWithAction, Severity } from 'vs/platform/message/common/message';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -44,10 +44,13 @@ import { EDITOR_GROUP_BACKGROUND } from 'vs/workbench/common/theme';
import { createCSSRule } from 'vs/base/browser/dom';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join } from 'vs/base/common/paths';
import { isCommonCodeEditor } from 'vs/editor/common/editorCommon';
import { IEditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
// {{SQL CARBON EDIT}}
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
class ProgressMonitor {
constructor(private _token: number, private progressPromise: TPromise<void>) { }
@@ -94,23 +97,23 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
private editorGroupsControl: IEditorGroupsControl;
private memento: object;
private stacks: EditorStacksModel;
private tabOptions: ITabOptions;
private tabOptions: IEditorTabOptions;
private forceHideTabs: boolean;
private doNotFireTabOptionsChanged: boolean;
private revealIfOpen: boolean;
private _onEditorsChanged: Emitter<void>;
private _onEditorsMoved: Emitter<void>;
private _onEditorOpening: Emitter<IEditorOpeningEvent>;
private _onEditorGroupMoved: Emitter<void>;
private _onEditorOpenFail: Emitter<EditorInput>;
private _onGroupOrientationChanged: Emitter<void>;
private _onTabOptionsChanged: Emitter<ITabOptions>;
private _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private textCompareEditorVisible: IContextKey<boolean>;
// The following data structures are partitioned into array of Position as provided by Services.POSITION array
private visibleEditors: BaseEditor[];
private instantiatedEditors: BaseEditor[][];
private mapEditorInstantiationPromiseToEditor: { [editorId: string]: TPromise<BaseEditor>; }[];
private editorOpenToken: number[];
private pendingEditorInputsToClose: EditorIdentifier[];
private pendingEditorInputCloseTimeout: number;
@@ -134,10 +137,11 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
super(id, { hasTitle: false }, themeService);
this._onEditorsChanged = new Emitter<void>();
this._onEditorsMoved = new Emitter<void>();
this._onEditorOpening = new Emitter<IEditorOpeningEvent>();
this._onEditorGroupMoved = new Emitter<void>();
this._onEditorOpenFail = new Emitter<EditorInput>();
this._onGroupOrientationChanged = new Emitter<void>();
this._onTabOptionsChanged = new Emitter<ITabOptions>();
this._onTabOptionsChanged = new Emitter<IEditorTabOptions>();
this.visibleEditors = [];
@@ -145,8 +149,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.instantiatedEditors = arrays.fill(POSITIONS.length, () => []);
this.mapEditorInstantiationPromiseToEditor = arrays.fill(POSITIONS.length, () => Object.create(null));
this.pendingEditorInputsToClose = [];
this.pendingEditorInputCloseTimeout = null;
@@ -162,18 +164,27 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
previewEditors: editorConfig.enablePreview,
showIcons: editorConfig.showIcons,
showTabs: editorConfig.showTabs,
tabCloseButton: editorConfig.tabCloseButton
tabCloseButton: editorConfig.tabCloseButton,
labelFormat: editorConfig.labelFormat,
};
this.revealIfOpen = editorConfig.revealIfOpen;
/* __GDPR__
"workbenchEditorConfiguration" : {
"${include}": [
"${IWorkbenchEditorConfiguration}"
]
}
*/
this.telemetryService.publicLog('workbenchEditorConfiguration', editorConfig);
} else {
this.tabOptions = {
previewEditors: true,
showIcons: false,
showTabs: true,
tabCloseButton: 'right'
tabCloseButton: 'right',
labelFormat: 'default',
};
this.revealIfOpen = false;
@@ -199,40 +210,44 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.toUnbind.push(this.stacks.onEditorClosed(event => this.onEditorClosed(event)));
this.toUnbind.push(this.stacks.onGroupOpened(event => this.onEditorGroupOpenedOrClosed()));
this.toUnbind.push(this.stacks.onGroupClosed(event => this.onEditorGroupOpenedOrClosed()));
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration<IWorkbenchEditorConfiguration>())));
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)));
}
private onEditorGroupOpenedOrClosed(): void {
this.updateStyles();
}
private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void {
if (configuration && configuration.workbench && configuration.workbench.editor) {
const editorConfig = configuration.workbench.editor;
private onConfigurationUpdated(event: IConfigurationChangeEvent): void {
if (event.affectsConfiguration('workbench.editor')) {
const configuration = this.configurationService.getConfiguration<IWorkbenchEditorConfiguration>();
if (configuration && configuration.workbench && configuration.workbench.editor) {
const editorConfig = configuration.workbench.editor;
// Pin all preview editors of the user chose to disable preview
const newPreviewEditors = editorConfig.enablePreview;
if (this.tabOptions.previewEditors !== newPreviewEditors && !newPreviewEditors) {
this.stacks.groups.forEach(group => {
if (group.previewEditor) {
this.pinEditor(group, group.previewEditor);
}
});
// Pin all preview editors of the user chose to disable preview
const newPreviewEditors = editorConfig.enablePreview;
if (this.tabOptions.previewEditors !== newPreviewEditors && !newPreviewEditors) {
this.stacks.groups.forEach(group => {
if (group.previewEditor) {
this.pinEditor(group, group.previewEditor);
}
});
}
const oldTabOptions = objects.clone(this.tabOptions);
this.tabOptions = {
previewEditors: newPreviewEditors,
showIcons: editorConfig.showIcons,
tabCloseButton: editorConfig.tabCloseButton,
showTabs: this.forceHideTabs ? false : editorConfig.showTabs,
labelFormat: editorConfig.labelFormat,
};
if (!this.doNotFireTabOptionsChanged && !objects.equals(oldTabOptions, this.tabOptions)) {
this._onTabOptionsChanged.fire(this.tabOptions);
}
this.revealIfOpen = editorConfig.revealIfOpen;
}
const oldTabOptions = objects.clone(this.tabOptions);
this.tabOptions = {
previewEditors: newPreviewEditors,
showIcons: editorConfig.showIcons,
tabCloseButton: editorConfig.tabCloseButton,
showTabs: this.forceHideTabs ? false : editorConfig.showTabs
};
if (!this.doNotFireTabOptionsChanged && !objects.equals(oldTabOptions, this.tabOptions)) {
this._onTabOptionsChanged.fire(this.tabOptions);
}
this.revealIfOpen = editorConfig.revealIfOpen;
}
}
@@ -248,10 +263,24 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
private onEditorOpened(identifier: EditorIdentifier): void {
/* __GDPR__
"editorOpened" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorOpened', identifier.editor.getTelemetryDescriptor());
}
private onEditorClosed(event: EditorCloseEvent): void {
/* __GDPR__
"editorClosed" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorClosed', event.editor.getTelemetryDescriptor());
}
@@ -270,8 +299,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return this._onEditorsChanged.event;
}
public get onEditorsMoved(): Event<void> {
return this._onEditorsMoved.event;
public get onEditorOpening(): Event<IEditorOpeningEvent> {
return this._onEditorOpening.event;
}
public get onEditorGroupMoved(): Event<void> {
return this._onEditorGroupMoved.event;
}
public get onEditorOpenFail(): Event<EditorInput> {
@@ -282,39 +315,52 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return this._onGroupOrientationChanged.event;
}
public get onTabOptionsChanged(): Event<ITabOptions> {
public get onTabOptionsChanged(): Event<IEditorTabOptions> {
return this._onTabOptionsChanged.event;
}
public getTabOptions(): ITabOptions {
public getTabOptions(): IEditorTabOptions {
return this.tabOptions;
}
public openEditor(input: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise<BaseEditor>;
public openEditor(input: EditorInput, options?: EditorOptions, position?: Position, ratio?: number[]): TPromise<BaseEditor>;
public openEditor(input: EditorInput, options?: EditorOptions, arg3?: any, ratio?: number[]): TPromise<BaseEditor> {
// Normalize some values
if (!options) { options = null; }
public openEditor(input: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise<IEditor>;
public openEditor(input: EditorInput, options?: EditorOptions, position?: Position, ratio?: number[]): TPromise<IEditor>;
public openEditor(input: EditorInput, options?: EditorOptions, arg3?: any, ratio?: number[]): TPromise<IEditor> {
if (!options) {
options = null;
}
// Determine position to open editor in (one, two, three)
const position = this.findPosition(input, options, arg3, ratio);
// Some conditions under which we prevent the request
if (
!input || // no input
position === null || // invalid position
Object.keys(this.mapEditorInstantiationPromiseToEditor[position]).length > 0 || // pending editor load
!this.editorGroupsControl || // too early
this.editorGroupsControl.isDragging() // pending editor DND
!input || // no input
position === null || // invalid position
!this.editorGroupsControl || // too early
this.editorGroupsControl.isDragging() // pending editor DND
) {
return TPromise.as<BaseEditor>(null);
}
// Editor opening event (can be prevented and overridden)
const event = new EditorOpeningEvent(input, options, position);
this._onEditorOpening.fire(event);
const prevented = event.isPrevented();
if (prevented) {
return prevented();
}
// {{SQL CARBON EDIT}}
// Convert input into custom type if it's one of the ones we support
input = convertEditorInput(input, options, this.instantiationService);
// Open through UI
return this.doOpenEditor(position, input, options, ratio);
}
private doOpenEditor(position: Position, input: EditorInput, options: EditorOptions, ratio: number[]): TPromise<BaseEditor> {
// We need an editor descriptor for the input
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(input);
if (!descriptor) {
@@ -323,15 +369,14 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Opened to the side
if (position !== Position.ONE) {
/* __GDPR__
"workbenchSideEditorOpened" : {
"position" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('workbenchSideEditorOpened', { position: position });
}
// Open through UI
return this.doOpenEditor(position, descriptor, input, options, ratio);
}
private doOpenEditor(position: Position, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, ratio: number[]): TPromise<BaseEditor> {
// Update stacks: We do this early on before the UI is there because we want our stacks model to have
// a consistent view of the editor world and updating it later async after the UI is there will cause
// issues (e.g. when a closeEditor call is made that expects the openEditor call to have updated the
@@ -339,7 +384,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// This can however cause a race condition where the stacks model indicates the opened editor is there
// while the UI is not yet ready. Clients have to deal with this fact and we have to make sure that the
// stacks model gets updated if any of the UI updating fails with an error.
const group = this.ensureGroup(position, !options || !options.preserveFocus);
const [group, newGroupOpened] = this.ensureGroup(position, !options || !options.preserveFocus);
const pinned = !this.tabOptions.previewEditors || (options && (options.pinned || typeof options.index === 'number')) || input.isDirty();
const active = (group.count === 0) || !options || !options.inactive;
@@ -362,23 +407,34 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}));
// Show editor
return this.doShowEditor(group, descriptor, input, options, ratio, monitor).then(editor => {
if (!editor) {
return TPromise.as<BaseEditor>(null); // canceled or other error
}
const editor = this.doShowEditor(group, descriptor, input, options, ratio, monitor);
if (!editor) {
return TPromise.as<BaseEditor>(null); // canceled or other error
}
// Set input to editor
return this.doSetInput(group, editor, input, options, monitor);
});
// Set input to editor
const inputPromise = this.doSetInput(group, editor, input, options, monitor);
// A new active group got opened. Since this involves updating the title area controls to show
// the new editor and actions we trigger a direct update of title controls from here to avoid
// some UI flickering if we rely on the event handlers that all use schedulers.
// The reason we can trigger this now is that after the input is set to the editor group, the
// resource context is updated and the correct number of actions will be resolved from the title
// area.
if (newGroupOpened && this.stacks.isActive(group)) {
this.editorGroupsControl.updateTitleAreas(true /* refresh new active group */);
}
return inputPromise;
}
private doShowEditor(group: EditorGroup, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, ratio: number[], monitor: ProgressMonitor): TPromise<BaseEditor> {
const position = this.stacks.positionOfGroup(group);
private doShowEditor(group: EditorGroup, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, ratio: number[], monitor: ProgressMonitor): BaseEditor {
let position = this.stacks.positionOfGroup(group);
const editorAtPosition = this.visibleEditors[position];
// Return early if the currently visible editor can handle the input
if (editorAtPosition && descriptor.describes(editorAtPosition)) {
return TPromise.as(editorAtPosition);
return editorAtPosition;
}
// Hide active one first
@@ -387,107 +443,77 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
// Create Editor
return this.doCreateEditor(group, descriptor, monitor).then(editor => {
const position = this.stacks.positionOfGroup(group); // might have changed due to a rochade meanwhile
const editor = this.doCreateEditor(group, descriptor, monitor);
position = this.stacks.positionOfGroup(group); // might have changed due to a rochade meanwhile
// Make sure that the user meanwhile did not open another editor or something went wrong
if (!editor || !this.visibleEditors[position] || editor.getId() !== this.visibleEditors[position].getId()) {
monitor.cancel();
// Make sure that the user meanwhile did not open another editor or something went wrong
if (!editor || !this.visibleEditors[position] || editor.getId() !== this.visibleEditors[position].getId()) {
monitor.cancel();
return null;
}
// Show in side by side control
this.editorGroupsControl.show(editor, position, options && options.preserveFocus, ratio);
// Indicate to editor that it is now visible
editor.setVisible(true, position);
// Update text compare editor visible context
this.updateTextCompareEditorVisible();
// Make sure the editor is layed out
this.editorGroupsControl.layout(position);
return editor;
}, e => {
this.messageService.show(Severity.Error, types.isString(e) ? new Error(e) : e);
return null;
});
}
// Show in side by side control
this.editorGroupsControl.show(editor, position, options && options.preserveFocus, ratio);
// Indicate to editor that it is now visible
editor.setVisible(true, position);
// Update text compare editor visible context
this.updateTextCompareEditorVisible();
// Make sure the editor is layed out
this.editorGroupsControl.layout(position);
return editor;
}
private doCreateEditor(group: EditorGroup, descriptor: IEditorDescriptor, monitor: ProgressMonitor): TPromise<BaseEditor> {
private doCreateEditor(group: EditorGroup, descriptor: IEditorDescriptor, monitor: ProgressMonitor): BaseEditor {
// Instantiate editor
return this.doInstantiateEditor(group, descriptor).then(editor => {
const position = this.stacks.positionOfGroup(group); // might have changed due to a rochade meanwhile
const editor = this.doInstantiateEditor(group, descriptor);
const position = this.stacks.positionOfGroup(group); // might have changed due to a rochade meanwhile
// Make sure that the user meanwhile did not open another editor
if (monitor.token !== this.editorOpenToken[position]) {
monitor.cancel();
// Make sure that the user meanwhile did not open another editor
if (monitor.token !== this.editorOpenToken[position]) {
monitor.cancel();
return null;
}
return null;
}
// Remember Editor at position
this.visibleEditors[position] = editor;
// Remember Editor at position
this.visibleEditors[position] = editor;
// Create editor as needed
if (!editor.getContainer()) {
editor.create($().div({
'class': 'editor-container',
'role': 'tabpanel',
id: descriptor.getId()
}));
}
// Create editor as needed
if (!editor.getContainer()) {
editor.create($().div({
'class': 'editor-container',
'role': 'tabpanel',
id: descriptor.getId()
}));
}
return editor;
});
return editor;
}
private doInstantiateEditor(group: EditorGroup, descriptor: IEditorDescriptor): TPromise<BaseEditor> {
private doInstantiateEditor(group: EditorGroup, descriptor: IEditorDescriptor): BaseEditor {
const position = this.stacks.positionOfGroup(group);
// Return early if already instantiated
const instantiatedEditor = this.instantiatedEditors[position].filter(e => descriptor.describes(e))[0];
if (instantiatedEditor) {
return TPromise.as(instantiatedEditor);
}
// Return early if editor is being instantiated at the same time from a previous call
const pendingEditorInstantiate = this.mapEditorInstantiationPromiseToEditor[position][descriptor.getId()];
if (pendingEditorInstantiate) {
return pendingEditorInstantiate;
return instantiatedEditor;
}
// Otherwise instantiate
const progressService = this.instantiationService.createInstance(WorkbenchProgressService, this.editorGroupsControl.getProgressBar(position), descriptor.getId(), true);
const editorInstantiationService = this.editorGroupsControl.getInstantiationService(position).createChild(new ServiceCollection([IProgressService, progressService]));
let loaded = false;
const onInstantiate = (arg: BaseEditor): TPromise<BaseEditor> => {
const position = this.stacks.positionOfGroup(group); // might have changed due to a rochade meanwhile
const editor = descriptor.instantiate(editorInstantiationService);
loaded = true;
delete this.mapEditorInstantiationPromiseToEditor[position][descriptor.getId()];
this.instantiatedEditors[position].push(editor);
this.instantiatedEditors[position].push(arg);
return TPromise.as(arg);
};
const instantiateEditorPromise = editorInstantiationService.createInstance(<EditorDescriptor>descriptor).then(onInstantiate);
if (!loaded) {
this.mapEditorInstantiationPromiseToEditor[position][descriptor.getId()] = instantiateEditorPromise;
}
return instantiateEditorPromise.then(result => {
progressService.dispose();
return result;
});
return editor;
}
private doSetInput(group: EditorGroup, editor: BaseEditor, input: EditorInput, options: EditorOptions, monitor: ProgressMonitor): TPromise<BaseEditor> {
@@ -641,6 +667,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Explicitly trigger the focus changed handler because the side by side control will not trigger it unless
// the user is actively changing focus with the mouse from left/top to right/bottom.
this.onGroupFocusChanged();
// Update title area sync to avoid some flickering with actions
this.editorGroupsControl.updateTitleAreas();
}
}
@@ -667,7 +696,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Emit Editor move event
if (rochade !== Rochade.NONE) {
this._onEditorsMoved.fire();
this._onEditorGroupMoved.fire();
}
}
@@ -875,14 +904,13 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Move data structures
arrays.move(this.visibleEditors, fromPosition, toPosition);
arrays.move(this.editorOpenToken, fromPosition, toPosition);
arrays.move(this.mapEditorInstantiationPromiseToEditor, fromPosition, toPosition);
arrays.move(this.instantiatedEditors, fromPosition, toPosition);
// Restore focus
this.focusGroup(fromGroup);
// Events
this._onEditorsMoved.fire();
this._onEditorGroupMoved.fire();
}
public moveEditor(input: EditorInput, from: EditorGroup, to: EditorGroup, moveOptions?: IMoveOptions): void;
@@ -1020,7 +1048,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
}
public replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: EditorOptions }[], position?: Position): TPromise<BaseEditor[]> {
public replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: EditorOptions }[], position?: Position): TPromise<IEditor[]> {
const activeReplacements: IEditorReplacement[] = [];
const hiddenReplacements: IEditorReplacement[] = [];
@@ -1079,9 +1107,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return res;
}
public openEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[]): TPromise<BaseEditor[]> {
public openEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[]): TPromise<IEditor[]> {
if (!editors.length) {
return TPromise.as<BaseEditor[]>([]);
return TPromise.as<IEditor[]>([]);
}
let activePosition: Position;
@@ -1098,7 +1126,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return this.stacks.groups.some(g => g.count > 0);
}
public restoreEditors(): TPromise<BaseEditor[]> {
public restoreEditors(): TPromise<IEditor[]> {
const editors = this.stacks.groups.map((group, index) => {
return {
input: group.activeEditor,
@@ -1108,7 +1136,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
});
if (!editors.length) {
return TPromise.as<BaseEditor[]>([]);
return TPromise.as<IEditor[]>([]);
}
let activePosition: Position;
@@ -1121,7 +1149,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return this.doOpenEditors(editors, activePosition, editorState && editorState.ratio);
}
private doOpenEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[], activePosition?: number, ratio?: number[]): TPromise<BaseEditor[]> {
private doOpenEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[], activePosition?: number, ratio?: number[]): TPromise<IEditor[]> {
const positionOneEditors = editors.filter(e => e.position === Position.ONE);
const positionTwoEditors = editors.filter(e => e.position === Position.TWO);
const positionThreeEditors = editors.filter(e => e.position === Position.THREE);
@@ -1168,7 +1196,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Open each input respecting the options. Since there can only be one active editor in each
// position, we have to pick the first input from each position and add the others as inactive
const promises: TPromise<BaseEditor>[] = [];
const promises: TPromise<IEditor>[] = [];
[positionOneEditors.shift(), positionTwoEditors.shift(), positionThreeEditors.shift()].forEach((editor, position) => {
if (!editor) {
return; // unused position
@@ -1177,7 +1205,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
const input = editor.input;
// Resolve editor options
const preserveFocus = (activePosition !== position);
const preserveFocus = (activePosition !== position && ratio && ratio.length > 0); // during restore, preserve focus to reduce flicker
let options: EditorOptions;
if (editor.options) {
options = editor.options;
@@ -1202,7 +1230,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
[positionOneEditors, positionTwoEditors, positionThreeEditors].forEach((editors, index) => {
const group = this.stacks.groupAt(index);
if (group) {
editors.forEach(editor => group.openEditor(editor.input, { pinned: true })); // group could be null if one openeditor call failed!
// Make sure we are keeping the order as the editors are passed to us. We have to set
// an explicit index because otherwise we would put editors in the wrong order
// (see https://github.com/Microsoft/vscode/issues/30364)
const startingIndex = group.indexOf(group.activeEditor) + 1;
editors.forEach((editor, offset) => group.openEditor(editor.input, { pinned: true, index: (startingIndex + offset) }));
}
});
@@ -1298,6 +1331,20 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
}
public invokeWithinEditorContext<T>(fn: (accessor: ServicesAccessor) => T): T {
const activeEditor = this.getActiveEditor();
if (activeEditor) {
const activeEditorControl = activeEditor.getControl();
if (isCommonCodeEditor(activeEditorControl)) {
return activeEditorControl.invokeWithinContext(fn);
}
return this.editorGroupsControl.getInstantiationService(activeEditor.position).invokeFunction(fn);
}
return this.instantiationService.invokeFunction(fn);
}
public layout(dimension: Dimension): Dimension[] {
// Pass to super
@@ -1337,7 +1384,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Emitters
this._onEditorsChanged.dispose();
this._onEditorsMoved.dispose();
this._onEditorOpening.dispose();
this._onEditorGroupMoved.dispose();
this._onEditorOpenFail.dispose();
// Reset Tokens
@@ -1493,7 +1541,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.doRochade(this.visibleEditors, from, to, null);
this.doRochade(this.editorOpenToken, from, to, null);
this.doRochade(this.mapEditorInstantiationPromiseToEditor, from, to, Object.create(null));
this.doRochade(this.instantiatedEditors, from, to, []);
}
}
@@ -1503,9 +1550,11 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
array[from] = empty;
}
private ensureGroup(position: Position, activate = true): EditorGroup {
private ensureGroup(position: Position, activate = true): [EditorGroup, boolean /* new group opened */] {
let newGroupOpened = false;
let group = this.stacks.groupAt(position);
if (!group) {
newGroupOpened = true;
// Race condition: it could be that someone quickly opens editors one after
// the other and we are asked to open an editor in position 2 before position
@@ -1528,7 +1577,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.stacks.setActive(group);
}
return group;
return [group, newGroupOpened];
}
private modifyGroups(modification: () => void) {

View File

@@ -7,14 +7,11 @@
import 'vs/css!./media/editorpicker';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import labels = require('vs/base/common/labels');
import URI from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import strings = require('vs/base/common/strings');
import { IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import scorer = require('vs/base/common/scorer');
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { IModeService } from 'vs/editor/common/services/modeService';
import { getIconClasses } from 'vs/workbench/browser/labels';
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -25,6 +22,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { EditorInput, toResource, IEditorGroup, IEditorStacksModel } from 'vs/workbench/common/editor';
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
export class EditorPickerEntry extends QuickOpenEntryGroup {
private stacks: IEditorStacksModel;
@@ -89,7 +87,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
}
export abstract class BaseEditorPicker extends QuickOpenHandler {
private scorerCache: { [key: string]: number };
private scorerCache: ScorerCache;
constructor(
@IInstantiationService protected instantiationService: IInstantiationService,
@@ -103,41 +101,38 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
}
public getResults(searchValue: string): TPromise<QuickOpenModel> {
searchValue = searchValue.trim();
const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase();
const editorEntries = this.getEditorEntries();
if (!editorEntries.length) {
return TPromise.as(null);
}
const stacks = this.editorGroupService.getStacksModel();
// Prepare search for scoring
const query = prepareQuery(searchValue);
const entries = editorEntries.filter(e => {
if (!searchValue) {
if (!query.value) {
return true;
}
const resource = e.getResource();
const targetToMatch = resource ? labels.getPathLabel(e.getResource(), this.contextService) : e.getLabel();
if (!scorer.matches(targetToMatch, normalizedSearchValueLowercase)) {
const itemScore = scoreItem(e, query, true, QuickOpenItemAccessor, this.scorerCache);
if (!itemScore.score) {
return false;
}
const { labelHighlights, descriptionHighlights } = QuickOpenEntry.highlight(e, searchValue, true /* fuzzy highlight */);
e.setHighlights(labelHighlights, descriptionHighlights);
e.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch);
return true;
});
// Sorting
if (searchValue) {
const stacks = this.editorGroupService.getStacksModel();
if (query.value) {
entries.sort((e1, e2) => {
if (e1.group !== e2.group) {
return stacks.positionOfGroup(e1.group) - stacks.positionOfGroup(e2.group);
}
return QuickOpenEntry.compareByScore(e1, e2, searchValue, normalizedSearchValueLowercase, this.scorerCache);
return compareItemsByScore(e1, e2, query, true, QuickOpenItemAccessor, this.scorerCache);
});
}
@@ -220,6 +215,8 @@ export abstract class EditorGroupPicker extends BaseEditorPicker {
export class GroupOnePicker extends EditorGroupPicker {
public static readonly ID = 'workbench.picker.editors.one';
protected getPosition(): Position {
return Position.ONE;
}
@@ -227,6 +224,8 @@ export class GroupOnePicker extends EditorGroupPicker {
export class GroupTwoPicker extends EditorGroupPicker {
public static readonly ID = 'workbench.picker.editors.two';
protected getPosition(): Position {
return Position.TWO;
}
@@ -234,6 +233,8 @@ export class GroupTwoPicker extends EditorGroupPicker {
export class GroupThreePicker extends EditorGroupPicker {
public static readonly ID = 'workbench.picker.editors.three';
protected getPosition(): Position {
return Position.THREE;
}
@@ -241,6 +242,8 @@ export class GroupThreePicker extends EditorGroupPicker {
export class AllEditorsPicker extends BaseEditorPicker {
public static readonly ID = 'workbench.picker.editors';
protected getEditorEntries(): EditorPickerEntry[] {
const entries: EditorPickerEntry[] = [];

View File

@@ -8,7 +8,7 @@
import 'vs/css!./media/editorstatus';
import nls = require('vs/nls');
import { TPromise } from 'vs/base/common/winjs.base';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
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');
@@ -17,12 +17,12 @@ import errors = require('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';
import * as browser from 'vs/base/browser/browser';
import { IMode } from 'vs/editor/common/modes';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IEditorAction, ICommonCodeEditor, EndOfLineSequence, IModel } from 'vs/editor/common/editorCommon';
import { IModelLanguageChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/common/linesOperations';
@@ -33,7 +33,7 @@ import { IEditor as IBaseEditor, IEditorInput } from 'vs/platform/editor/common/
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { SUPPORTED_ENCODINGS, IFileService } from 'vs/platform/files/common/files';
import { SUPPORTED_ENCODINGS, IFileService, IFilesConfiguration, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -43,13 +43,19 @@ import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { getCodeEditor as getEditorWidget } from 'vs/editor/common/services/codeEditorService';
import { getCodeEditor as getEditorWidget, getCodeOrDiffEditor } from 'vs/editor/common/services/codeEditorService';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry';
// TODO@Sandeep layer breaker
// tslint:disable-next-line:import-patterns
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/parts/query/services/queryEditorService';
@@ -233,7 +239,7 @@ const nlsMultiSelection = nls.localize('multiSelection', "{0} selections");
const nlsEOLLF = nls.localize('endOfLineLineFeed', "LF");
const nlsEOLCRLF = nls.localize('endOfLineCarriageReturnLineFeed', "CRLF");
const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus");
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Detected");
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 {
@@ -264,6 +270,7 @@ export class EditorStatus implements IStatusbarItem {
private activeEditorListeners: IDisposable[];
private delayedRender: IDisposable;
private toRender: StateChange;
private lastScreenReaderExplanation: ScreenReaderDetectedExplanation;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@@ -272,11 +279,13 @@ export class EditorStatus implements IStatusbarItem {
@IInstantiationService private instantiationService: IInstantiationService,
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
@IModeService private modeService: IModeService,
@ITextFileService private textFileService: ITextFileService
@ITextFileService private textFileService: ITextFileService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
) {
this.toDispose = [];
this.activeEditorListeners = [];
this.state = new State();
this.lastScreenReaderExplanation = null;
}
public render(container: HTMLElement): IDisposable {
@@ -291,6 +300,7 @@ export class EditorStatus implements IStatusbarItem {
this.screenRedearModeElement = append(this.element, $('a.editor-status-screenreadermode.status-bar-info'));
this.screenRedearModeElement.textContent = nlsScreenReaderDetected;
this.screenRedearModeElement.title = nlsScreenReaderDetectedTitle;
this.screenRedearModeElement.onclick = () => this.onScreenReaderModeClick();
hide(this.screenRedearModeElement);
this.selectionElement = append(this.element, $('a.editor-status-selection'));
@@ -472,6 +482,10 @@ export class EditorStatus implements IStatusbarItem {
action.dispose();
}
private onScreenReaderModeClick(): void {
this.lastScreenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation, this.screenRedearModeElement);
}
private onSelectionClick(): void {
this.quickOpenService.show(':'); // "Go to line"
}
@@ -610,15 +624,35 @@ export class EditorStatus implements IStatusbarItem {
this.updateState(update);
}
private _promptedScreenReader: boolean = false;
private onScreenReaderModeChange(editorWidget: ICommonCodeEditor): void {
let screenReaderMode = false;
// We only support text based editors
if (editorWidget) {
const screenReaderDetected = (browser.getAccessibilitySupport() === AccessibilitySupport.Enabled);
if (screenReaderDetected) {
const screenReaderConfiguration = this.configurationService.getConfiguration<IEditorOptions>('editor').accessibilitySupport;
if (screenReaderConfiguration === 'auto') {
// show explanation
if (!this._promptedScreenReader) {
this._promptedScreenReader = true;
setTimeout(() => {
this.onScreenReaderModeClick();
}, 100);
}
}
}
screenReaderMode = (editorWidget.getConfiguration().accessibilitySupport === AccessibilitySupport.Enabled);
}
if (screenReaderMode === false && this.lastScreenReaderExplanation) {
this.lastScreenReaderExplanation.hide();
this.lastScreenReaderExplanation = null;
}
this.updateState({ screenReaderMode: screenReaderMode });
}
@@ -699,7 +733,7 @@ export class EditorStatus implements IStatusbarItem {
private onResourceEncodingChange(resource: uri): void {
const activeEditor = this.editorService.getActiveEditor();
if (activeEditor) {
const activeResource = toResource(activeEditor.input, { supportSideBySide: true, filter: ['file', 'untitled'] });
const activeResource = toResource(activeEditor.input, { supportSideBySide: true });
if (activeResource && activeResource.toString() === resource.toString()) {
return this.onEncodingChange(<IBaseEditor>activeEditor); // only update if the encoding changed for the active resource
}
@@ -755,21 +789,18 @@ export class ChangeModeAction extends Action {
public static ID = 'workbench.action.editor.changeLanguageMode';
public static LABEL = nls.localize('changeMode', "Change Language Mode");
private static FILE_ASSOCIATION_KEY = 'files.associations';
constructor(
actionId: string,
actionLabel: string,
@IModeService private modeService: IModeService,
@IModelService private modelService: IModelService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService,
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IPreferencesService private preferencesService: IPreferencesService,
@IInstantiationService private instantiationService: IInstantiationService,
@ICommandService private commandService: ICommandService,
@IConfigurationEditingService private configurationEditService: IConfigurationEditingService
@IUntitledEditorService private untitledEditorService: IUntitledEditorService
) {
super(actionId, actionLabel);
}
@@ -782,11 +813,16 @@ export class ChangeModeAction extends Action {
}
const textModel = editorWidget.getModel();
const fileResource = toResource(activeEditor.input, { supportSideBySide: true, filter: 'file' });
const resource = toResource(activeEditor.input, { supportSideBySide: true });
let hasLanguageSupport = !!resource;
if (resource.scheme === 'untitled' && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
hasLanguageSupport = false; // no configuration for untitled resources (e.g. "Untitled-1")
}
// Compute mode
let currentModeId: string;
let modeId;
let modeId: string;
if (textModel) {
modeId = textModel.getLanguageIdentifier().language;
currentModeId = this.modeService.getLanguageName(modeId);
@@ -821,7 +857,7 @@ export class ChangeModeAction extends Action {
};
});
if (fileResource) {
if (hasLanguageSupport) {
picks[0].separator = { border: true, label: nls.localize('languagesPicks', "languages (identifier)") };
}
@@ -829,15 +865,14 @@ export class ChangeModeAction extends Action {
let configureModeAssociations: IPickOpenEntry;
let configureModeSettings: IPickOpenEntry;
let galleryAction: Action;
if (fileResource) {
const ext = paths.extname(fileResource.fsPath) || paths.basename(fileResource.fsPath);
if (hasLanguageSupport) {
const ext = paths.extname(resource.fsPath) || paths.basename(resource.fsPath);
galleryAction = this.instantiationService.createInstance(ShowLanguageExtensionsAction, ext);
if (galleryAction.enabled) {
picks.unshift(galleryAction);
}
configureModeSettings = { label: nls.localize('configureModeSettings', "Configure '{0}' language based settings...", currentModeId) };
picks.unshift(configureModeSettings);
configureModeAssociations = { label: nls.localize('configureAssociationsExt', "Configure File Association for '{0}'...", ext) };
@@ -848,7 +883,8 @@ export class ChangeModeAction extends Action {
const autoDetectMode: IPickOpenEntry = {
label: nls.localize('autoDetect', "Auto Detect")
};
if (fileResource) {
if (hasLanguageSupport) {
picks.unshift(autoDetectMode);
}
@@ -864,7 +900,7 @@ export class ChangeModeAction extends Action {
// User decided to permanently configure associations, return right after
if (pick === configureModeAssociations) {
this.configureFileAssociation(fileResource);
this.configureFileAssociation(resource);
return;
}
@@ -876,36 +912,46 @@ export class ChangeModeAction extends Action {
// Change mode for active editor
activeEditor = this.editorService.getActiveEditor();
const editorWidget = getEditorWidget(activeEditor);
if (editorWidget) {
const models: IModel[] = [];
const textModel = editorWidget.getModel();
if (textModel) {
models.push(textModel);
const codeOrDiffEditor = getCodeOrDiffEditor(activeEditor);
const models: IModel[] = [];
if (codeOrDiffEditor.codeEditor) {
const codeEditorModel = codeOrDiffEditor.codeEditor.getModel();
if (codeEditorModel) {
models.push(codeEditorModel);
}
// Find mode
let mode: TPromise<IMode>;
if (pick === autoDetectMode) {
mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true, filter: ['file', 'untitled'] }).fsPath, textModel.getLineContent(1));
} else {
mode = this.modeService.getOrCreateModeByLanguageName(pick.label);
}
if (codeOrDiffEditor.diffEditor) {
const diffEditorModel = codeOrDiffEditor.diffEditor.getModel();
if (diffEditorModel) {
if (diffEditorModel.original) {
models.push(diffEditorModel.original);
}
if (diffEditorModel.modified) {
models.push(diffEditorModel.modified);
}
}
}
// {{SQL CARBON EDIT}}
// Change mode
models.forEach(textModel => {
let self = this;
mode.then((modeValue) => {
QueryEditorService.sqlLanguageModeCheck(textModel, modeValue, activeEditor).then((newTextModel) => {
if (newTextModel) {
self.modelService.setMode(newTextModel, modeValue);
}
});
// Find mode
let mode: TPromise<IMode>;
if (pick === autoDetectMode) {
mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true }).fsPath, textModel.getLineContent(1));
} else {
mode = this.modeService.getOrCreateModeByLanguageName(pick.label);
}
// {{SQL CARBON EDIT}}
// Change mode
models.forEach(textModel => {
let self = this;
mode.then((modeValue) => {
QueryEditorService.sqlLanguageModeCheck(textModel, modeValue, activeEditor).then((newTextModel) => {
if (newTextModel) {
self.modelService.setMode(newTextModel, modeValue);
}
});
});
}
});
});
}
@@ -928,7 +974,7 @@ export class ChangeModeAction extends Action {
TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).done(() => {
this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).done(language => {
if (language) {
const fileAssociationsConfig = this.configurationService.lookup(ChangeModeAction.FILE_ASSOCIATION_KEY);
const fileAssociationsConfig = this.configurationService.inspect(FILES_ASSOCIATIONS_CONFIG);
let associationKey: string;
if (extension && basename[0] !== '.') {
@@ -951,8 +997,7 @@ export class ChangeModeAction extends Action {
currentAssociations[associationKey] = language.id;
// Write config
this.configurationEditingService.writeConfiguration(target, { key: ChangeModeAction.FILE_ASSOCIATION_KEY, value: currentAssociations });
this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target);
}
});
});
@@ -1070,7 +1115,7 @@ export class ChangeEncodingAction extends Action {
actionLabel: string,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
@ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService,
@IFileService private fileService: IFileService
) {
super(actionId, actionLabel);
@@ -1112,19 +1157,22 @@ export class ChangeEncodingAction extends Action {
return void 0;
}
const resource = toResource(activeEditor.input, { filter: ['file', 'untitled'], supportSideBySide: true });
const resource = toResource(activeEditor.input, { supportSideBySide: true });
return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */)
.then(() => {
if (!resource || resource.scheme !== 'file') {
return TPromise.as(null); // encoding detection only possible for file resources
if (!resource || !this.fileService.canHandleResource(resource)) {
return TPromise.as(null); // encoding detection only possible for resources the file service can handle
}
return this.fileService.resolveContent(resource, { autoGuessEncoding: true, acceptTextOnly: true }).then(content => content.encoding, err => null);
})
.then((guessedEncoding: string) => {
const isReopenWithEncoding = (action === reopenWithEncodingPick);
const configuredEncoding = this.configurationService.lookup('files.encoding', { resource }).value;
const config = this.textResourceConfigurationService.getConfiguration(resource) as IFilesConfiguration;
const configuredEncoding = config && config.files && config.files.encoding;
let directMatchIndex: number;
let aliasMatchIndex: number;
@@ -1153,7 +1201,7 @@ export class ChangeEncodingAction extends Action {
aliasMatchIndex = index;
}
return { id: key, label: SUPPORTED_ENCODINGS[key].labelLong };
return { id: key, label: SUPPORTED_ENCODINGS[key].labelLong, description: key };
});
// If we have a guessed encoding, show it first unless it matches the configured encoding
@@ -1178,3 +1226,107 @@ export class ChangeEncodingAction extends Action {
});
}
}
class ScreenReaderDetectedExplanation {
private _isDisposed: boolean;
private _toDispose: IDisposable[];
constructor(
anchorElement: HTMLElement,
@IThemeService private readonly themeService: IThemeService,
@IContextViewService private readonly contextViewService: IContextViewService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
) {
this._isDisposed = false;
this._toDispose = [];
this.contextViewService.showContextView({
getAnchor: () => anchorElement,
render: (container) => {
return this.renderContents(container);
},
onDOMEvent: (e, activeElement) => {
},
onHide: () => {
this.dispose();
}
});
}
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', {
'aria-hidden': 'true'
});
const title = $('h2.title', {}, nls.localize('screenReaderDetectedExplanation.title', "Screen Reader Optimized"));
domNode.appendChild(title);
const closeBtn = $('div.cancel');
this._toDispose.push(addDisposableListener(closeBtn, 'click', () => {
this.contextViewService.hideContextView();
}));
domNode.appendChild(closeBtn);
const question = $('p.question', {}, nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate VS Code?"));
domNode.appendChild(question);
const yesBtn = $('div.button', {}, nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"));
this._toDispose.push(addDisposableListener(yesBtn, 'click', () => {
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
domNode.appendChild(yesBtn);
const noBtn = $('div.button', {}, nls.localize('screenReaderDetectedExplanation.answerNo', "No"));
this._toDispose.push(addDisposableListener(noBtn, 'click', () => {
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
domNode.appendChild(noBtn);
const clear = $('div');
clear.style.clear = 'both';
domNode.appendChild(clear);
const br = $('br');
domNode.appendChild(br);
const hr = $('hr');
domNode.appendChild(hr);
const explanation1 = $('p.body1', {}, nls.localize('screenReaderDetectedExplanation.body1', "VS Code is now optimized for usage with a screen reader."));
domNode.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);
container.appendChild(domNode);
this._toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground }, colors => {
domNode.style.backgroundColor = colors.editorWidgetBackground;
if (colors.widgetShadow) {
domNode.style.boxShadow = `0 2px 8px ${colors.widgetShadow}`;
}
}));
return {
dispose: () => { this.dispose(); }
};
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#C5C5C5" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -18,7 +18,78 @@
padding: 0 5px 0 5px;
}
.monaco-workbench .editor-statusbar-item > .editor-status-metadata,
.monaco-workbench > .part.statusbar > .statusbar-item > .editor-statusbar-item > a.editor-status-screenreadermode {
.monaco-workbench .editor-statusbar-item > .editor-status-metadata {
cursor: default !important;
}
.monaco-shell .screen-reader-detected-explanation {
width: 420px;
top: 30px;
right: 6px;
padding: 1em;
cursor: default;
}
.monaco-shell .screen-reader-detected-explanation .cancel {
position: absolute;
top: 0;
right: 0;
margin: .5em 0 0;
padding: .5em;
width: 22px;
height: 22px;
border: none;
cursor: pointer;
}
.monaco-shell .screen-reader-detected-explanation h2 {
margin: 0;
padding: 0;
font-weight: 400;
font-size: 1.8em;
}
.monaco-shell .screen-reader-detected-explanation p {
font-size: 1.2em;
}
.monaco-shell .screen-reader-detected-explanation p.question {
font-size: 1.4em;
font-weight: bold;
}
.monaco-shell .screen-reader-detected-explanation .button {
color: white;
border: none;
cursor: pointer;
background-color: #007ACC;
padding-left: 12px;
padding-right: 12px;
border: 4px solid #007ACC;
border-radius: 4px;
float: left;
margin-right: 5px;
}
.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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -10,13 +10,15 @@ import errors = require('vs/base/common/errors');
import { IEditorGroup, toResource } from 'vs/workbench/common/editor';
import DOM = require('vs/base/browser/dom');
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { EditorLabel } from 'vs/workbench/browser/labels';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { Verbosity } from 'vs/platform/editor/common/editor';
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
export class NoTabsTitleControl extends TitleControl {
private titleContainer: HTMLElement;
private editorLabel: EditorLabel;
private editorLabel: ResourceLabel;
private titleTouchSupport: Gesture;
public setContext(group: IEditorGroup): void {
super.setContext(group);
@@ -29,17 +31,22 @@ export class NoTabsTitleControl extends TitleControl {
this.titleContainer = parent;
// Gesture Support
this.titleTouchSupport = new Gesture(this.titleContainer);
// Pin on double click
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
// Detect mouse click
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CLICK, (e: MouseEvent) => this.onTitleClick(e)));
// Detect touch
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
// Editor Label
this.editorLabel = this.instantiationService.createInstance(EditorLabel, this.titleContainer, void 0);
this.editorLabel = this.instantiationService.createInstance(ResourceLabel, this.titleContainer, void 0);
this.toUnbind.push(this.editorLabel);
this.toUnbind.push(DOM.addDisposableListener(this.editorLabel.labelElement, DOM.EventType.CLICK, (e: MouseEvent) => this.onTitleLabelClick(e)));
this.toUnbind.push(DOM.addDisposableListener(this.editorLabel.descriptionElement, DOM.EventType.CLICK, (e: MouseEvent) => this.onTitleLabelClick(e)));
this.toUnbind.push(this.editorLabel.onClick(e => this.onTitleLabelClick(e)));
// Right Actions Container
const actionsContainer = document.createElement('div');
@@ -51,6 +58,7 @@ export class NoTabsTitleControl extends TitleControl {
// Context Menu
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu({ group: this.context, editor: this.context.activeEditor }, e, this.titleContainer)));
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu({ group: this.context, editor: this.context.activeEditor }, e, this.titleContainer)));
}
private onTitleLabelClick(e: MouseEvent): void {
@@ -71,7 +79,7 @@ export class NoTabsTitleControl extends TitleControl {
this.editorGroupService.pinEditor(group, group.activeEditor);
}
private onTitleClick(e: MouseEvent): void {
private onTitleClick(e: MouseEvent | GestureEvent): void {
if (!this.context) {
return;
}
@@ -79,7 +87,7 @@ export class NoTabsTitleControl extends TitleControl {
const group = this.context;
// Close editor on middle mouse click
if (e.button === 1 /* Middle Button */) {
if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) {
this.closeEditorAction.run({ group, editor: group.activeEditor }).done(null, errors.onUnexpectedError);
}
@@ -119,7 +127,15 @@ export class NoTabsTitleControl extends TitleControl {
// Editor Label
const resource = toResource(editor, { supportSideBySide: true });
const name = editor.getName() || '';
const description = isActive ? (editor.getDescription() || '') : '';
const labelFormat = this.editorGroupService.getTabOptions().labelFormat;
let description: string;
if (labelFormat === 'default' && !isActive) {
description = ''; // hide description when group is not active and style is 'default'
} else {
description = editor.getDescription(this.getVerbosity(labelFormat)) || '';
}
let title = editor.getTitle(Verbosity.LONG);
if (description === title) {
title = ''; // dont repeat what is already shown
@@ -135,4 +151,18 @@ export class NoTabsTitleControl extends TitleControl {
// Update Editor Actions Toolbar
this.updateEditorActionsToolbar();
}
private getVerbosity(style: string): Verbosity {
switch (style) {
case 'short': return Verbosity.SHORT;
case 'long': return Verbosity.LONG;
default: return Verbosity.MEDIUM;
}
}
public dispose(): void {
super.dispose();
this.titleTouchSupport.dispose();
}
}

View File

@@ -4,13 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import * as strings from 'vs/base/common/strings';
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 { IEditorRegistry, Extensions as EditorExtensions, EditorInput, EditorOptions, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
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';
@@ -18,6 +17,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
export class SideBySideEditor extends BaseEditor {
@@ -110,7 +110,7 @@ export class SideBySideEditor extends BaseEditor {
return this.detailsEditor;
}
private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options?: EditorOptions): TPromise<void> {
private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options?: EditorOptions): void {
if (!newInput.matches(oldInput)) {
if (oldInput) {
this.disposeEditors();
@@ -126,24 +126,21 @@ export class SideBySideEditor extends BaseEditor {
}
}
private setNewInput(newInput: SideBySideEditorInput, options?: EditorOptions): TPromise<void> {
return TPromise.join([
this._createEditor(<EditorInput>newInput.details, this.detailsEditorContainer),
this._createEditor(<EditorInput>newInput.master, this.masterEditorContainer)
]).then(result => this.onEditorsCreated(result[0], result[1], newInput.details, newInput.master, options));
private setNewInput(newInput: SideBySideEditorInput, options?: EditorOptions): void {
const detailsEditor = this._createEditor(<EditorInput>newInput.details, this.detailsEditorContainer);
const masterEditor = this._createEditor(<EditorInput>newInput.master, this.masterEditorContainer);
this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options);
}
private _createEditor(editorInput: EditorInput, container: HTMLElement): TPromise<BaseEditor> {
private _createEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor {
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editorInput);
if (!descriptor) {
return TPromise.wrapError<BaseEditor>(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput)));
}
return this.instantiationService.createInstance(<EditorDescriptor>descriptor)
.then((editor: BaseEditor) => {
editor.create(new Builder(container));
editor.setVisible(this.isVisible(), this.position);
return editor;
});
const editor = descriptor.instantiate(this.instantiationService);
editor.create(new Builder(container));
editor.setVisible(this.isVisible(), this.position);
return editor;
}
private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions): TPromise<void> {

View File

@@ -17,10 +17,11 @@ import { ActionRunner, IAction } from 'vs/base/common/actions';
import { Position, IEditorInput, Verbosity, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
import { IEditorGroup, toResource } from 'vs/workbench/common/editor';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
import { KeyCode } from 'vs/base/common/keyCodes';
import { EditorLabel } from 'vs/workbench/browser/labels';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IMessageService } from 'vs/platform/message/common/message';
@@ -37,7 +38,6 @@ import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElemen
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { extractResources } from 'vs/base/browser/dnd';
import { getOrSet } from 'vs/base/common/map';
import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER } from 'vs/workbench/common/theme';
@@ -47,16 +47,17 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
interface IEditorInputLabel {
name: string;
hasAmbiguousName?: boolean;
description?: string;
title?: string;
}
type AugmentedLabel = IEditorInputLabel & { editor: IEditorInput };
export class TabsTitleControl extends TitleControl {
private titleContainer: HTMLElement;
private tabsContainer: HTMLElement;
private activeTab: HTMLElement;
private editorLabels: EditorLabel[];
private editorLabels: ResourceLabel[];
private scrollbar: ScrollableElement;
private tabDisposeables: IDisposable[];
private blockRevealActiveTab: boolean;
@@ -264,7 +265,7 @@ export class TabsTitleControl extends TitleControl {
// Compute labels and protect against duplicates
const editorsOfGroup = this.context.getEditors();
const labels = this.getUniqueTabLabels(editorsOfGroup);
const labels = this.getTabLabels(editorsOfGroup);
// Tab label and styles
editorsOfGroup.forEach((editor, index) => {
@@ -276,7 +277,7 @@ export class TabsTitleControl extends TitleControl {
const label = labels[index];
const name = label.name;
const description = label.hasAmbiguousName && label.description ? label.description : '';
const description = label.description || '';
const title = label.title || '';
// Container
@@ -294,7 +295,8 @@ export class TabsTitleControl extends TitleControl {
// Label
const tabLabel = this.editorLabels[index];
tabLabel.setLabel({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { extraClasses: ['tab-label'], italic: !isPinned });
// {{SQL CARBON EDIT}} -- add title in options passed
tabLabel.setLabel({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { extraClasses: ['tab-label'], italic: !isPinned, title });
// Active state
if (isTabActive) {
@@ -338,56 +340,108 @@ export class TabsTitleControl extends TitleControl {
this.layout();
}
private getUniqueTabLabels(editors: IEditorInput[]): IEditorInputLabel[] {
const labels: IEditorInputLabel[] = [];
const mapLabelToDuplicates = new Map<string, IEditorInputLabel[]>();
const mapLabelAndDescriptionToDuplicates = new Map<string, IEditorInputLabel[]>();
private getTabLabels(editors: IEditorInput[]): IEditorInputLabel[] {
const labelFormat = this.editorGroupService.getTabOptions().labelFormat;
const { verbosity, shortenDuplicates } = this.getLabelConfigFlags(labelFormat);
// Build labels and descriptions for each editor
editors.forEach(editor => {
const name = editor.getName();
let description = editor.getDescription();
if (mapLabelAndDescriptionToDuplicates.has(`${name}${description}`)) {
description = editor.getDescription(true); // try verbose description if name+description already exists
}
const labels = editors.map(editor => ({
editor,
name: editor.getName(),
description: editor.getDescription(verbosity),
title: editor.getTitle(Verbosity.LONG)
}));
const item: IEditorInputLabel = {
name,
description,
title: editor.getTitle(Verbosity.LONG)
};
labels.push(item);
getOrSet(mapLabelToDuplicates, item.name, []).push(item);
if (typeof description === 'string') {
getOrSet(mapLabelAndDescriptionToDuplicates, `${item.name}${item.description}`, []).push(item);
}
});
// Mark duplicates and shorten their descriptions
mapLabelToDuplicates.forEach(duplicates => {
if (duplicates.length > 1) {
duplicates = duplicates.filter(d => {
// we could have items with equal label and description. in that case it does not make much
// sense to produce a shortened version of the label, so we ignore those kind of items
return typeof d.description === 'string' && mapLabelAndDescriptionToDuplicates.get(`${d.name}${d.description}`).length === 1;
});
if (duplicates.length > 1) {
const shortenedDescriptions = shorten(duplicates.map(duplicate => duplicate.description));
duplicates.forEach((duplicate, i) => {
duplicate.description = shortenedDescriptions[i];
duplicate.hasAmbiguousName = true;
});
}
}
});
// Shorten labels as needed
if (shortenDuplicates) {
this.shortenTabLabels(labels);
}
return labels;
}
private shortenTabLabels(labels: AugmentedLabel[]): void {
// Gather duplicate titles, while filtering out invalid descriptions
const mapTitleToDuplicates = new Map<string, AugmentedLabel[]>();
for (const label of labels) {
if (typeof label.description === 'string') {
getOrSet(mapTitleToDuplicates, label.name, []).push(label);
} else {
label.description = '';
}
}
// Identify duplicate titles and shorten descriptions
mapTitleToDuplicates.forEach(duplicateTitles => {
// Remove description if the title isn't duplicated
if (duplicateTitles.length === 1) {
duplicateTitles[0].description = '';
return;
}
// Identify duplicate descriptions
const mapDescriptionToDuplicates = new Map<string, AugmentedLabel[]>();
for (const label of duplicateTitles) {
getOrSet(mapDescriptionToDuplicates, label.description, []).push(label);
}
// For editors with duplicate descriptions, check whether any long descriptions differ
let useLongDescriptions = false;
mapDescriptionToDuplicates.forEach((duplicateDescriptions, name) => {
if (!useLongDescriptions && duplicateDescriptions.length > 1) {
const [first, ...rest] = duplicateDescriptions.map(({ editor }) => editor.getDescription(Verbosity.LONG));
useLongDescriptions = rest.some(description => description !== first);
}
});
// If so, replace all descriptions with long descriptions
if (useLongDescriptions) {
mapDescriptionToDuplicates.clear();
duplicateTitles.forEach(label => {
label.description = label.editor.getDescription(Verbosity.LONG);
getOrSet(mapDescriptionToDuplicates, label.description, []).push(label);
});
}
// Obtain final set of descriptions
const descriptions: string[] = [];
mapDescriptionToDuplicates.forEach((_, description) => descriptions.push(description));
// Remove description if all descriptions are identical
if (descriptions.length === 1) {
for (const label of mapDescriptionToDuplicates.get(descriptions[0])) {
label.description = '';
}
return;
}
// Shorten descriptions
const shortenedDescriptions = shorten(descriptions);
descriptions.forEach((description, i) => {
for (const label of mapDescriptionToDuplicates.get(description)) {
label.description = shortenedDescriptions[i];
}
});
});
}
private getLabelConfigFlags(value: string) {
switch (value) {
case 'short':
return { verbosity: Verbosity.SHORT, shortenDuplicates: false };
case 'medium':
return { verbosity: Verbosity.MEDIUM, shortenDuplicates: false };
case 'long':
return { verbosity: Verbosity.LONG, shortenDuplicates: false };
default:
return { verbosity: Verbosity.MEDIUM, shortenDuplicates: true };
}
}
protected doRefresh(): void {
const group = this.context;
const editor = group && group.activeEditor;
@@ -451,8 +505,11 @@ export class TabsTitleControl extends TitleControl {
tabContainer.setAttribute('role', 'presentation'); // cannot use role "tab" here due to https://github.com/Microsoft/vscode/issues/8659
DOM.addClass(tabContainer, 'tab');
// Gesture Support
const gestureSupport = new Gesture(tabContainer);
// Tab Editor Label
const editorLabel = this.instantiationService.createInstance(EditorLabel, tabContainer, void 0);
const editorLabel = this.instantiationService.createInstance(ResourceLabel, tabContainer, void 0);
this.editorLabels.push(editorLabel);
// Tab Close
@@ -466,7 +523,7 @@ export class TabsTitleControl extends TitleControl {
// Eventing
const disposable = this.hookTabListeners(tabContainer, index);
this.tabDisposeables.push(combinedDisposable([disposable, bar, editorLabel]));
this.tabDisposeables.push(combinedDisposable([disposable, bar, editorLabel, gestureSupport]));
return tabContainer;
}
@@ -516,14 +573,42 @@ export class TabsTitleControl extends TitleControl {
private hookTabListeners(tab: HTMLElement, index: number): IDisposable {
const disposables: IDisposable[] = [];
// Open on Click
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
const handleClickOrTouch = (e: MouseEvent | GestureEvent) => {
tab.blur();
if (e instanceof MouseEvent && e.button !== 0) {
if (e.button === 1) {
return false; // required due to https://github.com/Microsoft/vscode/issues/16690
}
return void 0; // only for left mouse click
}
const { editor, position } = this.toTabContext(index);
if (e.button === 0 /* Left Button */ && !this.isTabActionBar((e.target || e.srcElement) as HTMLElement)) {
if (!this.isTabActionBar((e.target || e.srcElement) as HTMLElement)) {
setTimeout(() => this.editorService.openEditor(editor, null, position).done(null, errors.onUnexpectedError)); // timeout to keep focus in editor after mouse up
}
return void 0;
};
const showContextMenu = (e: Event) => {
DOM.EventHelper.stop(e);
const { group, editor } = this.toTabContext(index);
this.onContextMenu({ group, editor }, e, tab);
};
// Open on Click
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => handleClickOrTouch(e)));
// Open on Touch
disposables.push(DOM.addDisposableListener(tab, TouchEventType.Tap, (e: GestureEvent) => handleClickOrTouch(e)));
// Touch Scroll Support
disposables.push(DOM.addDisposableListener(tab, TouchEventType.Change, (e: GestureEvent) => {
this.tabsContainer.scrollLeft -= e.translationX;
}));
// Close on mouse middle click
@@ -540,14 +625,15 @@ export class TabsTitleControl extends TitleControl {
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
if (event.shiftKey && event.keyCode === KeyCode.F10) {
DOM.EventHelper.stop(e);
const { group, editor } = this.toTabContext(index);
this.onContextMenu({ group, editor }, e, tab);
showContextMenu(e);
}
}));
// Context menu on touch context menu gesture
disposables.push(DOM.addDisposableListener(tab, TouchEventType.Contextmenu, (e: GestureEvent) => {
showContextMenu(e);
}));
// Keyboard accessibility
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
@@ -617,12 +703,15 @@ export class TabsTitleControl extends TitleControl {
e.dataTransfer.effectAllowed = 'copyMove';
// Insert transfer accordingly
const fileResource = toResource(editor, { supportSideBySide: true, filter: 'file' });
if (fileResource) {
const resource = fileResource.toString();
e.dataTransfer.setData('URL', resource); // enables cross window DND of tabs
e.dataTransfer.setData('DownloadURL', [MIME_BINARY, editor.getName(), resource].join(':')); // enables support to drag a tab as file to desktop
const resource = toResource(editor, { supportSideBySide: true });
if (resource) {
const resourceStr = resource.toString();
e.dataTransfer.setData('URL', resourceStr); // enables cross window DND of tabs
e.dataTransfer.setData('text/plain', getPathLabel(resource)); // enables dropping tab resource path into text controls
if (resource.scheme === 'file') {
e.dataTransfer.setData('DownloadURL', [MIME_BINARY, editor.getName(), resourceStr].join(':')); // enables support to drag a tab as file to desktop
}
}
}));
@@ -635,7 +724,21 @@ export class TabsTitleControl extends TitleControl {
// Drag over
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.DRAG_ENTER, (e: DragEvent) => {
counter++;
this.updateDropFeedback(tab, true, index);
// Find out if the currently dragged editor is this tab and in that
// case we do not want to show any drop feedback
let draggedEditorIsTab = false;
const draggedEditor = TabsTitleControl.getDraggedEditor();
if (draggedEditor) {
const { group, editor } = this.toTabContext(index);
if (draggedEditor.editor === editor && draggedEditor.group === group) {
draggedEditorIsTab = true;
}
}
if (!draggedEditorIsTab) {
this.updateDropFeedback(tab, true, index);
}
}));
// Drag leave

View File

@@ -22,14 +22,13 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator';
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel';
import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService';
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IModeService } from 'vs/editor/common/services/modeService';
@@ -112,16 +111,12 @@ export class TextDiffEditor extends BaseTextEditor {
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
const oldInput = this.input;
super.setInput(input, options);
// Detect options
// Return early for same input unless we force to open
const forceOpen = options && options.forceOpen;
if (!forceOpen && input.matches(this.input)) {
// Same Input
if (!forceOpen && input.matches(oldInput)) {
// TextOptions (avoiding instanceof here for a reason, do not change!)
// Still apply options if any (avoiding instanceof here for a reason, do not change!)
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(<IDiffEditor>this.getControl(), ScrollType.Smooth);
@@ -135,49 +130,51 @@ export class TextDiffEditor extends BaseTextEditor {
this.diffNavigator.dispose();
}
// Different Input (Reload)
return input.resolve(true).then(resolvedModel => {
// Set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then(resolvedModel => {
// Assert Model Instance
if (!(resolvedModel instanceof TextDiffEditorModel) && this.openAsBinary(input, options)) {
return null;
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading a diff takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// Editor
const diffEditor = <IDiffEditor>this.getControl();
diffEditor.setModel((<TextDiffEditorModel>resolvedModel).textDiffEditorModel);
// Handle TextOptions
let alwaysRevealFirst = true;
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
// Assert Model Instance
if (!(resolvedModel instanceof TextDiffEditorModel) && this.openAsBinary(input, options)) {
return null;
}
}
// Listen on diff updated changes to reveal the first change
this.diffNavigator = new DiffNavigator(diffEditor, {
alwaysRevealFirst
// Assert that the current input is still the one we expect. This prevents a race condition when loading a diff takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// Editor
const diffEditor = <IDiffEditor>this.getControl();
diffEditor.setModel((<TextDiffEditorModel>resolvedModel).textDiffEditorModel);
// Handle TextOptions
let alwaysRevealFirst = true;
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
}
}
// Listen on diff updated changes to reveal the first change
this.diffNavigator = new DiffNavigator(diffEditor, {
alwaysRevealFirst
});
this.diffNavigator.addListener(DiffNavigator.Events.UPDATED, () => {
this.nextDiffAction.updateEnablement();
this.previousDiffAction.updateEnablement();
});
}, error => {
// In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff.
if (this.isFileBinaryError(error) && this.openAsBinary(input, options)) {
return null;
}
// Otherwise make sure the error bubbles up
return TPromise.wrapError(error);
});
this.diffNavigator.addListener(DiffNavigator.Events.UPDATED, () => {
this.nextDiffAction.updateEnablement();
this.previousDiffAction.updateEnablement();
});
}, error => {
// In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff.
if (this.isFileBinaryError(error) && this.openAsBinary(input, options)) {
return null;
}
// Otherwise make sure the error bubbles up
return TPromise.wrapError(error);
});
}

View File

@@ -14,7 +14,7 @@ import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import DOM = require('vs/base/browser/dom');
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { EditorInput, EditorOptions, toResource } from 'vs/workbench/common/editor';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorViewState, IEditor, isCommonCodeEditor, isCommonDiffEditor } from 'vs/editor/common/editorCommon';
import { Position } from 'vs/platform/editor/common/editor';
@@ -66,7 +66,7 @@ export abstract class BaseTextEditor extends BaseEditor {
) {
super(id, telemetryService, themeService);
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getConfiguration<IEditorConfiguration>(this.getResource()))));
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getConfiguration<IEditorConfiguration>(this.getResource()))));
}
protected get instantiationService(): IInstantiationService {
@@ -196,6 +196,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());
});
}
@@ -240,7 +241,7 @@ export abstract class BaseTextEditor extends BaseEditor {
*/
protected saveTextEditorViewState(key: string): void {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
let textEditorViewStateMemento = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
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;
@@ -248,7 +249,7 @@ export abstract class BaseTextEditor extends BaseEditor {
const editorViewState = this.getControl().saveViewState();
let lastKnownViewState: ITextEditorViewState = textEditorViewStateMemento[key];
let lastKnownViewState = textEditorViewStateMemento[key];
if (!lastKnownViewState) {
lastKnownViewState = Object.create(null);
textEditorViewStateMemento[key] = lastKnownViewState;
@@ -264,7 +265,7 @@ export abstract class BaseTextEditor extends BaseEditor {
*/
protected clearTextEditorViewState(keys: string[]): void {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
const textEditorViewStateMemento = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
const textEditorViewStateMemento: { [key: string]: { [position: number]: IEditorViewState } } = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
if (textEditorViewStateMemento) {
keys.forEach(key => delete textEditorViewStateMemento[key]);
}
@@ -275,9 +276,9 @@ export abstract class BaseTextEditor extends BaseEditor {
*/
protected loadTextEditorViewState(key: string): IEditorViewState {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
const textEditorViewStateMemento = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
const textEditorViewStateMemento: { [key: string]: { [position: number]: IEditorViewState } } = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
if (textEditorViewStateMemento) {
const viewState: ITextEditorViewState = textEditorViewStateMemento[key];
const viewState = textEditorViewStateMemento[key];
if (viewState) {
return viewState[this.position];
}
@@ -317,7 +318,7 @@ export abstract class BaseTextEditor extends BaseEditor {
}
if (this.input) {
return toResource(this.input);
return this.input.getResource();
}
return null;

View File

@@ -55,16 +55,12 @@ export class TextResourceEditor extends BaseTextEditor {
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
const oldInput = this.input;
super.setInput(input, options);
// Detect options
// Return early for same input unless we force to open
const forceOpen = options && options.forceOpen;
if (!forceOpen && input.matches(this.input)) {
// Same Input
if (!forceOpen && input.matches(oldInput)) {
// TextOptions (avoiding instanceof here for a reason, do not change!)
// Still apply options if any (avoiding instanceof here for a reason, do not change!)
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(this.getControl(), ScrollType.Smooth);
@@ -74,39 +70,41 @@ export class TextResourceEditor extends BaseTextEditor {
}
// Remember view settings if input changes
this.saveTextEditorViewStateForInput(oldInput);
this.saveTextEditorViewStateForInput(this.input);
// Different Input (Reload)
return input.resolve(true).then((resolvedModel: EditorModel) => {
// Set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then((resolvedModel: EditorModel) => {
// Assert Model instance
if (!(resolvedModel instanceof BaseTextEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as text'));
}
// Assert Model instance
if (!(resolvedModel instanceof BaseTextEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as text'));
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// Set Editor Model
const textEditor = this.getControl();
const textEditorModel = resolvedModel.textEditorModel;
textEditor.setModel(textEditorModel);
// Set Editor Model
const textEditor = this.getControl();
const textEditorModel = resolvedModel.textEditorModel;
textEditor.setModel(textEditorModel);
// Apply Options from TextOptions
let optionsGotApplied = false;
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
optionsGotApplied = textOptions.apply(textEditor, ScrollType.Immediate);
}
// Apply Options from TextOptions
let optionsGotApplied = false;
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
optionsGotApplied = textOptions.apply(textEditor, ScrollType.Immediate);
}
// Otherwise restore View State
if (!optionsGotApplied) {
this.restoreViewState(input);
}
// Otherwise restore View State
if (!optionsGotApplied) {
this.restoreViewState(input);
}
return void 0;
return void 0;
});
});
}

View File

@@ -60,6 +60,7 @@ export interface ITitleAreaControl {
getContainer(): HTMLElement;
refresh(instant?: boolean): void;
update(instant?: boolean): void;
updateEditorActionsToolbar(): void;
layout(): void;
dispose(): void;
}
@@ -259,6 +260,12 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
// Log in telemetry
if (this.telemetryService) {
/* __GDPR__
"workbenchActionExecuted" : {
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: 'editorPart' });
}
}));
@@ -334,7 +341,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
return { primary, secondary };
}
protected updateEditorActionsToolbar(): void {
public updateEditorActionsToolbar(): void {
const group = this.context;
if (!group) {
return;
@@ -543,7 +550,7 @@ export function handleWorkspaceExternalDrop(
// Multiple folders: Create new workspace with folders and open
else if (folders.length > 1) {
workspacesToOpen = workspacesService.createWorkspace([...folders].map(folder => folder.fsPath)).then(workspace => [workspace.configPath]);
workspacesToOpen = workspacesService.createWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [workspace.configPath]);
}
// Open

View File

@@ -40,13 +40,13 @@ export abstract class BaseWebviewEditor extends BaseEditor {
protected saveViewState(resource: URI | string, editorViewState: HtmlPreviewEditorViewState): void {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
let editorViewStateMemento = memento[this.viewStateStorageKey];
let editorViewStateMemento: { [key: string]: { [position: number]: HtmlPreviewEditorViewState } } = memento[this.viewStateStorageKey];
if (!editorViewStateMemento) {
editorViewStateMemento = Object.create(null);
memento[this.viewStateStorageKey] = editorViewStateMemento;
}
let fileViewState: HtmlPreviewEditorViewStates = editorViewStateMemento[resource.toString()];
let fileViewState = editorViewStateMemento[resource.toString()];
if (!fileViewState) {
fileViewState = Object.create(null);
editorViewStateMemento[resource.toString()] = fileViewState;
@@ -59,9 +59,9 @@ export abstract class BaseWebviewEditor extends BaseEditor {
protected loadViewState(resource: URI | string): HtmlPreviewEditorViewState | null {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
const editorViewStateMemento = memento[this.viewStateStorageKey];
const editorViewStateMemento: { [key: string]: { [position: number]: HtmlPreviewEditorViewState } } = memento[this.viewStateStorageKey];
if (editorViewStateMemento) {
const fileViewState: HtmlPreviewEditorViewStates = editorViewStateMemento[resource.toString()];
const fileViewState = editorViewStateMemento[resource.toString()];
if (fileViewState) {
return fileViewState[this.position];
}