Vscode merge (#4582)

* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd

* fix issues with merges

* bump node version in azpipe

* replace license headers

* remove duplicate launch task

* fix build errors

* fix build errors

* fix tslint issues

* working through package and linux build issues

* more work

* wip

* fix packaged builds

* working through linux build errors

* wip

* wip

* wip

* fix mac and linux file limits

* iterate linux pipeline

* disable editor typing

* revert series to parallel

* remove optimize vscode from linux

* fix linting issues

* revert testing change

* add work round for new node

* readd packaging for extensions

* fix issue with angular not resolving decorator dependencies
This commit is contained in:
Anthony Dresser
2019-03-19 17:44:35 -07:00
committed by GitHub
parent 833d197412
commit 87765e8673
1879 changed files with 54505 additions and 38058 deletions

View File

@@ -8,7 +8,7 @@ import { EditorInput, EditorOptions, IEditor, GroupIdentifier, IEditorMemento }
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { LRUCache } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';

View File

@@ -11,7 +11,7 @@ import { localize } from 'vs/nls';
import { IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import { GroupIdentifier } from 'vs/workbench/common/editor';
@@ -19,7 +19,7 @@ export const IBreadcrumbsService = createDecorator<IBreadcrumbsService>('IEditor
export interface IBreadcrumbsService {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable;
@@ -29,7 +29,7 @@ export interface IBreadcrumbsService {
export class BreadcrumbsService implements IBreadcrumbsService {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
private readonly _map = new Map<number, BreadcrumbsWidget>();

View File

@@ -41,10 +41,12 @@ import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workb
import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker';
import { SideBySideEditorInput } from 'vs/workbench/common/editor';
import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
class Item extends BreadcrumbsItem {
@@ -148,6 +150,7 @@ export class BreadcrumbsControl {
private _disposables = new Array<IDisposable>();
private _breadcrumbsDisposables = new Array<IDisposable>();
private _breadcrumbsPickerShowing = false;
private _breadcrumbsPickerIgnoreOnceItem: BreadcrumbsItem | undefined;
constructor(
container: HTMLElement,
@@ -196,7 +199,7 @@ export class BreadcrumbsControl {
this.domNode.remove();
}
layout(dim: dom.Dimension): void {
layout(dim: dom.Dimension | undefined): void {
this._widget.layout(dim);
}
@@ -219,7 +222,7 @@ export class BreadcrumbsControl {
input = input.master;
}
if (!input || !input.getResource() || (input.getResource().scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()))) {
if (!input || !input.getResource() || (input.getResource()!.scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()!))) {
// cleanup and return when there is no input or when
// we cannot handle this input
this._ckBreadcrumbsPossible.set(false);
@@ -236,7 +239,7 @@ export class BreadcrumbsControl {
this._ckBreadcrumbsPossible.set(true);
let editor = this._getActiveCodeEditor();
let model = new EditorBreadcrumbsModel(input.getResource(), editor, this._workspaceService, this._configurationService);
let model = new EditorBreadcrumbsModel(input.getResource()!, editor, this._workspaceService, this._configurationService);
dom.toggleClass(this.domNode, 'relative-path', model.isRelative());
let updateBreadcrumbs = () => {
@@ -260,9 +263,12 @@ export class BreadcrumbsControl {
return true;
}
private _getActiveCodeEditor(): ICodeEditor {
private _getActiveCodeEditor(): ICodeEditor | undefined {
if (!this._editorGroup.activeControl) {
return undefined;
}
let control = this._editorGroup.activeControl.getControl();
let editor: ICodeEditor;
let editor: ICodeEditor | undefined;
if (isCodeEditor(control)) {
editor = control as ICodeEditor;
} else if (isDiffEditor(control)) {
@@ -282,6 +288,13 @@ export class BreadcrumbsControl {
return;
}
if (event.item === this._breadcrumbsPickerIgnoreOnceItem) {
this._breadcrumbsPickerIgnoreOnceItem = undefined;
this._widget.setFocused(undefined);
this._widget.setSelection(undefined);
return;
}
const { element } = event.item as Item;
this._editorGroup.focus();
@@ -313,7 +326,7 @@ export class BreadcrumbsControl {
let picker: BreadcrumbsPicker;
let editor = this._getActiveCodeEditor();
let editorDecorations: string[] = [];
let editorViewState: ICodeEditorViewState;
let editorViewState: ICodeEditorViewState | undefined;
this._contextViewService.showContextView({
render: (parent: HTMLElement) => {
@@ -336,7 +349,7 @@ export class BreadcrumbsControl {
return;
}
if (!editorViewState) {
editorViewState = editor.saveViewState();
editorViewState = withNullAsUndefined(editor.saveViewState());
}
const { symbol } = data.target;
editor.revealRangeInCenter(symbol.range, ScrollType.Smooth);
@@ -347,12 +360,29 @@ export class BreadcrumbsControl {
isWholeLine: true
}
}]);
});
let zoomListener = onDidChangeZoomLevel(() => {
this._contextViewService.hideContextView(this);
});
let focusTracker = dom.trackFocus(parent);
let blurListener = focusTracker.onDidBlur(() => {
this._breadcrumbsPickerIgnoreOnceItem = this._widget.isDOMFocused() ? event.item : undefined;
this._contextViewService.hideContextView(this);
});
this._breadcrumbsPickerShowing = true;
this._updateCkBreadcrumbsActive();
return combinedDisposable([selectListener, focusListener, picker]);
return combinedDisposable([
picker,
selectListener,
focusListener,
zoomListener,
focusTracker,
blurListener
]);
},
getAnchor: () => {
let maxInnerWidth = window.innerWidth - 8 /*a little less the full widget*/;
@@ -381,7 +411,7 @@ export class BreadcrumbsControl {
} else {
pickerArrowOffset = (data.left + (data.width * 0.3)) - x;
}
picker.setInput(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
picker.show(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
return { x, y };
},
onHide: (data) => {
@@ -406,7 +436,7 @@ export class BreadcrumbsControl {
this._ckBreadcrumbsActive.set(value);
}
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, pinned: boolean = false): void {
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | undefined, pinned: boolean = false): void {
if (element instanceof FileElement) {
if (element.kind === FileKind.FILE) {
// open file in any editor
@@ -421,14 +451,16 @@ export class BreadcrumbsControl {
} else if (element instanceof OutlineElement) {
// open symbol in code editor
let model = OutlineModel.get(element);
this._codeEditorService.openCodeEditor({
resource: model.textModel.uri,
options: {
selection: Range.collapseToStart(element.symbol.selectionRange),
revealInCenterIfOutsideViewport: true
}
}, this._getActiveCodeEditor(), group === SIDE_GROUP);
const model = OutlineModel.get(element);
if (model) {
this._codeEditorService.openCodeEditor({
resource: model.textModel.uri,
options: {
selection: Range.collapseToStart(element.symbol.selectionRange),
revealInCenterIfOutsideViewport: true
}
}, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP);
}
}
}
@@ -449,7 +481,7 @@ export class BreadcrumbsControl {
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: 'breadcrumbs.toggle',
title: { value: localize('cmd.toggle', "Toggle Breadcrumbs"), original: 'Toggle Breadcrumbs' },
title: { value: localize('cmd.toggle', "Toggle Breadcrumbs"), original: 'View: Toggle Breadcrumbs' },
category: localize('cmd.category', "View")
}
});
@@ -540,7 +572,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).focusNext();
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.focusNext();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -556,7 +592,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).focusPrev();
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.focusPrev();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -569,6 +609,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_Pick);
}
});
@@ -582,6 +625,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_Reveal);
}
});
@@ -593,9 +639,15 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).setFocused(undefined);
breadcrumbs.getWidget(groups.activeGroup.id).setSelection(undefined);
groups.activeGroup.activeControl.focus();
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.setFocused(undefined);
widget.setSelection(undefined);
if (groups.activeGroup.activeControl) {
groups.activeGroup.activeControl.focus();
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -606,15 +658,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler(accessor) {
const editors = accessor.get(IEditorService);
const lists = accessor.get(IListService);
const element = <OutlineElement | IFileStat>lists.lastFocusedList.getFocus();
const element = lists.lastFocusedList ? <OutlineElement | IFileStat>lists.lastFocusedList.getFocus() : undefined;
if (element instanceof OutlineElement) {
const outlineElement = OutlineModel.get(element);
if (!outlineElement) {
return undefined;
}
// open symbol in editor
return editors.openEditor({
resource: OutlineModel.get(element).textModel.uri,
resource: outlineElement.textModel.uri,
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
}, SIDE_GROUP);
} else if (URI.isUri(element.resource)) {
} else if (element && URI.isUri(element.resource)) {
// open file in editor
return editors.openEditor({
resource: element.resource,

View File

@@ -21,6 +21,7 @@ import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { FileKind } from 'vs/platform/files/common/files';
import { withNullAsUndefined } from 'vs/base/common/types';
export class FileElement {
constructor(
@@ -105,7 +106,7 @@ export class EditorBreadcrumbsModel {
}
let info: FileInfo = {
folder: workspaceService.getWorkspaceFolder(uri) || undefined,
folder: withNullAsUndefined(workspaceService.getWorkspaceFolder(uri)),
path: []
};
@@ -117,7 +118,7 @@ export class EditorBreadcrumbsModel {
info.path.unshift(new FileElement(uriPrefix, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER));
let prevPathLength = uriPrefix.path.length;
uriPrefix = dirname(uriPrefix);
if (!uriPrefix || uriPrefix.path.length === prevPathLength) {
if (uriPrefix.path.length === prevPathLength) {
break;
}
}

View File

@@ -3,35 +3,38 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
import { compareFileNames } from 'vs/base/common/comparers';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { createMatches, FuzzyScore, fuzzyScore } from 'vs/base/common/filters';
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
import * as glob from 'vs/base/common/glob';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { join } from 'vs/base/common/paths';
import { posix } from 'vs/base/common/path';
import { basename, dirname, isEqual } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
import 'vs/css!./media/breadcrumbscontrol';
import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { OutlineDataSource, OutlineItemComparator, OutlineRenderer, OutlineItemCompareType } from 'vs/editor/contrib/documentSymbols/outlineTree';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files';
import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { HighlightingWorkbenchTree, IHighlighter, IHighlightingTreeConfiguration, IHighlightingTreeOptions } from 'vs/platform/list/browser/listService';
import { WorkbenchDataTree, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { breadcrumbsPickerBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ResourceLabels, IResourceLabel, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter, IDataSource } from 'vs/base/browser/ui/tree/tree';
import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineItem } from 'vs/editor/contrib/documentSymbols/outlineTree';
import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
import { IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree';
export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker {
let ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
const ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement
? BreadcrumbsFilePicker
: BreadcrumbsOutlinePicker;
return instantiationService.createInstance(ctor, parent);
}
@@ -43,16 +46,17 @@ interface ILayoutInfo {
inputHeight: number;
}
type Tree<I, E> = WorkbenchDataTree<I, E, FuzzyScore> | WorkbenchAsyncDataTree<I, E, FuzzyScore>;
export abstract class BreadcrumbsPicker {
protected readonly _disposables = new Array<IDisposable>();
protected readonly _domNode: HTMLDivElement;
protected readonly _arrow: HTMLDivElement;
protected readonly _treeContainer: HTMLDivElement;
protected readonly _tree: HighlightingWorkbenchTree;
protected readonly _focus: dom.IFocusTracker;
protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>;
private _layoutInfo: ILayoutInfo;
protected _arrow: HTMLDivElement;
protected _treeContainer: HTMLDivElement;
protected _tree: Tree<any, any>;
protected _fakeEvent = new UIEvent('fakeEvent');
protected _layoutInfo: ILayoutInfo;
private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>();
readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event;
@@ -64,154 +68,111 @@ export abstract class BreadcrumbsPicker {
parent: HTMLElement,
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
@IWorkbenchThemeService protected readonly _themeService: IWorkbenchThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IConfigurationService protected readonly _configurationService: IConfigurationService,
) {
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons';
parent.appendChild(this._domNode);
this._focus = dom.trackFocus(this._domNode);
this._focus.onDidBlur(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined }), undefined, this._disposables);
this._disposables.push(onDidChangeZoomLevel(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined })));
const theme = this._themeService.getTheme();
const color = theme.getColor(breadcrumbsPickerBackground);
this._arrow = document.createElement('div');
this._arrow.className = 'arrow';
this._arrow.style.borderColor = `transparent transparent ${color.toString()}`;
this._domNode.appendChild(this._arrow);
this._treeContainer = document.createElement('div');
this._treeContainer.style.background = color.toString();
this._treeContainer.style.paddingTop = '2px';
this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`;
this._domNode.appendChild(this._treeContainer);
this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService);
const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
this._disposables.push(filterConfig);
const treeConfig = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined });
this._tree = this._instantiationService.createInstance(
HighlightingWorkbenchTree,
this._treeContainer,
treeConfig,
<IHighlightingTreeOptions>{ useShadows: false, filterOnType: filterConfig.getValue(), showTwistie: false, twistiePixels: 12 },
{ placeholder: localize('placeholder', "Find") }
);
this._disposables.push(this._tree.onDidChangeSelection(e => {
if (e.payload !== this._tree) {
const target = this._getTargetFromEvent(e.selection[0], e.payload);
if (target) {
setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click
this._onDidPickElement.fire({ target, payload: e.payload });
}, 0);
}
}
}));
this._disposables.push(this._tree.onDidChangeFocus(e => {
const target = this._getTargetFromEvent(e.focus, e.payload);
if (target) {
this._onDidFocusElement.fire({ target, payload: e.payload });
}
}));
this._disposables.push(this._tree.onDidStartFiltering(() => {
this._layoutInfo.inputHeight = 36;
this._layout();
}));
this._disposables.push(this._tree.onDidExpandItem(() => {
this._layout();
}));
this._disposables.push(this._tree.onDidCollapseItem(() => {
this._layout();
}));
// tree icon theme specials
dom.addClass(this._treeContainer, 'file-icon-themable-tree');
dom.addClass(this._treeContainer, 'show-file-icons');
const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => {
dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons);
dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true);
};
this._disposables.push(_themeService.onDidFileIconThemeChange(onFileIconThemeChange));
onFileIconThemeChange(_themeService.getFileIconTheme());
this._domNode.focus();
}
dispose(): void {
dispose(this._disposables);
this._onDidPickElement.dispose();
this._tree.dispose();
this._focus.dispose();
this._symbolSortOrder.dispose();
}
setInput(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void {
let actualInput = this._getInput(input);
this._tree.setInput(actualInput).then(() => {
show(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void {
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
this._layout();
const theme = this._themeService.getTheme();
const color = theme.getColor(breadcrumbsPickerBackground);
// use proper selection, reveal
let selection = this._getInitialSelection(this._tree, input);
if (selection) {
return this._tree.reveal(selection, 0.5).then(() => {
this._tree.setSelection([selection], this._tree);
this._tree.setFocus(selection);
this._tree.domFocus();
});
} else {
this._tree.focusFirst();
this._tree.setSelection([this._tree.getFocus()], this._tree);
this._tree.domFocus();
return Promise.resolve(null);
this._arrow = document.createElement('div');
this._arrow.className = 'arrow';
this._arrow.style.borderColor = `transparent transparent ${color ? color.toString() : ''}`;
this._domNode.appendChild(this._arrow);
this._treeContainer = document.createElement('div');
this._treeContainer.style.background = color ? color.toString() : '';
this._treeContainer.style.paddingTop = '2px';
this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`;
this._domNode.appendChild(this._treeContainer);
const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
this._disposables.push(filterConfig);
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
this._tree = this._createTree(this._treeContainer);
this._disposables.push(this._tree.onDidChangeSelection(e => {
if (e.browserEvent !== this._fakeEvent) {
const target = this._getTargetFromEvent(e.elements[0], e.browserEvent);
if (target) {
setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click
this._onDidPickElement.fire({ target, payload: undefined });
}, 0);
}
}
}, onUnexpectedError);
}));
this._disposables.push(this._tree.onDidChangeFocus(e => {
const target = this._getTargetFromEvent(e.elements[0], e.browserEvent);
if (target) {
this._onDidFocusElement.fire({ target, payload: undefined });
}
}));
this._disposables.push(this._tree.onDidChangeContentHeight(() => {
this._layout();
}));
// filter on type: state
const cfgFilterOnType = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
this._tree.updateOptions({ filterOnType: cfgFilterOnType.getValue() });
this._disposables.push(this._tree.onDidUpdateOptions(e => {
this._configurationService.updateValue(cfgFilterOnType.name, e.filterOnType, ConfigurationTarget.MEMORY);
}));
this._domNode.focus();
this._setInput(input).then(() => {
this._layout();
}).catch(onUnexpectedError);
}
private _layout(info: ILayoutInfo = this._layoutInfo): void {
protected _layout(): void {
let count = 0;
let nav = this._tree.getNavigator(undefined, false);
while (nav.next() && count < 13) { count += 1; }
let headerHeight = 2 * info.arrowSize;
let treeHeight = Math.min(info.maxHeight - headerHeight, count * 22);
let totalHeight = treeHeight + headerHeight;
const headerHeight = 2 * this._layoutInfo.arrowSize;
const treeHeight = Math.min(this._layoutInfo.maxHeight - headerHeight, this._tree.contentHeight);
const totalHeight = treeHeight + headerHeight;
this._domNode.style.height = `${totalHeight}px`;
this._domNode.style.width = `${info.width}px`;
this._arrow.style.top = `-${2 * info.arrowSize}px`;
this._arrow.style.borderWidth = `${info.arrowSize}px`;
this._arrow.style.marginLeft = `${info.arrowOffset}px`;
this._domNode.style.width = `${this._layoutInfo.width}px`;
this._arrow.style.top = `-${2 * this._layoutInfo.arrowSize}px`;
this._arrow.style.borderWidth = `${this._layoutInfo.arrowSize}px`;
this._arrow.style.marginLeft = `${this._layoutInfo.arrowOffset}px`;
this._treeContainer.style.height = `${treeHeight}px`;
this._treeContainer.style.width = `${info.width}px`;
this._tree.layout();
this._layoutInfo = info;
this._treeContainer.style.width = `${this._layoutInfo.width}px`;
this._tree.layout(treeHeight, this._layoutInfo.width);
}
protected abstract _getInput(input: BreadcrumbElement): any;
protected abstract _getInitialSelection(tree: ITree, input: BreadcrumbElement): any;
protected abstract _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration;
protected abstract _getTargetFromEvent(element: any, payload: any): any | undefined;
protected abstract _setInput(element: BreadcrumbElement): Promise<void>;
protected abstract _createTree(container: HTMLElement): Tree<any, any>;
protected abstract _getTargetFromEvent(element: any, payload: UIEvent | undefined): any | undefined;
}
//#region - Files
export class FileDataSource implements IDataSource {
class FileVirtualDelegate implements IListVirtualDelegate<IFileStat | IWorkspaceFolder> {
getHeight(_element: IFileStat | IWorkspaceFolder) {
return 22;
}
getTemplateId(_element: IFileStat | IWorkspaceFolder): string {
return 'FileStat';
}
}
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
constructor(
@IFileService private readonly _fileService: IFileService,
) { }
getId(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): string {
class FileIdentityProvider implements IIdentityProvider<IWorkspace | IWorkspaceFolder | IFileStat | URI> {
getId(element: IWorkspace | IWorkspaceFolder | IFileStat | URI): { toString(): string; } {
if (URI.isUri(element)) {
return element.toString();
} else if (IWorkspace.isIWorkspace(element)) {
@@ -222,12 +183,26 @@ export class FileDataSource implements IDataSource {
return element.resource.toString();
}
}
}
hasChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): boolean {
return URI.isUri(element) || IWorkspace.isIWorkspace(element) || IWorkspaceFolder.isIWorkspaceFolder(element) || element.isDirectory;
class FileDataSource implements IAsyncDataSource<IWorkspace | URI, IWorkspaceFolder | IFileStat> {
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
constructor(
@IFileService private readonly _fileService: IFileService,
) { }
hasChildren(element: IWorkspace | URI | IWorkspaceFolder | IFileStat): boolean {
return URI.isUri(element)
|| IWorkspace.isIWorkspace(element)
|| IWorkspaceFolder.isIWorkspaceFolder(element)
|| element.isDirectory;
}
getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): Promise<IWorkspaceFolder[] | IFileStat[]> {
getChildren(element: IWorkspace | URI | IWorkspaceFolder | IFileStat): Promise<(IWorkspaceFolder | IFileStat)[]> {
if (IWorkspace.isIWorkspace(element)) {
return Promise.resolve(element.folders).then(folders => {
for (let child of folders) {
@@ -245,19 +220,62 @@ export class FileDataSource implements IDataSource {
uri = element.resource;
}
return this._fileService.resolveFile(uri).then(stat => {
for (let child of stat.children) {
for (const child of stat.children || []) {
this._parents.set(stat, child);
}
return stat.children;
return stat.children || [];
});
}
getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): Promise<IWorkspaceFolder | IFileStat> {
return Promise.resolve(this._parents.get(element));
}
}
export class FileFilter implements IFilter {
class FileRenderer implements ITreeRenderer<IFileStat | IWorkspaceFolder, FuzzyScore, IResourceLabel> {
readonly templateId: string = 'FileStat';
constructor(
private readonly _labels: ResourceLabels,
@IConfigurationService private readonly _configService: IConfigurationService,
) { }
renderTemplate(container: HTMLElement): IResourceLabel {
return this._labels.create(container, { supportHighlights: true });
}
renderElement(node: ITreeNode<IWorkspaceFolder | IFileStat, [number, number, number]>, index: number, templateData: IResourceLabel): void {
const fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
const { element } = node;
let resource: URI;
let fileKind: FileKind;
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
resource = element.uri;
fileKind = FileKind.ROOT_FOLDER;
} else {
resource = element.resource;
fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE;
}
templateData.setFile(resource, {
fileKind,
hidePath: true,
fileDecorations: fileDecorations,
matches: createMatches(node.filterData),
extraClasses: ['picker-item']
});
}
disposeTemplate(templateData: IResourceLabel): void {
templateData.dispose();
}
}
class FileNavigationLabelProvider implements IKeyboardNavigationLabelProvider<IWorkspaceFolder | IFileStat> {
getKeyboardNavigationLabel(element: IWorkspaceFolder | IFileStat): { toString(): string; } {
return element.name;
}
}
class FileFilter implements ITreeFilter<IWorkspaceFolder | IFileStat> {
private readonly _cachedExpressions = new Map<string, glob.ParsedExpression>();
private readonly _disposables: IDisposable[] = [];
@@ -281,7 +299,7 @@ export class FileFilter implements IFilter {
continue;
}
let patternAbs = pattern.indexOf('**/') !== 0
? join(folder.uri.path, pattern)
? posix.join(folder.uri.path, pattern)
: pattern;
adjustedConfig[patternAbs] = excludesConfig[pattern];
@@ -301,7 +319,7 @@ export class FileFilter implements IFilter {
dispose(this._disposables);
}
isVisible(tree: ITree, element: IWorkspaceFolder | IFileStat): boolean {
filter(element: IWorkspaceFolder | IFileStat, _parentVisibility: TreeVisibility): boolean {
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
// not a file
return true;
@@ -312,77 +330,24 @@ export class FileFilter implements IFilter {
return true;
}
const expression = this._cachedExpressions.get(folder.uri.toString());
const expression = this._cachedExpressions.get(folder.uri.toString())!;
return !expression(element.resource.path, basename(element.resource));
}
}
export class FileHighlighter implements IHighlighter {
getHighlightsStorageKey(element: IFileStat | IWorkspaceFolder): string {
return IWorkspaceFolder.isIWorkspaceFolder(element) ? element.uri.toString() : element.resource.toString();
}
getHighlights(tree: ITree, element: IFileStat | IWorkspaceFolder, pattern: string): FuzzyScore {
return fuzzyScore(pattern, pattern.toLowerCase(), 0, element.name, element.name.toLowerCase(), 0, true);
}
}
export class FileRenderer implements IRenderer {
constructor(
private readonly _labels: ResourceLabels,
@IConfigurationService private readonly _configService: IConfigurationService,
) { }
getHeight(tree: ITree, element: any): number {
return 22;
}
getTemplateId(tree: ITree, element: any): string {
return 'FileStat';
}
renderTemplate(tree: ITree, templateId: string, container: HTMLElement) {
return this._labels.create(container, { supportHighlights: true });
}
renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: IResourceLabel): void {
let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
let resource: URI;
let fileKind: FileKind;
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
resource = element.uri;
fileKind = FileKind.ROOT_FOLDER;
} else {
resource = element.resource;
fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE;
}
templateData.setFile(resource, {
fileKind,
hidePath: true,
fileDecorations: fileDecorations,
matches: createMatches((tree as HighlightingWorkbenchTree).getHighlighterScore(element)),
extraClasses: ['picker-item']
});
}
disposeTemplate(tree: ITree, templateId: string, templateData: IResourceLabel): void {
templateData.dispose();
}
}
export class FileSorter implements ISorter {
compare(tree: ITree, a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
export class FileSorter implements ITreeSorter<IFileStat | IWorkspaceFolder> {
compare(a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
if (IWorkspaceFolder.isIWorkspaceFolder(a) && IWorkspaceFolder.isIWorkspaceFolder(b)) {
return a.index - b.index;
}
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
// same type -> compare on names
return compareFileNames(a.name, b.name);
} else if ((a as IFileStat).isDirectory) {
return -1;
} else {
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
// same type -> compare on names
return compareFileNames(a.name, b.name);
} else if ((a as IFileStat).isDirectory) {
return -1;
} else {
return 1;
}
return 1;
}
}
}
@@ -399,44 +364,69 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
super(parent, instantiationService, themeService, configService);
}
protected _getInput(input: BreadcrumbElement): any {
let { uri, kind } = (input as FileElement);
if (kind === FileKind.ROOT_FOLDER) {
return this._workspaceService.getWorkspace();
} else {
return dirname(uri);
}
}
_createTree(container: HTMLElement) {
protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any {
let { uri } = (input as FileElement);
let nav = tree.getNavigator();
while (nav.next()) {
let cur = nav.current();
let candidate = IWorkspaceFolder.isIWorkspaceFolder(cur) ? cur.uri : (cur as IFileStat).resource;
if (isEqual(uri, candidate)) {
return cur;
}
}
return undefined;
}
// tree icon theme specials
dom.addClass(this._treeContainer, 'file-icon-themable-tree');
dom.addClass(this._treeContainer, 'show-file-icons');
const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => {
dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons);
dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true);
};
this._disposables.push(this._themeService.onDidFileIconThemeChange(onFileIconThemeChange));
onFileIconThemeChange(this._themeService.getFileIconTheme());
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
// todo@joh reuse explorer implementations?
const filter = this._instantiationService.createInstance(FileFilter);
this._disposables.push(filter);
config.dataSource = this._instantiationService.createInstance(FileDataSource);
const labels = this._instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER /* TODO@Jo visibility propagation */);
this._disposables.push(labels);
config.renderer = this._instantiationService.createInstance(FileRenderer, labels);
config.sorter = new FileSorter();
config.highlighter = new FileHighlighter();
config.filter = filter;
return config;
return this._instantiationService.createInstance(
WorkbenchAsyncDataTree,
container,
new FileVirtualDelegate(),
[this._instantiationService.createInstance(FileRenderer, labels)],
this._instantiationService.createInstance(FileDataSource),
{
filterOnType: true,
multipleSelectionSupport: false,
sorter: new FileSorter(),
filter: this._instantiationService.createInstance(FileFilter),
identityProvider: new FileIdentityProvider(),
keyboardNavigationLabelProvider: new FileNavigationLabelProvider()
}
) as WorkbenchAsyncDataTree<BreadcrumbElement, any, FuzzyScore>;
}
_setInput(element: BreadcrumbElement): Promise<void> {
const { uri, kind } = (element as FileElement);
let input: IWorkspace | URI;
if (kind === FileKind.ROOT_FOLDER) {
input = this._workspaceService.getWorkspace();
} else {
input = dirname(uri);
}
const tree = this._tree as WorkbenchAsyncDataTree<IWorkspace | URI, IWorkspaceFolder | IFileStat, FuzzyScore>;
return tree.setInput(input).then(() => {
let focusElement: IWorkspaceFolder | IFileStat | undefined;
for (const { element } of tree.getNode().children) {
if (IWorkspaceFolder.isIWorkspaceFolder(element) && isEqual(element.uri, uri)) {
focusElement = element;
break;
} else if (isEqual((element as IFileStat).resource, uri)) {
focusElement = element as IFileStat;
break;
}
}
if (focusElement) {
tree.reveal(focusElement, 0.5);
tree.setFocus([focusElement], this._fakeEvent);
}
tree.domFocus();
});
}
protected _getTargetFromEvent(element: any, _payload: any): any | undefined {
// todo@joh
if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) {
return new FileElement((element as IFileStat).resource, FileKind.FILE);
}
@@ -446,52 +436,84 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
//#region - Symbols
class OutlineHighlighter implements IHighlighter {
getHighlights(tree: ITree, element: OutlineElement, pattern: string): FuzzyScore {
OutlineModel.get(element).updateMatches(pattern);
return element.score;
}
}
export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
protected _getInput(input: BreadcrumbElement): any {
let element = input as TreeElement;
let model = OutlineModel.get(element);
model.updateMatches('');
return model;
protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>;
constructor(
parent: HTMLElement,
@IInstantiationService instantiationService: IInstantiationService,
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(parent, instantiationService, themeService, configurationService);
this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService);
}
protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any {
return input instanceof OutlineModel ? undefined : input;
protected _createTree(container: HTMLElement) {
return this._instantiationService.createInstance<
HTMLElement,
IListVirtualDelegate<OutlineItem>,
ITreeRenderer<any, FuzzyScore, any>[],
IDataSource<OutlineModel, OutlineItem>,
IDataTreeOptions<OutlineItem, FuzzyScore>,
WorkbenchDataTree<OutlineModel, OutlineItem, FuzzyScore>
>(
WorkbenchDataTree,
container,
new OutlineVirtualDelegate(),
[new OutlineGroupRenderer(), this._instantiationService.createInstance(OutlineElementRenderer)],
new OutlineDataSource(),
{
filterOnType: true,
expandOnlyOnTwistieClick: true,
multipleSelectionSupport: false,
sorter: new OutlineItemComparator(this._getOutlineItemCompareType()),
identityProvider: new OutlineIdentityProvider(),
keyboardNavigationLabelProvider: this._instantiationService.createInstance(OutlineNavigationLabelProvider)
}
) as WorkbenchDataTree<OutlineModel, OutlineItem, FuzzyScore>;
}
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
config.dataSource = this._instantiationService.createInstance(OutlineDataSource);
config.renderer = this._instantiationService.createInstance(OutlineRenderer);
config.sorter = new OutlineItemComparator(this._getOutlineItemComparator());
config.highlighter = new OutlineHighlighter();
return config;
dispose(): void {
this._symbolSortOrder.dispose();
super.dispose();
}
protected _getTargetFromEvent(element: any, payload: any): any | undefined {
if (payload && payload.didClickOnTwistie) {
return;
protected _setInput(input: BreadcrumbElement): Promise<void> {
const element = input as TreeElement;
const model = OutlineModel.get(element)!;
const tree = this._tree as WorkbenchDataTree<OutlineModel, any, FuzzyScore>;
tree.setInput(model);
let focusElement: TreeElement;
if (element === model) {
focusElement = tree.navigate().first();
} else {
focusElement = element;
}
tree.reveal(focusElement, 0.5);
tree.setFocus([focusElement], this._fakeEvent);
tree.domFocus();
return Promise.resolve();
}
protected _getTargetFromEvent(element: any): any | undefined {
if (element instanceof OutlineElement) {
return element;
}
}
private _getOutlineItemComparator(): OutlineItemCompareType {
private _getOutlineItemCompareType(): OutlineSortOrder {
switch (this._symbolSortOrder.getValue()) {
case 'name':
return OutlineItemCompareType.ByName;
return OutlineSortOrder.ByName;
case 'type':
return OutlineItemCompareType.ByKind;
return OutlineSortOrder.ByKind;
case 'position':
default:
return OutlineItemCompareType.ByPosition;
return OutlineSortOrder.ByPosition;
}
}
}

View File

@@ -103,7 +103,7 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
interface ISerializedUntitledEditorInput {
resource: string;
resourceJSON: object;
modeId: string;
modeId: string | null;
encoding: string;
}
@@ -114,7 +114,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
@ITextFileService private readonly textFileService: ITextFileService
) { }
serialize(editorInput: EditorInput): string {
serialize(editorInput: EditorInput): string | null {
if (!this.textFileService.isHotExitEnabled) {
return null; // never restore untitled unless hot exit is enabled
}
@@ -170,7 +170,7 @@ interface ISerializedSideBySideEditorInput {
// Register Side by Side Editor Input Factory
class SideBySideEditorInputFactory implements IEditorInputFactory {
serialize(editorInput: EditorInput): string {
serialize(editorInput: EditorInput): string | null {
const input = <SideBySideEditorInput>editorInput;
if (input.details && input.master) {
@@ -198,7 +198,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
return null;
}
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | null {
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
@@ -266,7 +266,7 @@ export class QuickOpenActionContributor extends ActionBarContributor {
return actions;
}
private getEntry(context: any): IEditorQuickOpenEntry {
private getEntry(context: any): IEditorQuickOpenEntry | null {
if (!context || !context.element) {
return null;
}

View File

@@ -3,9 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { GroupIdentifier, IWorkbenchEditorConfiguration, IWorkbenchEditorPartConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditor } from 'vs/workbench/common/editor';
import { GroupIdentifier, IWorkbenchEditorConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditor, IEditorPartOptions } from 'vs/workbench/common/editor';
import { EditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Dimension } from 'vs/base/browser/dom';
import { Event } from 'vs/base/common/event';
@@ -18,13 +18,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
export const EDITOR_TITLE_HEIGHT = 35;
export interface IEditorPartCreationOptions {
restorePreviousState: boolean;
}
export const DEFAULT_EDITOR_MIN_DIMENSIONS = new Dimension(220, 70);
export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
export interface IEditorPartOptions extends IWorkbenchEditorPartConfiguration {
iconTheme?: string;
}
export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
showTabs: true,
highlightModifiedTabs: false,
@@ -77,7 +77,7 @@ export interface IEditorOpeningEvent extends IEditorIdentifier {
* to return a promise that resolves to NULL to prevent the opening
* alltogether.
*/
prevent(callback: () => Promise<IEditor>): void;
prevent(callback: () => undefined | Promise<IEditor | undefined>): void;
}
export interface IEditorGroupsAccessor {
@@ -87,7 +87,7 @@ export interface IEditorGroupsAccessor {
readonly partOptions: IEditorPartOptions;
readonly onDidEditorPartOptionsChange: Event<IEditorPartOptionsChangeEvent>;
getGroup(identifier: GroupIdentifier): IEditorGroupView;
getGroup(identifier: GroupIdentifier): IEditorGroupView | undefined;
getGroups(order: GroupsOrder): IEditorGroupView[];
activateGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView;
@@ -146,15 +146,3 @@ export interface EditorServiceImpl extends IEditorService {
*/
readonly onDidOpenEditorFail: Event<IEditorIdentifier>;
}
/**
* A sub-interface of IEditorGroupsService to hide some workbench-core specific
* methods from clients.
*/
export interface EditorGroupsServiceImpl extends IEditorGroupsService {
/**
* A promise that resolves when groups have been restored.
*/
readonly whenRestored: Promise<void>;
}

View File

@@ -10,7 +10,7 @@ import { IEditorInput, EditorInput, IEditorIdentifier, ConfirmResult, IEditorCom
import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -18,7 +18,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { CLOSE_EDITOR_COMMAND_ID, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, MOVE_ACTIVE_EDITOR_COMMAND_ID, NAVIGATE_IN_ACTIVE_GROUP_PREFIX, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, EditorsOrder, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, EditorsOrder, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
@@ -188,20 +188,22 @@ export class JoinTwoGroupsAction extends Action {
}
run(context?: IEditorIdentifier): Promise<any> {
let sourceGroup: IEditorGroup;
let sourceGroup: IEditorGroup | undefined;
if (context && typeof context.groupId === 'number') {
sourceGroup = this.editorGroupService.getGroup(context.groupId);
} else {
sourceGroup = this.editorGroupService.activeGroup;
}
const targetGroupDirections = [GroupDirection.RIGHT, GroupDirection.DOWN, GroupDirection.LEFT, GroupDirection.UP];
for (const targetGroupDirection of targetGroupDirections) {
const targetGroup = this.editorGroupService.findGroup({ direction: targetGroupDirection }, sourceGroup);
if (targetGroup && sourceGroup !== targetGroup) {
this.editorGroupService.mergeGroup(sourceGroup, targetGroup);
if (sourceGroup) {
const targetGroupDirections = [GroupDirection.RIGHT, GroupDirection.DOWN, GroupDirection.LEFT, GroupDirection.UP];
for (const targetGroupDirection of targetGroupDirections) {
const targetGroup = this.editorGroupService.findGroup({ direction: targetGroupDirection }, sourceGroup);
if (targetGroup && sourceGroup !== targetGroup) {
this.editorGroupService.mergeGroup(sourceGroup, targetGroup);
return Promise.resolve(true);
return Promise.resolve(true);
}
}
}
@@ -428,7 +430,7 @@ export class OpenToSideFromQuickOpenAction extends Action {
if (entry) {
const input = entry.getInput();
if (input instanceof EditorInput) {
return this.editorService.openEditor(input, entry.getOptions(), SIDE_GROUP);
return this.editorService.openEditor(input, entry.getOptions() || undefined, SIDE_GROUP);
}
const resourceInput = input as IResourceInput;
@@ -441,7 +443,7 @@ export class OpenToSideFromQuickOpenAction extends Action {
}
}
export function toEditorQuickOpenEntry(element: any): IEditorQuickOpenEntry {
export function toEditorQuickOpenEntry(element: any): IEditorQuickOpenEntry | null {
// QuickOpenEntryGroup
if (element instanceof QuickOpenEntryGroup) {
@@ -491,13 +493,13 @@ export class CloseOneEditorAction extends Action {
}
run(context?: IEditorCommandsContext): Promise<any> {
let group: IEditorGroup;
let editorIndex: number;
let group: IEditorGroup | undefined;
let editorIndex: number | undefined;
if (context) {
group = this.editorGroupService.getGroup(context.groupId);
if (group) {
editorIndex = context.editorIndex; // only allow editor at index if group is valid
editorIndex = context.editorIndex!; // only allow editor at index if group is valid
}
}
@@ -579,7 +581,7 @@ export class CloseLeftEditorsInGroupAction extends Action {
}
}
function getTarget(editorService: IEditorService, editorGroupService: IEditorGroupsService, context?: IEditorIdentifier): { editor: IEditorInput, group: IEditorGroup } {
function getTarget(editorService: IEditorService, editorGroupService: IEditorGroupsService, context?: IEditorIdentifier): { editor: IEditorInput | null, group: IEditorGroup | undefined } {
if (context) {
return { editor: context.editor, group: editorGroupService.getGroup(context.groupId) };
}
@@ -593,7 +595,7 @@ export abstract class BaseCloseAllAction extends Action {
constructor(
id: string,
label: string,
clazz: string,
clazz: string | undefined,
private textFileService: ITextFileService,
protected editorGroupService: IEditorGroupsService
) {
@@ -629,9 +631,9 @@ export abstract class BaseCloseAllAction extends Action {
let saveOrRevertPromise: Promise<boolean>;
if (confirm === ConfirmResult.DONT_SAVE) {
saveOrRevertPromise = this.textFileService.revertAll(null, { soft: true }).then(() => true);
saveOrRevertPromise = this.textFileService.revertAll(undefined, { soft: true }).then(() => true);
} else {
saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => r.success));
saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => !!r.success));
}
return saveOrRevertPromise.then(success => {
@@ -703,8 +705,8 @@ export class CloseEditorsInOtherGroupsAction extends Action {
run(context?: IEditorIdentifier): Promise<any> {
const groupToSkip = context ? this.editorGroupService.getGroup(context.groupId) : this.editorGroupService.activeGroup;
return Promise.all(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => {
if (g.id === groupToSkip.id) {
return Promise.resolve(null);
if (groupToSkip && g.id === groupToSkip.id) {
return Promise.resolve();
}
return g.closeAllEditors();
@@ -732,7 +734,7 @@ export class CloseEditorInAllGroupsAction extends Action {
return Promise.all(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => g.closeEditor(activeEditor)));
}
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -748,22 +750,24 @@ export class BaseMoveGroupAction extends Action {
}
run(context?: IEditorIdentifier): Promise<any> {
let sourceGroup: IEditorGroup;
let sourceGroup: IEditorGroup | undefined;
if (context && typeof context.groupId === 'number') {
sourceGroup = this.editorGroupService.getGroup(context.groupId);
} else {
sourceGroup = this.editorGroupService.activeGroup;
}
const targetGroup = this.findTargetGroup(sourceGroup);
if (targetGroup) {
this.editorGroupService.moveGroup(sourceGroup, targetGroup, this.direction);
if (sourceGroup) {
const targetGroup = this.findTargetGroup(sourceGroup);
if (targetGroup) {
this.editorGroupService.moveGroup(sourceGroup, targetGroup, this.direction);
}
}
return Promise.resolve(true);
}
private findTargetGroup(sourceGroup: IEditorGroup): IEditorGroup {
private findTargetGroup(sourceGroup: IEditorGroup): IEditorGroup | undefined {
const targetNeighbours: GroupDirection[] = [this.direction];
// Allow the target group to be in alternative locations to support more
@@ -882,14 +886,14 @@ export class ResetGroupSizesAction extends Action {
export class MaximizeGroupAction extends Action {
static readonly ID = 'workbench.action.maximizeEditor';
static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Sidebar");
static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Side Bar");
constructor(
id: string,
label: string,
@IEditorService private readonly editorService: IEditorService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IPartService private readonly partService: IPartService
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super(id, label);
}
@@ -897,7 +901,7 @@ export class MaximizeGroupAction extends Action {
run(): Promise<any> {
if (this.editorService.activeEditor) {
this.editorGroupService.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS);
this.partService.setSideBarHidden(true);
this.layoutService.setSideBarHidden(true);
}
return Promise.resolve(false);
@@ -927,10 +931,14 @@ export abstract class BaseNavigateEditorAction extends Action {
}
const group = this.editorGroupService.getGroup(groupId);
return group.openEditor(editor);
if (group) {
return group.openEditor(editor);
}
return Promise.resolve();
}
protected abstract navigate(): IEditorIdentifier;
protected abstract navigate(): IEditorIdentifier | undefined;
}
export class OpenNextEditor extends BaseNavigateEditorAction {
@@ -947,12 +955,12 @@ export class OpenNextEditor extends BaseNavigateEditorAction {
super(id, label, editorGroupService, editorService);
}
protected navigate(): IEditorIdentifier {
protected navigate(): IEditorIdentifier | undefined {
// Navigate in active group if possible
const activeGroup = this.editorGroupService.activeGroup;
const activeGroupEditors = activeGroup.getEditors(EditorsOrder.SEQUENTIAL);
const activeEditorIndex = activeGroupEditors.indexOf(activeGroup.activeEditor);
const activeEditorIndex = activeGroup.activeEditor ? activeGroupEditors.indexOf(activeGroup.activeEditor) : -1;
if (activeEditorIndex + 1 < activeGroupEditors.length) {
return { editor: activeGroupEditors[activeEditorIndex + 1], groupId: activeGroup.id };
}
@@ -982,12 +990,12 @@ export class OpenPreviousEditor extends BaseNavigateEditorAction {
super(id, label, editorGroupService, editorService);
}
protected navigate(): IEditorIdentifier {
protected navigate(): IEditorIdentifier | undefined {
// Navigate in active group if possible
const activeGroup = this.editorGroupService.activeGroup;
const activeGroupEditors = activeGroup.getEditors(EditorsOrder.SEQUENTIAL);
const activeEditorIndex = activeGroupEditors.indexOf(activeGroup.activeEditor);
const activeEditorIndex = activeGroup.activeEditor ? activeGroupEditors.indexOf(activeGroup.activeEditor) : -1;
if (activeEditorIndex > 0) {
return { editor: activeGroupEditors[activeEditorIndex - 1], groupId: activeGroup.id };
}
@@ -1020,7 +1028,7 @@ export class OpenNextEditorInGroup extends BaseNavigateEditorAction {
protected navigate(): IEditorIdentifier {
const group = this.editorGroupService.activeGroup;
const editors = group.getEditors(EditorsOrder.SEQUENTIAL);
const index = editors.indexOf(group.activeEditor);
const index = group.activeEditor ? editors.indexOf(group.activeEditor) : -1;
return { editor: index + 1 < editors.length ? editors[index + 1] : editors[0], groupId: group.id };
}
@@ -1043,7 +1051,7 @@ export class OpenPreviousEditorInGroup extends BaseNavigateEditorAction {
protected navigate(): IEditorIdentifier {
const group = this.editorGroupService.activeGroup;
const editors = group.getEditors(EditorsOrder.SEQUENTIAL);
const index = editors.indexOf(group.activeEditor);
const index = group.activeEditor ? editors.indexOf(group.activeEditor) : -1;
return { editor: index > 0 ? editors[index - 1] : editors[editors.length - 1], groupId: group.id };
}
@@ -1105,7 +1113,7 @@ export class NavigateForwardAction extends Action {
run(): Promise<any> {
this.historyService.forward();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1121,7 +1129,7 @@ export class NavigateBackwardsAction extends Action {
run(): Promise<any> {
this.historyService.back();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1137,7 +1145,7 @@ export class NavigateToLastEditLocationAction extends Action {
run(): Promise<any> {
this.historyService.openLastEditLocation();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1153,7 +1161,7 @@ export class NavigateLastAction extends Action {
run(): Promise<any> {
this.historyService.last();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1296,7 +1304,7 @@ export class OpenPreviousEditorFromHistoryAction extends Action {
run(): Promise<any> {
const keys = this.keybindingService.lookupKeybindings(this.id);
this.quickOpenService.show(null, { quickNavigateConfiguration: { keybindings: keys } });
this.quickOpenService.show(undefined, { quickNavigateConfiguration: { keybindings: keys } });
return Promise.resolve(true);
}
@@ -1314,7 +1322,7 @@ export class OpenNextRecentlyUsedEditorAction extends Action {
run(): Promise<any> {
this.historyService.forward(true);
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1330,7 +1338,7 @@ export class OpenPreviousRecentlyUsedEditorAction extends Action {
run(): Promise<any> {
this.historyService.back(true);
return Promise.resolve(null);
return Promise.resolve();
}
}

View File

@@ -8,7 +8,7 @@ import * as types from 'vs/base/common/types';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditor, IEditorInput } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService, IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
@@ -16,8 +16,8 @@ import { URI } from 'vs/base/common/uri';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IListService } from 'vs/platform/list/browser/listService';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { distinct } from 'vs/base/common/arrays';
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
import { distinct, coalesce } from 'vs/base/common/arrays';
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
@@ -52,9 +52,9 @@ export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active ';
export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex';
export interface ActiveEditorMoveArguments {
to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
by?: 'tab' | 'group';
value?: number;
to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
by: 'tab' | 'group';
value: number;
}
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
@@ -90,7 +90,24 @@ function registerActiveEditorMoveCommand(): void {
{
name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."),
constraint: isActiveEditorMoveArg
constraint: isActiveEditorMoveArg,
schema: {
'type': 'object',
'required': ['to'],
'properties': {
'to': {
'type': 'string',
'enum': ['left', 'right']
},
'by': {
'type': 'string',
'enum': ['tab', 'group']
},
'value': {
'type': 'number'
}
},
}
}
]
}
@@ -113,7 +130,7 @@ function moveActiveEditor(args: ActiveEditorMoveArguments = Object.create(null),
}
}
function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
function moveActiveTab(args: ActiveEditorMoveArguments, control: IVisibleEditor, accessor: ServicesAccessor): void {
const group = control.group;
let index = group.getIndexOfEditor(control.input);
switch (args.to) {
@@ -141,12 +158,12 @@ function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, access
group.moveEditor(control.input, group, { index });
}
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IVisibleEditor, accessor: ServicesAccessor): void {
const editorGroupService = accessor.get(IEditorGroupsService);
const configurationService = accessor.get(IConfigurationService);
const sourceGroup = control.group;
let targetGroup: IEditorGroup;
let targetGroup: IEditorGroup | undefined;
switch (args.to) {
case 'left':
@@ -340,7 +357,7 @@ function registerOpenEditorAtIndexCommands(): void {
case 9: return KeyCode.KEY_9;
}
return undefined;
throw new Error('invalid index');
}
}
@@ -392,7 +409,7 @@ function registerFocusEditorGroupAtIndexCommands(): void {
case 7: return 'workbench.action.focusEighthEditorGroup';
}
return undefined;
throw new Error('Invalid index');
}
function toKeyCode(index: number): KeyCode {
@@ -406,23 +423,27 @@ function registerFocusEditorGroupAtIndexCommands(): void {
case 7: return KeyCode.KEY_8;
}
return undefined;
throw new Error('Invalid index');
}
}
export function splitEditor(editorGroupService: IEditorGroupsService, direction: GroupDirection, context?: IEditorCommandsContext): void {
let sourceGroup: IEditorGroup;
let sourceGroup: IEditorGroup | undefined;
if (context && typeof context.groupId === 'number') {
sourceGroup = editorGroupService.getGroup(context.groupId);
} else {
sourceGroup = editorGroupService.activeGroup;
}
if (!sourceGroup) {
return;
}
// Add group
const newGroup = editorGroupService.addGroup(sourceGroup, direction);
// Split editor (if it can be split)
let editorToCopy: IEditorInput;
let editorToCopy: IEditorInput | null;
if (context && typeof context.editorIndex === 'number') {
editorToCopy = sourceGroup.getEditor(context.editorIndex);
} else {
@@ -466,9 +487,14 @@ function registerCloseEditorCommands() {
contexts.push({ groupId: activeGroup.id }); // active group as fallback
}
return Promise.all(distinct(contexts.map(c => c.groupId)).map(groupId =>
editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
));
return Promise.all(distinct(contexts.map(c => c.groupId)).map(groupId => {
const group = editorGroupService.getGroup(groupId);
if (group) {
return group.closeEditors({ savedOnly: true });
}
return Promise.resolve();
}));
}
});
@@ -486,9 +512,14 @@ function registerCloseEditorCommands() {
distinctGroupIds.push(editorGroupService.activeGroup.id);
}
return Promise.all(distinctGroupIds.map(groupId =>
editorGroupService.getGroup(groupId).closeAllEditors()
));
return Promise.all(distinctGroupIds.map(groupId => {
const group = editorGroupService.getGroup(groupId);
if (group) {
return group.closeAllEditors();
}
return Promise.resolve();
}));
}
});
@@ -511,11 +542,15 @@ function registerCloseEditorCommands() {
return Promise.all(groupIds.map(groupId => {
const group = editorGroupService.getGroup(groupId);
const editors = contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
if (group) {
const editors = coalesce(contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor));
return group.closeEditors(editors);
return group.closeEditors(editors);
}
return Promise.resolve();
}));
}
});
@@ -530,14 +565,16 @@ function registerCloseEditorCommands() {
const editorGroupService = accessor.get(IEditorGroupsService);
const commandsContext = getCommandsContext(resourceOrContext, context);
let group: IEditorGroup;
let group: IEditorGroup | undefined;
if (commandsContext && typeof commandsContext.groupId === 'number') {
group = editorGroupService.getGroup(commandsContext.groupId);
} else {
group = editorGroupService.activeGroup;
}
editorGroupService.removeGroup(group);
if (group) {
editorGroupService.removeGroup(group);
}
}
});
@@ -560,12 +597,16 @@ function registerCloseEditorCommands() {
return Promise.all(groupIds.map(groupId => {
const group = editorGroupService.getGroup(groupId);
const editors = contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
if (group) {
const editors = contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
return group.closeEditors(editorsToClose);
return group.closeEditors(editorsToClose);
}
return Promise.resolve();
}));
}
});
@@ -619,7 +660,10 @@ function registerCloseEditorCommands() {
const commandsContext = getCommandsContext(resourceOrContext, context);
if (commandsContext && typeof commandsContext.groupId === 'number') {
editorGroupService.activateGroup(editorGroupService.getGroup(commandsContext.groupId)); // we need the group to be active
const group = editorGroupService.getGroup(commandsContext.groupId);
if (group) {
editorGroupService.activateGroup(group); // we need the group to be active
}
}
return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
@@ -642,7 +686,7 @@ function registerCloseEditorCommands() {
});
}
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext {
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext | undefined {
if (URI.isUri(resourceOrContext)) {
return context;
}
@@ -658,11 +702,11 @@ function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, con
return undefined;
}
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor?: IEditorInput, control?: IEditor } {
// Resolve from context
let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
let editor = group && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : undefined;
let editor = group && context && typeof context.editorIndex === 'number' ? types.withNullAsUndefined(group.getEditor(context.editorIndex)) : undefined;
let control = group ? group.activeControl : undefined;
// Fallback to active group as needed
@@ -675,7 +719,7 @@ function resolveCommandsContext(editorGroupService: IEditorGroupsService, contex
return { group, editor, control };
}
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext | undefined, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
// First check for a focused list to return the selected items from
const list = listService.lastFocusedList;
@@ -685,7 +729,9 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
return { groupId: element.id, editorIndex: undefined };
}
return { groupId: element.groupId, editorIndex: editorGroupService.getGroup(element.groupId).getIndexOfEditor(element.editor) };
const group = editorGroupService.getGroup(element.groupId);
return { groupId: element.groupId, editorIndex: group ? group.getIndexOfEditor(element.editor) : -1 };
};
const onlyEditorGroupAndEditor = (e: IEditorIdentifier | IEditorGroup) => isEditorGroup(e) || isEditorIdentifier(e);
@@ -697,7 +743,14 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
const selection: Array<IEditorIdentifier | IEditorGroup> = list.getSelectedElements().filter(onlyEditorGroupAndEditor);
// Only respect selection if it contains focused element
if (selection && selection.some(s => isEditorGroup(s) ? s.id === focus.groupId : s.groupId === focus.groupId && editorGroupService.getGroup(s.groupId).getIndexOfEditor(s.editor) === focus.editorIndex)) {
if (selection && selection.some(s => {
if (isEditorGroup(s)) {
return s.id === focus.groupId;
}
const group = editorGroupService.getGroup(s.groupId);
return s.groupId === focus.groupId && (group ? group.getIndexOfEditor(s.editor) : -1) === focus.editorIndex;
})) {
return selection.map(elementToContext);
}

View File

@@ -8,12 +8,14 @@ import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { Dimension, show, hide, addClass } from 'vs/base/browser/dom';
import { Registry } from 'vs/platform/registry/common/platform';
import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
import { Event, Emitter } from 'vs/base/common/event';
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
import { withUndefinedAsNull } from 'vs/base/common/types';
export interface IOpenEditorResult {
readonly control: BaseEditor;
@@ -27,7 +29,7 @@ export class EditorControl extends Disposable {
get maximumWidth() { return this._activeControl ? this._activeControl.maximumWidth : DEFAULT_EDITOR_MAX_DIMENSIONS.width; }
get maximumHeight() { return this._activeControl ? this._activeControl.maximumHeight : DEFAULT_EDITOR_MAX_DIMENSIONS.height; }
private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
get onDidFocus(): Event<void> { return this._onDidFocus.event; }
private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; } | undefined>());
@@ -43,7 +45,7 @@ export class EditorControl extends Disposable {
constructor(
private parent: HTMLElement,
private groupView: IEditorGroupView,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IProgressService progressService: IProgressService
) {
@@ -52,8 +54,8 @@ export class EditorControl extends Disposable {
this.editorOperation = this._register(new LongRunningOperation(progressService));
}
get activeControl() {
return this._activeControl;
get activeControl(): IVisibleEditor | null {
return this._activeControl as IVisibleEditor | null;
}
openEditor(editor: EditorInput, options?: EditorOptions): Promise<IOpenEditorResult> {
@@ -66,7 +68,7 @@ export class EditorControl extends Disposable {
const control = this.doShowEditorControl(descriptor);
// Set input
return this.doSetInput(control, editor, options || null).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
return this.doSetInput(control, editor, withUndefinedAsNull(options)).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
}
private doShowEditorControl(descriptor: IEditorDescriptor): BaseEditor {
@@ -170,7 +172,7 @@ export class EditorControl extends Disposable {
// Show progress while setting input after a certain timeout. If the workbench is opening
// be more relaxed about progress showing by increasing the delay a little bit to reduce flicker.
const operation = this.editorOperation.start(this.partService.isRestored() ? 800 : 3200);
const operation = this.editorOperation.start(this.layoutService.isRestored() ? 800 : 3200);
// Call into editor control
const editorWillChange = !inputMatches;

View File

@@ -12,7 +12,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { isMacintosh } from 'vs/base/common/platform';
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/group/common/editorGroupsService';
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService';
import { toDisposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { RunOnceScheduler } from 'vs/base/common/async';
@@ -28,7 +28,7 @@ class DropOverlay extends Themable {
private container: HTMLElement;
private overlay: HTMLElement;
private currentDropOperation: IDropOperation;
private currentDropOperation?: IDropOperation;
private _disposed: boolean;
private cleanupOverlayScheduler: RunOnceScheduler;
@@ -103,12 +103,12 @@ class DropOverlay extends Themable {
// Update the dropEffect to "copy" if there is no local data to be dragged because
// in that case we can only copy the data into and not move it from its source
if (!isDraggingEditor && !isDraggingGroup) {
if (!isDraggingEditor && !isDraggingGroup && e.dataTransfer) {
e.dataTransfer.dropEffect = 'copy';
}
// Find out if operation is valid
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier) : true;
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier) : true;
if (!isCopy) {
const sourceGroupView = this.findSourceGroupView();
if (sourceGroupView === this.groupView) {
@@ -158,16 +158,16 @@ class DropOverlay extends Themable {
}));
}
private findSourceGroupView(): IEditorGroupView {
private findSourceGroupView(): IEditorGroupView | undefined {
// Check for group transfer
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier);
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier);
}
// Check for editor transfer
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier.groupId);
return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier.groupId);
}
return undefined;
@@ -189,59 +189,66 @@ class DropOverlay extends Themable {
// Check for group transfer
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier;
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier;
// Return if the drop is a no-op
const sourceGroup = this.accessor.getGroup(draggedEditorGroup);
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
return;
}
if (sourceGroup) {
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
return;
}
// Split to new group
let targetGroup: IEditorGroupView;
if (typeof splitDirection === 'number') {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection);
} else {
targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection);
// Split to new group
let targetGroup: IEditorGroupView | undefined;
if (typeof splitDirection === 'number') {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection);
} else {
targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection);
}
}
// Merge into existing group
else {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS });
} else {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView);
}
}
if (targetGroup) {
this.accessor.activateGroup(targetGroup);
}
}
// Merge into existing group
else {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS });
} else {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView);
}
}
this.accessor.activateGroup(targetGroup);
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
}
// Check for editor transfer
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
const targetGroup = ensureTargetGroup();
// Return if the drop is a no-op
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
if (sourceGroup === targetGroup) {
return;
}
if (sourceGroup) {
if (sourceGroup === targetGroup) {
return;
}
// Open in target group
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
targetGroup.openEditor(draggedEditor.editor, options);
// Open in target group
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
targetGroup.openEditor(draggedEditor.editor, options);
// Ensure target has focus
targetGroup.focus();
// Ensure target has focus
targetGroup.focus();
// Close in source group unless we copy
const copyEditor = this.isCopyOperation(event, draggedEditor);
if (!copyEditor) {
sourceGroup.closeEditor(draggedEditor.editor);
// Close in source group unless we copy
const copyEditor = this.isCopyOperation(event, draggedEditor);
if (!copyEditor) {
sourceGroup.closeEditor(draggedEditor.editor);
}
}
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
@@ -250,7 +257,7 @@ class DropOverlay extends Themable {
// Check for URI transfer
else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup.focus());
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup!.focus());
}
}
@@ -298,7 +305,7 @@ class DropOverlay extends Themable {
// child.style.top = edgeHeightThreshold + 'px';
// No split if mouse is above certain threshold in the center of the view
let splitDirection: GroupDirection;
let splitDirection: GroupDirection | undefined;
if (
mousePosX > edgeWidthThreshold && mousePosX < editorControlWidth - edgeWidthThreshold &&
mousePosY > edgeHeightThreshold && mousePosY < editorControlHeight - edgeHeightThreshold
@@ -429,7 +436,7 @@ class DropOverlay extends Themable {
export class EditorDropTarget extends Themable {
private _overlay: DropOverlay;
private _overlay?: DropOverlay;
private counter = 0;
@@ -447,7 +454,7 @@ export class EditorDropTarget extends Themable {
this.registerListeners();
}
private get overlay(): DropOverlay {
private get overlay(): DropOverlay | undefined {
if (this._overlay && !this._overlay.disposed) {
return this._overlay;
}
@@ -468,7 +475,7 @@ export class EditorDropTarget extends Themable {
if (
!this.editorTransfer.hasData(DraggedEditorIdentifier.prototype) &&
!this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype) &&
!event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789
event.dataTransfer && !event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789
) {
event.dataTransfer.dropEffect = 'none';
return; // unsupported transfer
@@ -510,7 +517,7 @@ export class EditorDropTarget extends Themable {
this.disposeOverlay();
}
private findTargetGroupView(child: HTMLElement): IEditorGroupView {
private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined {
const groups = this.accessor.groups;
for (const groupView of groups) {
if (isAncestor(child, groupView.element)) {

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/editorgroupview';
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { EditorInput, EditorOptions, GroupIdentifier, ConfirmResult, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor } from 'vs/workbench/common/editor';
import { Event, Emitter, Relay } from 'vs/base/common/event';
@@ -16,12 +17,11 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_FOCUSED_EMPTY_BORDER } from 'vs/workbench/common/theme';
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder, ICloseEditorOptions } from 'vs/workbench/services/editor/common/editorGroupsService';
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { ProgressService } from 'vs/workbench/services/progress/browser/progressService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { localize } from 'vs/nls';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
@@ -33,7 +33,6 @@ import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { join } from 'vs/base/common/paths';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ActionRunner, IAction, Action } from 'vs/base/common/actions';
@@ -45,10 +44,14 @@ import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemAc
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
// {{SQL CARBON EDIT}}
import { ICommandService } from 'vs/platform/commands/common/commands';
import { GlobalNewUntitledFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions';
import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/fileActions';
// {{SQL CARBON EDIT}} - End
import { isErrorWithActions, IErrorWithActions } from 'vs/base/common/errorsWithActions';
import { URI } from 'vs/base/common/uri';
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
import { guessMimeTypes } from 'vs/base/common/mime';
import { extname } from 'vs/base/common/path';
export class EditorGroupView extends Themable implements IEditorGroupView {
@@ -70,25 +73,25 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
//#region events
private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
get onDidFocus(): Event<void> { return this._onDidFocus.event; }
private _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
private readonly _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
get onWillDispose(): Event<void> { return this._onWillDispose.event; }
private _onDidGroupChange: Emitter<IGroupChangeEvent> = this._register(new Emitter<IGroupChangeEvent>());
private readonly _onDidGroupChange: Emitter<IGroupChangeEvent> = this._register(new Emitter<IGroupChangeEvent>());
get onDidGroupChange(): Event<IGroupChangeEvent> { return this._onDidGroupChange.event; }
private _onWillOpenEditor: Emitter<IEditorOpeningEvent> = this._register(new Emitter<IEditorOpeningEvent>());
private readonly _onWillOpenEditor: Emitter<IEditorOpeningEvent> = this._register(new Emitter<IEditorOpeningEvent>());
get onWillOpenEditor(): Event<IEditorOpeningEvent> { return this._onWillOpenEditor.event; }
private _onDidOpenEditorFail: Emitter<EditorInput> = this._register(new Emitter<EditorInput>());
private readonly _onDidOpenEditorFail: Emitter<EditorInput> = this._register(new Emitter<EditorInput>());
get onDidOpenEditorFail(): Event<EditorInput> { return this._onDidOpenEditorFail.event; }
private _onWillCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
private readonly _onWillCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
get onWillCloseEditor(): Event<IEditorCloseEvent> { return this._onWillCloseEditor.event; }
private _onDidCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
private readonly _onDidCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
get onDidCloseEditor(): Event<IEditorCloseEvent> { return this._onDidCloseEditor.event; }
//#endregion
@@ -112,7 +115,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private editorContainer: HTMLElement;
private editorControl: EditorControl;
private ignoreOpenEditorErrors: boolean;
private disposedEditorsWorker: RunOnceWorker<EditorInput>;
private mapEditorToPendingConfirmation: Map<EditorInput, Promise<boolean>> = new Map<EditorInput, Promise<boolean>>();
@@ -130,6 +132,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IMenuService private readonly menuService: IMenuService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IHashService private readonly hashService: IHashService,
// {{SQL CARBON EDIT}}
@ICommandService private commandService: ICommandService
) {
@@ -417,6 +420,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
const activeEditor = this._group.activeEditor;
if (!activeEditor) {
return Promise.resolve();
}
options.pinned = this._group.isPinned(activeEditor); // preserve pinned state
options.preserveFocus = true; // handle focus after editor is opened
@@ -458,14 +465,18 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
private onDidEditorOpen(editor: EditorInput): void {
/* __GDPR__
"editorOpened" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorOpened', editor.getTelemetryDescriptor());
// Telemetry
this.toEditorTelemetryDescriptor(editor).then(descriptor => {
/* __GDPR__
"editorOpened" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorOpened', descriptor);
});
// Update container
this.updateContainer();
@@ -496,14 +507,17 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
});
/* __GDPR__
"editorClosed" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorClosed', event.editor.getTelemetryDescriptor());
// Telemetry
this.toEditorTelemetryDescriptor(event.editor).then(descriptor => {
/* __GDPR__
"editorClosed" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorClosed', descriptor);
});
// Update container
this.updateContainer();
@@ -513,6 +527,26 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_CLOSE, editor, editorIndex: event.index });
}
private toEditorTelemetryDescriptor(editor: EditorInput): Thenable<object> {
const descriptor = editor.getTelemetryDescriptor();
const resource = editor.getResource();
if (resource && resource.fsPath) {
return this.hashService.createSHA1(resource.fsPath).then(hashedPath => {
descriptor['resource'] = { mimeType: guessMimeTypes(resource.fsPath).join(', '), scheme: resource.scheme, ext: extname(resource.fsPath), path: hashedPath };
/* __GDPR__FRAGMENT__
"EditorTelemetryDescriptor" : {
"resource": { "${inline}": [ "${URIDescriptor}" ] }
}
*/
return descriptor;
});
}
return Promise.resolve(descriptor);
}
private onDidEditorDispose(editor: EditorInput): void {
// To prevent race conditions, we handle disposed editors in our worker with a timeout
@@ -524,7 +558,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private handleDisposedEditors(editors: EditorInput[]): void {
// Split between visible and hidden editors
let activeEditor: EditorInput;
let activeEditor: EditorInput | undefined;
const inactiveEditors: EditorInput[] = [];
editors.forEach(editor => {
if (this._group.isActive(editor)) {
@@ -567,7 +601,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Pin preview editor once user disables preview
if (event.oldPartOptions.enablePreview && !event.newPartOptions.enablePreview) {
this.pinEditor(this._group.previewEditor);
if (this._group.previewEditor) {
this.pinEditor(this._group.previewEditor);
}
}
}
@@ -658,15 +694,15 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this._group.count;
}
get activeControl(): BaseEditor {
return this.editorControl ? this.editorControl.activeControl : undefined;
get activeControl(): IVisibleEditor | undefined {
return this.editorControl ? withNullAsUndefined(this.editorControl.activeControl) : undefined;
}
get activeEditor(): EditorInput {
get activeEditor(): EditorInput | null {
return this._group.activeEditor;
}
get previewEditor(): EditorInput {
get previewEditor(): EditorInput | null {
return this._group.previewEditor;
}
@@ -686,7 +722,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this.editors;
}
getEditor(index: number): EditorInput {
getEditor(index: number): EditorInput | null {
return this._group.getEditor(index);
}
@@ -711,7 +747,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._onDidFocus.fire();
}
pinEditor(editor: EditorInput = this.activeEditor): void {
pinEditor(editor: EditorInput | undefined = this.activeEditor || undefined): void {
if (editor && !this._group.isPinned(editor)) {
// Update model
@@ -749,7 +785,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this.doOpenEditor(editor, options);
}
private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise<IEditor> {
private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise<IEditor | null> {
// Determine options
const openEditorOptions: IEditorOpenOptions = {
@@ -758,7 +794,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
active: this._group.count === 0 || !options || !options.inactive
};
if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.isPreview(this._group.activeEditor)) {
if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.activeEditor && this._group.isPreview(this._group.activeEditor)) {
// Special case: we are to open an editor inactive and not pinned, but the current active
// editor is also not pinned, which means it will get replaced with this one. As such,
// the editor can only be active.
@@ -787,13 +823,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._group.openEditor(editor, openEditorOptions);
// Show editor
return this.doShowEditor(editor, openEditorOptions.active, options);
return this.doShowEditor(editor, !!openEditorOptions.active, options);
}
private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise<IEditor> {
private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise<IEditor | null> {
// Show in editor control if the active editor changed
let openEditorPromise: Promise<IEditor>;
let openEditorPromise: Promise<IEditor | null>;
if (active) {
openEditorPromise = this.editorControl.openEditor(editor, options).then(result => {
@@ -824,7 +860,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Report error only if this was not us restoring previous error state or
// we are told to ignore errors that occur from opening an editor
if (this.isRestored && !isPromiseCanceledError(error) && !this.ignoreOpenEditorErrors) {
if (this.isRestored && !isPromiseCanceledError(error) && (!options || !options.ignoreError)) {
const actions: INotificationActions = { primary: [] };
if (isErrorWithActions(error)) {
actions.primary = (error as IErrorWithActions).actions;
@@ -836,7 +872,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
actions
});
Event.once(handle.onDidClose)(() => dispose(actions.primary));
Event.once(handle.onDidClose)(() => actions.primary && dispose(actions.primary));
}
// Event
@@ -861,10 +897,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Do not modify original array
editors = editors.slice(0);
let result: IEditor;
let result: IEditor | null;
// Use the first editor as active editor
const { editor, options } = editors.shift();
const { editor, options } = editors.shift()!;
return this.openEditor(editor, options).then(activeEditor => {
result = activeEditor; // this can be NULL if the opening failed
@@ -964,7 +1000,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
//#region closeEditor()
closeEditor(editor: EditorInput = this.activeEditor): Promise<void> {
closeEditor(editor: EditorInput | undefined = this.activeEditor || undefined, options?: ICloseEditorOptions): Promise<void> {
if (!editor) {
return Promise.resolve();
}
@@ -976,11 +1012,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Do close
this.doCloseEditor(editor);
this.doCloseEditor(editor, options && options.preserveFocus ? false : undefined);
});
}
private doCloseEditor(editor: EditorInput, focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
private doCloseEditor(editor: EditorInput, focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void {
// Closing the active editor of the group is a bit more work
if (this._group.isActive(editor)) {
@@ -996,7 +1032,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this.titleAreaControl.closeEditor(editor);
}
private doCloseActiveEditor(focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
private doCloseActiveEditor(focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void {
const editorToClose = this.activeEditor;
const restoreFocus = this.shouldRestoreFocus(this.element);
@@ -1021,32 +1057,34 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Update model
this._group.closeEditor(editorToClose);
if (editorToClose) {
this._group.closeEditor(editorToClose);
}
// Open next active if there are more to show
const nextActiveEditor = this._group.activeEditor;
if (nextActiveEditor) {
const options = EditorOptions.create({ preserveFocus: !focusNext });
// When closing an editor due to an error we can end up in a loop where we continue closing
// editors that fail to open (e.g. when the file no longer exists). We do not want to show
// repeated errors in this case to the user. As such, if we open the next editor and we are
// in a scope of a previous editor failing, we silence the input errors until the editor is
// opened.
// opened by setting ignoreError: true.
if (fromError) {
this.ignoreOpenEditorErrors = true;
options.ignoreError = true;
}
const options = !focusNext ? EditorOptions.create({ preserveFocus: true }) : undefined;
this.openEditor(nextActiveEditor, options).then(() => {
this.ignoreOpenEditorErrors = false;
});
this.openEditor(nextActiveEditor, options);
}
// Otherwise we are empty, so clear from editor control and send event
else {
// Forward to editor control
this.editorControl.closeEditor(editorToClose);
if (editorToClose) {
this.editorControl.closeEditor(editorToClose);
}
// Restore focus to group container as needed unless group gets closed
if (restoreFocus && !closeEmptyGroup) {
@@ -1085,7 +1123,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return Promise.resolve(false); // no veto
}
const editor = editors.shift();
const editor = editors.shift()!;
// To prevent multiple confirmation dialogs from showing up one after the other
// we check if a pending confirmation is currently showing and if so, join that
@@ -1157,7 +1195,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
//#region closeEditors()
closeEditors(args: EditorInput[] | ICloseEditorsFilter): Promise<void> {
closeEditors(args: EditorInput[] | ICloseEditorsFilter, options?: ICloseEditorOptions): Promise<void> {
if (this.isEmpty()) {
return Promise.resolve();
}
@@ -1171,7 +1209,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Do close
this.doCloseEditors(editors);
this.doCloseEditors(editors, options);
});
}
@@ -1205,7 +1243,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return editorsToClose;
}
private doCloseEditors(editors: EditorInput[]): void {
private doCloseEditors(editors: EditorInput[], options?: ICloseEditorOptions): void {
// Close all inactive editors first
let closeActiveEditor = false;
@@ -1219,7 +1257,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Close active editor last if contained in editors list to close
if (closeActiveEditor) {
this.doCloseActiveEditor();
this.doCloseActiveEditor(options && options.preserveFocus ? false : undefined);
}
// Forward to title control
@@ -1278,7 +1316,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
replaceEditors(editors: EditorReplacement[]): Promise<void> {
// Extract active vs. inactive replacements
let activeReplacement: EditorReplacement;
let activeReplacement: EditorReplacement | undefined;
const inactiveReplacements: EditorReplacement[] = [];
editors.forEach(({ editor, replacement, options }) => {
if (editor.isDirty()) {
@@ -1314,11 +1352,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Open inactive editor
this.doOpenEditor(replacement, options);
// Close replaced inactive edior
this.doCloseInactiveEditor(editor);
// Forward to title control
this.titleAreaControl.closeEditor(editor);
// Close replaced inactive editor unless they match
if (!editor.matches(replacement)) {
this.doCloseInactiveEditor(editor);
this.titleAreaControl.closeEditor(editor);
}
});
// Handle active last
@@ -1327,11 +1365,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Open replacement as active editor
const openEditorResult = this.doOpenEditor(activeReplacement.replacement, activeReplacement.options);
// Close previous active editor
this.doCloseInactiveEditor(activeReplacement.editor);
// Forward to title control
this.titleAreaControl.closeEditor(activeReplacement.editor);
// Close replaced active editor unless they match
if (!activeReplacement.editor.matches(activeReplacement.replacement)) {
this.doCloseInactiveEditor(activeReplacement.editor);
this.titleAreaControl.closeEditor(activeReplacement.editor);
}
return openEditorResult.then(() => undefined);
}
@@ -1384,8 +1422,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
get maximumWidth(): number { return this.editorControl.maximumWidth; }
get maximumHeight(): number { return this.editorControl.maximumHeight; }
private _onDidChange = this._register(new Relay<{ width: number; height: number; }>());
readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event;
private _onDidChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
layout(width: number, height: number): void {
this.dimension = new Dimension(width, height);
@@ -1430,7 +1468,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
constructor(
private _group: GroupIdentifier,
private _editor: EditorInput,
private _options: EditorOptions
private _options: EditorOptions | undefined
) {
}
@@ -1442,7 +1480,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
return this._editor;
}
get options(): EditorOptions {
get options(): EditorOptions | undefined {
return this._options;
}
@@ -1464,10 +1502,10 @@ export interface EditorReplacement {
registerThemingParticipant((theme, collector, environment) => {
// Letterpress
const letterpress = `resources/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`;
const letterpress = `./media/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`;
collector.addRule(`
.monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress {
background-image: url('${URI.file(join(environment.appRoot, letterpress)).toString()}')
background-image: url('${require.toUrl(letterpress)}')
}
`);

View File

@@ -9,14 +9,14 @@ import { Part } from 'vs/workbench/browser/part';
import { Dimension, isAncestor, toggleClass, addClass, $ } from 'vs/base/browser/dom';
import { Event, Emitter, Relay } from 'vs/base/common/event';
import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument } from 'vs/workbench/services/group/common/editorGroupsService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid, ISerializableView } from 'vs/base/browser/ui/grid/grid';
import { GroupIdentifier, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid';
import { GroupIdentifier, IWorkbenchEditorConfiguration, IEditorPartOptions } from 'vs/workbench/common/editor';
import { values } from 'vs/base/common/map';
import { EDITOR_GROUP_BORDER, EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme';
import { distinct } from 'vs/base/common/arrays';
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions, getEditorPartOptions, impactsEditorPartOptions, IEditorPartOptionsChangeEvent, EditorGroupsServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
import { distinct, coalesce } from 'vs/base/common/arrays';
import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsEditorPartOptions, IEditorPartOptionsChangeEvent, IEditorPartCreationOptions } from 'vs/workbench/browser/parts/editor/editor';
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
@@ -29,7 +29,8 @@ import { Color } from 'vs/base/common/color';
import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout';
import { IView, orthogonal, LayoutPriority } from 'vs/base/browser/ui/grid/gridview';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Parts } from 'vs/workbench/services/part/common/partService';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
// {{SQL CARBON EDIT}}
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
@@ -50,8 +51,8 @@ class GridWidgetView<T extends IView> implements IView {
get minimumHeight(): number { return this.gridWidget ? this.gridWidget.minimumHeight : 0; }
get maximumHeight(): number { return this.gridWidget ? this.gridWidget.maximumHeight : Number.POSITIVE_INFINITY; }
private _onDidChange = new Relay<{ width: number; height: number; }>();
readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event;
private _onDidChange = new Relay<{ width: number; height: number; } | undefined>();
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
private _gridWidget: Grid<T>;
@@ -83,44 +84,43 @@ class GridWidgetView<T extends IView> implements IView {
}
}
export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditorGroupsAccessor, ISerializableView {
export class EditorPart extends Part implements IEditorGroupsService, IEditorGroupsAccessor {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.state';
private static readonly EDITOR_PART_CENTERED_VIEW_STORAGE_KEY = 'editorpart.centeredview';
//#region Events
private _onDidLayout: Emitter<Dimension> = this._register(new Emitter<Dimension>());
private readonly _onDidLayout: Emitter<Dimension> = this._register(new Emitter<Dimension>());
get onDidLayout(): Event<Dimension> { return this._onDidLayout.event; }
private _onDidActiveGroupChange: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
private readonly _onDidActiveGroupChange: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidActiveGroupChange(): Event<IEditorGroupView> { return this._onDidActiveGroupChange.event; }
private _onDidAddGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
private readonly _onDidActivateGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidActivateGroup(): Event<IEditorGroupView> { return this._onDidActivateGroup.event; }
private readonly _onDidAddGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidAddGroup(): Event<IEditorGroupView> { return this._onDidAddGroup.event; }
private _onDidRemoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
private readonly _onDidRemoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidRemoveGroup(): Event<IEditorGroupView> { return this._onDidRemoveGroup.event; }
private _onDidMoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
private readonly _onDidMoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidMoveGroup(): Event<IEditorGroupView> { return this._onDidMoveGroup.event; }
private onDidSetGridWidget = this._register(new Emitter<{ width: number; height: number; }>());
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; }>());
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; }> { return Event.any(this.onDidSetGridWidget.event, this._onDidSizeConstraintsChange.event); }
private onDidSetGridWidget = this._register(new Emitter<{ width: number; height: number; } | undefined>());
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; } | undefined> { return Event.any(this.onDidSetGridWidget.event, this._onDidSizeConstraintsChange.event); }
private _onDidPreferredSizeChange: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidPreferredSizeChange: Emitter<void> = this._register(new Emitter<void>());
get onDidPreferredSizeChange(): Event<void> { return this._onDidPreferredSizeChange.event; }
private _onDidActivateGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidActivateGroup(): Event<IEditorGroupView> { return this._onDidActivateGroup.event; }
//#endregion
private dimension: Dimension;
private _preferredSize: Dimension;
private _preferredSize: Dimension | undefined;
private workspaceMemento: object;
private globalMemento: object;
@@ -139,22 +139,14 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
private _whenRestored: Promise<void>;
private whenRestoredResolve: () => void;
element: HTMLElement;
private _onDidChange = new Emitter<{ width: number; height: number; }>();
readonly onDidChange = this._onDidChange.event;
priority: LayoutPriority = LayoutPriority.High;
constructor(
id: string,
private restorePreviousState: boolean,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStorageService storageService: IStorageService
@IStorageService storageService: IStorageService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
) {
super(id, { hasTitle: false }, themeService, storageService);
super(Parts.EDITOR_PART, { hasTitle: false }, themeService, storageService, layoutService);
this.gridWidgetView = new GridWidgetView<IEditorGroupView>();
@@ -172,7 +164,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
private enforcedPartOptions: IEditorPartOptions[] = [];
private _onDidEditorPartOptionsChange: Emitter<IEditorPartOptionsChangeEvent> = this._register(new Emitter<IEditorPartOptionsChangeEvent>());
private readonly _onDidEditorPartOptionsChange: Emitter<IEditorPartOptionsChangeEvent> = this._register(new Emitter<IEditorPartOptionsChangeEvent>());
get onDidEditorPartOptionsChange(): Event<IEditorPartOptionsChangeEvent> { return this._onDidEditorPartOptionsChange.event; }
private registerListeners(): void {
@@ -216,6 +208,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
//#region IEditorGroupsService
private _dimension: Dimension;
get dimension(): Dimension { return this._dimension; }
get activeGroup(): IEditorGroupView {
return this._activeGroup;
}
@@ -229,11 +224,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
get orientation(): GroupOrientation {
if (!this.gridWidget) {
return undefined; // we have not been created yet
}
return this.gridWidget.orientation === Orientation.VERTICAL ? GroupOrientation.VERTICAL : GroupOrientation.HORIZONTAL;
return (this.gridWidget && this.gridWidget.orientation === Orientation.VERTICAL) ? GroupOrientation.VERTICAL : GroupOrientation.HORIZONTAL;
}
get whenRestored(): Promise<void> {
@@ -246,7 +237,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return this.groups;
case GroupsOrder.MOST_RECENTLY_ACTIVE:
const mostRecentActive = this.mostRecentActiveGroups.map(groupId => this.getGroup(groupId));
const mostRecentActive = coalesce(this.mostRecentActiveGroups.map(groupId => this.getGroup(groupId)));
// there can be groups that got never active, even though they exist. in this case
// make sure to ust append them at the end so that all groups are returned properly
@@ -270,7 +261,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
}
getGroup(identifier: GroupIdentifier): IEditorGroupView {
getGroup(identifier: GroupIdentifier): IEditorGroupView | undefined {
return this.groupViews.get(identifier);
}
@@ -282,7 +273,11 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
// by location
return this.doFindGroupByLocation(scope.location, source, wrap);
if (typeof scope.location === 'number') {
return this.doFindGroupByLocation(scope.location, source, wrap);
}
throw new Error('invalid arguments');
}
private doFindGroupByDirection(direction: GroupDirection, source: IEditorGroupView | GroupIdentifier, wrap?: boolean): IEditorGroupView {
@@ -422,7 +417,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.doCreateGridControlWithState(gridDescriptor, activeGroup.id, currentGroupViews);
// Layout
this.doLayout(this.dimension);
this.doLayout(this._dimension);
// Update container
this.updateContainer();
@@ -506,7 +501,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return newGroupView;
}
private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroup): IEditorGroupView {
private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroup | null): IEditorGroupView {
// Label: just use the number of existing groups as label
const label = this.getGroupLabel(this.count + 1);
@@ -601,7 +596,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
}
private toGridViewOrientation(orientation: GroupOrientation, fallback?: Orientation): Orientation {
private toGridViewOrientation(orientation: GroupOrientation, fallback: Orientation): Orientation {
if (typeof orientation === 'number') {
return orientation === GroupOrientation.HORIZONTAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
}
@@ -742,21 +737,26 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
private assertGroupView(group: IEditorGroupView | GroupIdentifier): IEditorGroupView {
let groupView: IEditorGroupView | undefined;
if (typeof group === 'number') {
group = this.getGroup(group);
groupView = this.getGroup(group);
} else {
groupView = group;
}
if (!group) {
if (!groupView) {
throw new Error('Invalid editor group provided!');
}
return group;
return groupView;
}
//#endregion
//#region Part
readonly priority: LayoutPriority = LayoutPriority.High;
get minimumWidth(): number { return this.centeredLayoutWidget.minimumWidth; }
get maximumWidth(): number { return this.centeredLayoutWidget.maximumWidth; }
get minimumHeight(): number { return this.centeredLayoutWidget.minimumHeight; }
@@ -783,7 +783,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return this.theme.getColor(EDITOR_GROUP_BORDER) || this.theme.getColor(contrastBorder) || Color.transparent;
}
protected updateStyles(): void {
updateStyles(): void {
this.container.style.backgroundColor = this.getColor(editorBackground);
const separatorBorderStyle = { separatorBorder: this.gridSeparatorBorder, background: this.theme.getColor(EDITOR_PANE_BACKGROUND) || Color.transparent };
@@ -791,7 +791,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.centeredLayoutWidget.styles(separatorBorderStyle);
}
createContentArea(parent: HTMLElement): HTMLElement {
createContentArea(parent: HTMLElement, options?: IEditorPartCreationOptions): HTMLElement {
// Container
this.element = parent;
@@ -800,7 +800,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
parent.appendChild(this.container);
// Grid control with center layout
this.doCreateGridControl();
this.doCreateGridControl(options);
this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]));
@@ -819,15 +819,16 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return this.centeredLayoutWidget.isActive();
}
private doCreateGridControl(): void {
private doCreateGridControl(options?: IEditorPartCreationOptions): void {
// Grid Widget (with previous UI state)
if (this.restorePreviousState) {
this.doCreateGridControlWithPreviousState();
let restoreError = false;
if (!options || options.restorePreviousState) {
restoreError = !this.doCreateGridControlWithPreviousState();
}
// Grid Widget (no previous UI state or failed to restore)
if (!this.gridWidget) {
if (!this.gridWidget || restoreError) {
const initialGroup = this.doCreateGroupView();
this.doSetGridWidget(new SerializableGrid(initialGroup));
@@ -842,7 +843,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.updateContainer();
}
private doCreateGridControlWithPreviousState(): void {
private doCreateGridControlWithPreviousState(): boolean {
const uiState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState;
if (uiState && uiState.serializedGrid) {
try {
@@ -856,25 +857,20 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
// Ensure last active group has focus
this._activeGroup.focus();
} catch (error) {
this.handleGridRestoreError(error, uiState);
// Log error
onUnexpectedError(new Error(`Error restoring editor grid widget: ${error} (with state: ${JSON.stringify(uiState)})`));
// Clear any state we have from the failing restore
this.groupViews.forEach(group => group.dispose());
this.groupViews.clear();
this.mostRecentActiveGroups = [];
return false; // failure
}
}
}
private handleGridRestoreError(error: Error, state: IEditorPartUIState): void {
// Log error
onUnexpectedError(new Error(`Error restoring editor grid widget: ${error} (with state: ${JSON.stringify(state)})`));
// Clear any state we have from the failing restore
if (this.gridWidget) {
this.doSetGridWidget();
}
this.groupViews.forEach(group => group.dispose());
this.groupViews.clear();
this._activeGroup = undefined;
this.mostRecentActiveGroups = [];
return true; // success
}
private doCreateGridControlWithState(serializedGrid: ISerializedGrid, activeGroupId: GroupIdentifier, editorGroupViewsToReuse?: IEditorGroupView[]): void {
@@ -893,7 +889,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
fromJSON: (serializedEditorGroup: ISerializedEditorGroup | null) => {
let groupView: IEditorGroupView;
if (reuseGroupViews.length > 0) {
groupView = reuseGroupViews.shift();
groupView = reuseGroupViews.shift()!;
} else {
groupView = this.doCreateGroupView(serializedEditorGroup);
}
@@ -924,7 +920,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.doSetGridWidget(gridWidget);
}
private doSetGridWidget(gridWidget?: SerializableGrid<IEditorGroupView>): void {
private doSetGridWidget(gridWidget: SerializableGrid<IEditorGroupView>): void {
if (this.gridWidget) {
this.gridWidget.dispose();
}
@@ -932,9 +928,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.gridWidget = gridWidget;
this.gridWidgetView.gridWidget = gridWidget;
if (gridWidget) {
this._onDidSizeConstraintsChange.input = gridWidget.onDidChange;
}
this._onDidSizeConstraintsChange.input = gridWidget.onDidChange;
this.onDidSetGridWidget.fire(undefined);
}
@@ -962,23 +956,20 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return this.groupViews.size === 1 && this._activeGroup.isEmpty();
}
layout(dimension: Dimension): Dimension[];
layout(width: number, height: number): void;
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2));
layout(width: number, height: number): void {
this.doLayout(sizes[1]);
// Layout contents
const contentAreaSize = super.layoutContents(width, height).contentSize;
if (dim1 instanceof Dimension) {
return sizes;
}
// Layout editor container
this.doLayout(contentAreaSize);
}
private doLayout(dimension: Dimension): void {
this.dimension = dimension;
this._dimension = dimension;
// Layout Grid
this.centeredLayoutWidget.layout(this.dimension.width, this.dimension.height);
this.centeredLayoutWidget.layout(this._dimension.width, this._dimension.height);
// Event
this._onDidLayout.fire(dimension);
@@ -1039,3 +1030,5 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
};
}
}
registerSingleton(IEditorGroupsService, EditorPart);

View File

@@ -5,7 +5,6 @@
import 'vs/css!./media/editorpicker';
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';
@@ -14,7 +13,7 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { IModelService } from 'vs/editor/common/services/modelService';
import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { EditorInput, toResource } from 'vs/workbench/common/editor';
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
@@ -33,12 +32,12 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
getLabelOptions(): IIconLabelValueOptions {
return {
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()),
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource() || undefined),
italic: !this._group.isPinned(this.editor)
};
}
getLabel(): string {
getLabel() {
return this.editor.getName();
}
@@ -50,7 +49,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
return this._group;
}
getResource(): URI {
getResource() {
return toResource(this.editor, { supportSideBySide: true });
}
@@ -58,7 +57,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
return nls.localize('entryAriaLabel', "{0}, editor group picker", this.getLabel());
}
getDescription(): string {
getDescription() {
return this.editor.getDescription();
}
@@ -109,7 +108,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
return false;
}
e.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch);
e.setHighlights(itemScore.labelMatch || [], itemScore.descriptionMatch);
return true;
});

View File

@@ -7,16 +7,15 @@ import 'vs/css!./media/editorstatus';
import * as nls from 'vs/nls';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings';
import * as paths from 'vs/base/common/paths';
import { extname, basename } from 'vs/base/common/resources';
import * as types from 'vs/base/common/types';
import { URI as uri } from 'vs/base/common/uri';
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 { Language } from 'vs/base/common/platform';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IEditorAction } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model';
@@ -27,7 +26,6 @@ import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/bina
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
@@ -41,7 +39,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { deepClone } from 'vs/base/common/objects';
import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Schemas } from 'vs/base/common/network';
@@ -51,6 +49,10 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { timeout } from 'vs/base/common/async';
import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { Event } from 'vs/base/common/event';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/workbench/services/queryEditor/browser/queryEditorService';
class SideBySideEditorEncodingSupport implements IEncodingSupport {
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
@@ -64,10 +66,7 @@ class SideBySideEditorEncodingSupport implements IEncodingSupport {
}
}
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/workbench/services/queryEditor/browser/queryEditorService';
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport {
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | null {
// Untitled Editor
if (input instanceof UntitledEditorInput) {
@@ -104,25 +103,14 @@ interface IEditorSelectionStatus {
class StateChange {
_stateChangeBrand: void;
indentation: boolean;
selectionStatus: boolean;
mode: boolean;
encoding: boolean;
EOL: boolean;
tabFocusMode: boolean;
screenReaderMode: boolean;
metadata: boolean;
constructor() {
this.indentation = false;
this.selectionStatus = false;
this.mode = false;
this.encoding = false;
this.EOL = false;
this.tabFocusMode = false;
this.screenReaderMode = false;
this.metadata = false;
}
indentation: boolean = false;
selectionStatus: boolean = false;
mode: boolean = false;
encoding: boolean = false;
EOL: boolean = false;
tabFocusMode: boolean = false;
screenReaderMode: boolean = false;
metadata: boolean = false;
combine(other: StateChange) {
this.indentation = this.indentation || other.indentation;
@@ -134,6 +122,17 @@ class StateChange {
this.screenReaderMode = this.screenReaderMode || other.screenReaderMode;
this.metadata = this.metadata || other.metadata;
}
public hasChanges(): boolean {
return this.indentation
|| this.selectionStatus
|| this.mode
|| this.encoding
|| this.EOL
|| this.tabFocusMode
|| this.screenReaderMode
|| this.metadata;
}
}
interface StateDelta {
@@ -144,33 +143,33 @@ interface StateDelta {
indentation?: string;
tabFocusMode?: boolean;
screenReaderMode?: boolean;
metadata?: string;
metadata?: string | null;
}
class State {
private _selectionStatus: string;
get selectionStatus(): string { return this._selectionStatus; }
private _selectionStatus: string | null | undefined;
get selectionStatus(): string | null | undefined { return this._selectionStatus; }
private _mode: string;
get mode(): string { return this._mode; }
private _mode: string | null | undefined;
get mode(): string | null | undefined { return this._mode; }
private _encoding: string;
get encoding(): string { return this._encoding; }
private _encoding: string | null | undefined;
get encoding(): string | null | undefined { return this._encoding; }
private _EOL: string;
get EOL(): string { return this._EOL; }
private _EOL: string | null | undefined;
get EOL(): string | null | undefined { return this._EOL; }
private _indentation: string;
get indentation(): string { return this._indentation; }
private _indentation: string | null | undefined;
get indentation(): string | null | undefined { return this._indentation; }
private _tabFocusMode: boolean;
get tabFocusMode(): boolean { return this._tabFocusMode; }
private _tabFocusMode: boolean | null | undefined;
get tabFocusMode(): boolean | null | undefined { return this._tabFocusMode; }
private _screenReaderMode: boolean;
get screenReaderMode(): boolean { return this._screenReaderMode; }
private _screenReaderMode: boolean | null | undefined;
get screenReaderMode(): boolean | null | undefined { return this._screenReaderMode; }
private _metadata: string;
get metadata(): string { return this._metadata; }
private _metadata: string | null | undefined;
get metadata(): string | null | undefined { return this._metadata; }
constructor() {
this._selectionStatus = null;
@@ -183,70 +182,58 @@ class State {
}
update(update: StateDelta): StateChange {
const e = new StateChange();
let somethingChanged = false;
const change = new StateChange();
if (typeof update.selectionStatus !== 'undefined') {
if ('selectionStatus' in update) {
if (this._selectionStatus !== update.selectionStatus) {
this._selectionStatus = update.selectionStatus;
somethingChanged = true;
e.selectionStatus = true;
change.selectionStatus = true;
}
}
if (typeof update.indentation !== 'undefined') {
if ('indentation' in update) {
if (this._indentation !== update.indentation) {
this._indentation = update.indentation;
somethingChanged = true;
e.indentation = true;
change.indentation = true;
}
}
if (typeof update.mode !== 'undefined') {
if ('mode' in update) {
if (this._mode !== update.mode) {
this._mode = update.mode;
somethingChanged = true;
e.mode = true;
change.mode = true;
}
}
if (typeof update.encoding !== 'undefined') {
if ('encoding' in update) {
if (this._encoding !== update.encoding) {
this._encoding = update.encoding;
somethingChanged = true;
e.encoding = true;
change.encoding = true;
}
}
if (typeof update.EOL !== 'undefined') {
if ('EOL' in update) {
if (this._EOL !== update.EOL) {
this._EOL = update.EOL;
somethingChanged = true;
e.EOL = true;
change.EOL = true;
}
}
if (typeof update.tabFocusMode !== 'undefined') {
if ('tabFocusMode' in update) {
if (this._tabFocusMode !== update.tabFocusMode) {
this._tabFocusMode = update.tabFocusMode;
somethingChanged = true;
e.tabFocusMode = true;
change.tabFocusMode = true;
}
}
if (typeof update.screenReaderMode !== 'undefined') {
if ('screenReaderMode' in update) {
if (this._screenReaderMode !== update.screenReaderMode) {
this._screenReaderMode = update.screenReaderMode;
somethingChanged = true;
e.screenReaderMode = true;
change.screenReaderMode = true;
}
}
if (typeof update.metadata !== 'undefined') {
if ('metadata' in update) {
if (this._metadata !== update.metadata) {
this._metadata = update.metadata;
somethingChanged = true;
e.metadata = true;
change.metadata = true;
}
}
if (somethingChanged) {
return e;
}
return null;
return change;
}
}
@@ -260,34 +247,51 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus");
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized");
const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\".");
function setDisplay(el: HTMLElement, desiredValue: string): void {
if (el.style.display !== desiredValue) {
el.style.display = desiredValue;
class StatusBarItem {
private _showing = true;
constructor(
private readonly element: HTMLElement,
title: string,
) {
this.setVisible(false);
this.element.title = title;
}
public set textContent(value: string) {
this.element.textContent = value;
}
public set onclick(value: () => void) {
this.element.onclick = value;
}
public setVisible(shouldShow: boolean): void {
if (shouldShow !== this._showing) {
this._showing = shouldShow;
this.element.style.display = shouldShow ? '' : 'none';
}
}
}
function show(el: HTMLElement): void {
setDisplay(el, '');
}
function hide(el: HTMLElement): void {
setDisplay(el, 'none');
}
export class EditorStatus implements IStatusbarItem {
private state: State;
private element: HTMLElement;
private tabFocusModeElement: HTMLElement;
private screenRedearModeElement: HTMLElement;
private indentationElement: HTMLElement;
private selectionElement: HTMLElement;
private encodingElement: HTMLElement;
private eolElement: HTMLElement;
private modeElement: HTMLElement;
private metadataElement: HTMLElement;
private tabFocusModeElement: StatusBarItem;
private screenRedearModeElement: StatusBarItem;
private indentationElement: StatusBarItem;
private selectionElement: StatusBarItem;
private encodingElement: StatusBarItem;
private eolElement: StatusBarItem;
private modeElement: StatusBarItem;
private metadataElement: StatusBarItem;
private toDispose: IDisposable[];
private activeEditorListeners: IDisposable[];
private delayedRender: IDisposable;
private toRender: StateChange;
private screenReaderNotification: INotificationHandle;
private delayedRender: IDisposable | null;
private toRender: StateChange | null;
private screenReaderNotification: INotificationHandle | null;
constructor(
@IEditorService private readonly editorService: IEditorService,
@@ -296,8 +300,9 @@ export class EditorStatus implements IStatusbarItem {
@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
@IModeService private readonly modeService: IModeService,
@ITextFileService private readonly textFileService: ITextFileService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
@INotificationService private readonly notificationService: INotificationService
@IConfigurationService private readonly configurationService: IConfigurationService,
@INotificationService private readonly notificationService: INotificationService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
) {
this.toDispose = [];
this.activeEditorListeners = [];
@@ -307,59 +312,57 @@ export class EditorStatus implements IStatusbarItem {
render(container: HTMLElement): IDisposable {
this.element = append(container, $('.editor-statusbar-item'));
this.tabFocusModeElement = append(this.element, $('a.editor-status-tabfocusmode.status-bar-info'));
this.tabFocusModeElement.title = nls.localize('disableTabMode', "Disable Accessibility Mode");
this.tabFocusModeElement = new StatusBarItem(
append(this.element, $('a.editor-status-tabfocusmode.status-bar-info')),
nls.localize('disableTabMode', "Disable Accessibility Mode"));
this.tabFocusModeElement.onclick = () => this.onTabFocusModeClick();
this.tabFocusModeElement.textContent = nlsTabFocusMode;
hide(this.tabFocusModeElement);
this.screenRedearModeElement = append(this.element, $('a.editor-status-screenreadermode.status-bar-info'));
this.screenRedearModeElement = new StatusBarItem(
append(this.element, $('a.editor-status-screenreadermode.status-bar-info')),
nlsScreenReaderDetectedTitle);
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'));
this.selectionElement.title = nls.localize('gotoLine', "Go to Line");
this.selectionElement = new StatusBarItem(
append(this.element, $('a.editor-status-selection')),
nls.localize('gotoLine', "Go to Line"));
this.selectionElement.onclick = () => this.onSelectionClick();
hide(this.selectionElement);
this.indentationElement = append(this.element, $('a.editor-status-indentation'));
this.indentationElement.title = nls.localize('selectIndentation', "Select Indentation");
this.indentationElement = new StatusBarItem(
append(this.element, $('a.editor-status-indentation')),
nls.localize('selectIndentation', "Select Indentation"));
this.indentationElement.onclick = () => this.onIndentationClick();
hide(this.indentationElement);
this.encodingElement = append(this.element, $('a.editor-status-encoding'));
this.encodingElement.title = nls.localize('selectEncoding', "Select Encoding");
this.encodingElement = new StatusBarItem(
append(this.element, $('a.editor-status-encoding')),
nls.localize('selectEncoding', "Select Encoding"));
this.encodingElement.onclick = () => this.onEncodingClick();
hide(this.encodingElement);
this.eolElement = append(this.element, $('a.editor-status-eol'));
this.eolElement.title = nls.localize('selectEOL', "Select End of Line Sequence");
this.eolElement = new StatusBarItem(
append(this.element, $('a.editor-status-eol')),
nls.localize('selectEOL', "Select End of Line Sequence"));
this.eolElement.onclick = () => this.onEOLClick();
hide(this.eolElement);
this.modeElement = append(this.element, $('a.editor-status-mode'));
this.modeElement.title = nls.localize('selectLanguageMode', "Select Language Mode");
this.modeElement = new StatusBarItem(
append(this.element, $('a.editor-status-mode')),
nls.localize('selectLanguageMode', "Select Language Mode"));
this.modeElement.onclick = () => this.onModeClick();
hide(this.modeElement);
this.metadataElement = append(this.element, $('span.editor-status-metadata'));
this.metadataElement.title = nls.localize('fileInfo', "File Information");
hide(this.metadataElement);
this.metadataElement = new StatusBarItem(
append(this.element, $('span.editor-status-metadata')),
nls.localize('fileInfo', "File Information"));
this.delayedRender = null;
this.toRender = null;
this.toDispose.push(
{
dispose: () => {
if (this.delayedRender) {
this.delayedRender.dispose();
this.delayedRender = null;
}
toDisposable(() => {
if (this.delayedRender) {
this.delayedRender.dispose();
this.delayedRender = null;
}
},
}),
this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()),
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
@@ -371,7 +374,7 @@ export class EditorStatus implements IStatusbarItem {
private updateState(update: StateDelta): void {
const changed = this.state.update(update);
if (!changed) {
if (!changed.hasChanges()) {
// Nothing really changed
return;
}
@@ -382,7 +385,9 @@ export class EditorStatus implements IStatusbarItem {
this.delayedRender = null;
const toRender = this.toRender;
this.toRender = null;
this._renderNow(toRender);
if (toRender) {
this._renderNow(toRender);
}
});
} else {
this.toRender.combine(changed);
@@ -391,79 +396,71 @@ export class EditorStatus implements IStatusbarItem {
private _renderNow(changed: StateChange): void {
if (changed.tabFocusMode) {
if (this.state.tabFocusMode && this.state.tabFocusMode === true) {
show(this.tabFocusModeElement);
} else {
hide(this.tabFocusModeElement);
}
this.tabFocusModeElement.setVisible(!!this.state.tabFocusMode);
}
if (changed.screenReaderMode) {
if (this.state.screenReaderMode && this.state.screenReaderMode === true) {
show(this.screenRedearModeElement);
} else {
hide(this.screenRedearModeElement);
}
this.screenRedearModeElement.setVisible(!!this.state.screenReaderMode);
}
if (changed.indentation) {
if (this.state.indentation) {
this.indentationElement.textContent = this.state.indentation;
show(this.indentationElement);
this.indentationElement.setVisible(true);
} else {
hide(this.indentationElement);
this.indentationElement.setVisible(false);
}
}
if (changed.selectionStatus) {
if (this.state.selectionStatus && !this.state.screenReaderMode) {
this.selectionElement.textContent = this.state.selectionStatus;
show(this.selectionElement);
this.selectionElement.setVisible(true);
} else {
hide(this.selectionElement);
this.selectionElement.setVisible(false);
}
}
if (changed.encoding) {
if (this.state.encoding) {
this.encodingElement.textContent = this.state.encoding;
show(this.encodingElement);
this.encodingElement.setVisible(true);
} else {
hide(this.encodingElement);
this.encodingElement.setVisible(false);
}
}
if (changed.EOL) {
if (this.state.EOL) {
this.eolElement.textContent = this.state.EOL === '\r\n' ? nlsEOLCRLF : nlsEOLLF;
show(this.eolElement);
this.eolElement.setVisible(true);
} else {
hide(this.eolElement);
this.eolElement.setVisible(false);
}
}
if (changed.mode) {
if (this.state.mode) {
this.modeElement.textContent = this.state.mode;
show(this.modeElement);
this.modeElement.setVisible(true);
} else {
hide(this.modeElement);
this.modeElement.setVisible(false);
}
}
if (changed.metadata) {
if (this.state.metadata) {
this.metadataElement.textContent = this.state.metadata;
show(this.metadataElement);
this.metadataElement.setVisible(true);
} else {
hide(this.metadataElement);
this.metadataElement.setVisible(false);
}
}
}
private getSelectionLabel(info: IEditorSelectionStatus): string {
private getSelectionLabel(info: IEditorSelectionStatus): string | undefined {
if (!info || !info.selections) {
return null;
return undefined;
}
if (info.selections.length === 1) {
@@ -482,7 +479,7 @@ export class EditorStatus implements IStatusbarItem {
return strings.format(nlsMultiSelection, info.selections.length);
}
return null;
return undefined;
}
private onModeClick(): void {
@@ -502,8 +499,7 @@ export class EditorStatus implements IStatusbarItem {
if (!this.screenReaderNotification) {
this.screenReaderNotification = this.notificationService.prompt(
Severity.Info,
// {{SQL CARBON EDIT}}
nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio?"),
nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio? (Certain features like folding, minimap or word wrap are disabled when using a screen reader)"),
[{
label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"),
run: () => {
@@ -548,7 +544,7 @@ export class EditorStatus implements IStatusbarItem {
private updateStatusBar(): void {
const activeControl = this.editorService.activeControl;
const activeCodeEditor = activeControl ? getCodeEditor(activeControl.getControl()) : undefined;
const activeCodeEditor = activeControl ? types.withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
// Update all states
this.onScreenReaderModeChange(activeCodeEditor);
@@ -586,11 +582,13 @@ export class EditorStatus implements IStatusbarItem {
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelContent((e) => {
this.onEOLChange(activeCodeEditor);
let selections = activeCodeEditor.getSelections();
for (const change of e.changes) {
if (selections.some(selection => Range.areIntersecting(selection, change.range))) {
this.onSelectionChange(activeCodeEditor);
break;
const selections = activeCodeEditor.getSelections();
if (selections) {
for (const change of e.changes) {
if (selections.some(selection => Range.areIntersecting(selection, change.range))) {
this.onSelectionChange(activeCodeEditor);
break;
}
}
}
}));
@@ -630,8 +628,8 @@ export class EditorStatus implements IStatusbarItem {
}
}
private onModeChange(editorWidget: ICodeEditor): void {
let info: StateDelta = { mode: null };
private onModeChange(editorWidget: ICodeEditor | undefined): void {
let info: StateDelta = { mode: undefined };
// We only support text based editors
if (editorWidget) {
@@ -639,15 +637,15 @@ export class EditorStatus implements IStatusbarItem {
if (textModel) {
// Compute mode
const modeId = textModel.getLanguageIdentifier().language;
info = { mode: this.modeService.getLanguageName(modeId) };
info = { mode: this.modeService.getLanguageName(modeId) || undefined };
}
}
this.updateState(info);
}
private onIndentationChange(editorWidget: ICodeEditor): void {
const update: StateDelta = { indentation: null };
private onIndentationChange(editorWidget: ICodeEditor | undefined): void {
const update: StateDelta = { indentation: undefined };
if (editorWidget) {
const model = editorWidget.getModel();
@@ -655,7 +653,7 @@ export class EditorStatus implements IStatusbarItem {
const modelOpts = model.getOptions();
update.indentation = (
modelOpts.insertSpaces
? nls.localize('spacesSize', "Spaces: {0}", modelOpts.tabSize)
? nls.localize('spacesSize', "Spaces: {0}", modelOpts.indentSize)
: nls.localize({ key: 'tabSize', comment: ['Tab corresponds to the tab key'] }, "Tab Size: {0}", modelOpts.tabSize)
);
}
@@ -664,8 +662,8 @@ export class EditorStatus implements IStatusbarItem {
this.updateState(update);
}
private onMetadataChange(editor: IBaseEditor): void {
const update: StateDelta = { metadata: null };
private onMetadataChange(editor: IBaseEditor | undefined): void {
const update: StateDelta = { metadata: undefined };
if (editor instanceof BaseBinaryResourceEditor || editor instanceof BinaryResourceDiffEditor) {
update.metadata = editor.getMetadata();
@@ -676,12 +674,12 @@ export class EditorStatus implements IStatusbarItem {
private _promptedScreenReader: boolean = false;
private onScreenReaderModeChange(editorWidget: ICodeEditor): void {
private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void {
let screenReaderMode = false;
// We only support text based editors
if (editorWidget) {
const screenReaderDetected = (browser.getAccessibilitySupport() === AccessibilitySupport.Enabled);
const screenReaderDetected = (this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled);
if (screenReaderDetected) {
const screenReaderConfiguration = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
if (screenReaderConfiguration === 'auto') {
@@ -705,7 +703,7 @@ export class EditorStatus implements IStatusbarItem {
this.updateState({ screenReaderMode: screenReaderMode });
}
private onSelectionChange(editorWidget: ICodeEditor): void {
private onSelectionChange(editorWidget: ICodeEditor | undefined): void {
const info: IEditorSelectionStatus = {};
// We only support text based editors
@@ -719,13 +717,13 @@ export class EditorStatus implements IStatusbarItem {
const textModel = editorWidget.getModel();
if (textModel) {
info.selections.forEach(selection => {
info.charactersSelected += textModel.getValueLengthInRange(selection);
info.charactersSelected! += textModel.getValueLengthInRange(selection);
});
}
// Compute the visible column for one selection. This will properly handle tabs and their configured widths
if (info.selections.length === 1) {
const visibleColumn = editorWidget.getVisibleColumnFromPosition(editorWidget.getPosition());
const visibleColumn = editorWidget.getVisibleColumnFromPosition(editorWidget.getPosition()!);
let selectionClone = info.selections[0].clone(); // do not modify the original position we got from the editor
selectionClone = new Selection(
@@ -742,8 +740,8 @@ export class EditorStatus implements IStatusbarItem {
this.updateState({ selectionStatus: this.getSelectionLabel(info) });
}
private onEOLChange(editorWidget: ICodeEditor): void {
const info: StateDelta = { EOL: null };
private onEOLChange(editorWidget: ICodeEditor | undefined): void {
const info: StateDelta = { EOL: undefined };
if (editorWidget && !editorWidget.getConfiguration().readOnly) {
const codeEditorModel = editorWidget.getModel();
@@ -760,11 +758,11 @@ export class EditorStatus implements IStatusbarItem {
return;
}
const info: StateDelta = { encoding: null };
const info: StateDelta = { encoding: undefined };
// We only support text based editors
if (e && (isCodeEditor(e.getControl()) || isDiffEditor(e.getControl()))) {
const encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(e.input);
const encodingSupport: IEncodingSupport | null = e.input ? toEditorWithEncodingSupport(e.input) : null;
if (encodingSupport) {
const rawEncoding = encodingSupport.getEncoding();
const encodingInfo = SUPPORTED_ENCODINGS[rawEncoding];
@@ -798,11 +796,11 @@ export class EditorStatus implements IStatusbarItem {
private isActiveEditor(control: IBaseEditor): boolean {
const activeControl = this.editorService.activeControl;
return activeControl && activeControl === control;
return !!activeControl && activeControl === control;
}
}
function isWritableCodeEditor(codeEditor: ICodeEditor): boolean {
function isWritableCodeEditor(codeEditor: ICodeEditor | undefined): boolean {
if (!codeEditor) {
return false;
}
@@ -811,7 +809,7 @@ function isWritableCodeEditor(codeEditor: ICodeEditor): boolean {
}
function isWritableBaseEditor(e: IBaseEditor): boolean {
return e && isWritableCodeEditor(getCodeEditor(e.getControl()));
return e && isWritableCodeEditor(getCodeEditor(e.getControl()) || undefined);
}
export class ShowLanguageExtensionsAction extends Action {
@@ -844,7 +842,7 @@ export class ChangeModeAction extends Action {
@IModeService private readonly modeService: IModeService,
@IModelService private readonly modelService: IModelService,
@IEditorService private readonly editorService: IEditorService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@@ -860,19 +858,19 @@ export class ChangeModeAction extends Action {
}
const textModel = activeTextEditorWidget.getModel();
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: true }) : null;
let hasLanguageSupport = !!resource;
if (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
if (resource && resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
hasLanguageSupport = false; // no configuration for untitled resources (e.g. "Untitled-1")
}
// Compute mode
let currentModeId: string;
let currentModeId: string | undefined;
let modeId: string;
if (textModel) {
modeId = textModel.getLanguageIdentifier().language;
currentModeId = this.modeService.getLanguageName(modeId);
currentModeId = this.modeService.getLanguageName(modeId) || undefined;
}
// All languages are valid picks
@@ -886,7 +884,7 @@ export class ChangeModeAction extends Action {
}
// construct a fake resource to be able to show nice icons if any
let fakeResource: uri;
let fakeResource: uri | undefined;
const extensions = this.modeService.getExtensions(lang);
if (extensions && extensions.length) {
fakeResource = uri.file(extensions[0]);
@@ -912,8 +910,8 @@ export class ChangeModeAction extends Action {
let configureModeAssociations: IQuickPickItem;
let configureModeSettings: IQuickPickItem;
let galleryAction: Action;
if (hasLanguageSupport) {
const ext = paths.extname(resource.fsPath) || paths.basename(resource.fsPath);
if (hasLanguageSupport && resource) {
const ext = extname(resource) || basename(resource);
galleryAction = this.instantiationService.createInstance(ShowLanguageExtensionsAction, ext);
if (galleryAction.enabled) {
@@ -947,7 +945,9 @@ export class ChangeModeAction extends Action {
// User decided to permanently configure associations, return right after
if (pick === configureModeAssociations) {
this.configureFileAssociation(resource);
if (resource) {
this.configureFileAssociation(resource);
}
return;
}
@@ -980,10 +980,15 @@ export class ChangeModeAction extends Action {
}
// Find mode
let languageSelection: ILanguageSelection;
let languageSelection: ILanguageSelection | undefined;
if (pick === autoDetectMode) {
// {{SQL CARBON EDIT}} - use activeEditor.input instead of activeEditor
languageSelection = this.modeService.createByFilepathOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true }).fsPath, textModel.getLineContent(1));
if (textModel) {
// {{SQL CARBON EDIT}} - use activeEditor.input instead of activeEditor
const resource = toResource(activeEditor.input, { supportSideBySide: true });
if (resource) {
languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1));
}
}
} else {
languageSelection = this.modeService.createByLanguageName(pick.label);
}
@@ -991,10 +996,9 @@ export class ChangeModeAction extends Action {
// {{SQL CARBON EDIT}}
// Change mode
models.forEach(textModel => {
let self = this;
QueryEditorService.sqlLanguageModeCheck(textModel, languageSelection, activeEditor).then((newTextModel) => {
if (newTextModel) {
self.modelService.setMode(newTextModel, languageSelection);
this.modelService.setMode(newTextModel, languageSelection);
}
});
});
@@ -1002,9 +1006,9 @@ export class ChangeModeAction extends Action {
}
private configureFileAssociation(resource: uri): void {
const extension = paths.extname(resource.fsPath);
const basename = paths.basename(resource.fsPath);
const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(basename);
const extension = extname(resource);
const base = basename(resource);
const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(base);
const languages = this.modeService.getRegisteredLanguageNames();
const picks: IQuickPickItem[] = languages.sort().map((lang, index) => {
@@ -1018,15 +1022,15 @@ export class ChangeModeAction extends Action {
});
setTimeout(() => {
this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).then(language => {
this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || base) }).then(language => {
if (language) {
const fileAssociationsConfig = this.configurationService.inspect(FILES_ASSOCIATIONS_CONFIG);
let associationKey: string;
if (extension && basename[0] !== '.') {
if (extension && base[0] !== '.') {
associationKey = `*${extension}`; // only use "*.ext" if the file path is in the form of <name>.<ext>
} else {
associationKey = basename; // otherwise use the basename (e.g. .gitignore, Dockerfile)
associationKey = base; // otherwise use the basename (e.g. .gitignore, Dockerfile)
}
// If the association is already being made in the workspace, make sure to target workspace settings
@@ -1036,11 +1040,7 @@ export class ChangeModeAction extends Action {
}
// Make sure to write into the value of the target and not the merged value from USER and WORKSPACE config
let currentAssociations = deepClone((target === ConfigurationTarget.WORKSPACE) ? fileAssociationsConfig.workspace : fileAssociationsConfig.user);
if (!currentAssociations) {
currentAssociations = Object.create(null);
}
const currentAssociations = deepClone((target === ConfigurationTarget.WORKSPACE) ? fileAssociationsConfig.workspace : fileAssociationsConfig.user) || Object.create(null);
currentAssociations[associationKey] = language.id;
this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target);
@@ -1089,7 +1089,7 @@ class ChangeIndentationAction extends Action {
return {
id: a.id,
label: a.label,
detail: (language === LANGUAGE_DEFAULT) ? null : a.alias,
detail: Language.isDefaultVariant() ? undefined : a.alias,
run: () => {
activeTextEditorWidget.focus();
a.run();
@@ -1140,7 +1140,7 @@ export class ChangeEOLAction extends Action {
return this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] }).then(eol => {
if (eol) {
const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget);
if (activeCodeEditor && isWritableCodeEditor(activeCodeEditor)) {
if (activeCodeEditor && activeCodeEditor.hasModel() && isWritableCodeEditor(activeCodeEditor)) {
const textModel = activeCodeEditor.getModel();
textModel.pushEOL(eol.eol);
}
@@ -1170,15 +1170,18 @@ export class ChangeEncodingAction extends Action {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
let activeControl = this.editorService.activeControl;
let encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(activeControl.input);
const activeControl = this.editorService.activeControl;
if (!activeControl) {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
const encodingSupport: IEncodingSupport | null = toEditorWithEncodingSupport(activeControl.input);
if (!encodingSupport) {
return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]);
}
let saveWithEncodingPick: IQuickPickItem;
let reopenWithEncodingPick: IQuickPickItem;
if (language === LANGUAGE_DEFAULT) {
if (Language.isDefaultVariant()) {
saveWithEncodingPick = { label: nls.localize('saveWithEncoding', "Save with Encoding") };
reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding") };
} else {
@@ -1200,7 +1203,7 @@ export class ChangeEncodingAction extends Action {
return undefined;
}
const resource = toResource(activeControl.input, { supportSideBySide: true });
const resource = toResource(activeControl!.input, { supportSideBySide: true });
return timeout(50 /* quick open is sensitive to being opened so soon after another */)
.then(() => {
@@ -1213,10 +1216,10 @@ export class ChangeEncodingAction extends Action {
.then((guessedEncoding: string) => {
const isReopenWithEncoding = (action === reopenWithEncodingPick);
const configuredEncoding = this.textResourceConfigurationService.getValue(resource, 'files.encoding');
const configuredEncoding = this.textResourceConfigurationService.getValue(types.withNullAsUndefined(resource), 'files.encoding');
let directMatchIndex: number;
let aliasMatchIndex: number;
let directMatchIndex: number | undefined;
let aliasMatchIndex: number | undefined;
// All encodings are valid picks
const picks: QuickPickInput[] = Object.keys(SUPPORTED_ENCODINGS)
@@ -1258,12 +1261,16 @@ export class ChangeEncodingAction extends Action {
placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1]
}).then(encoding => {
if (encoding) {
activeControl = this.editorService.activeControl;
encodingSupport = toEditorWithEncodingSupport(activeControl.input);
if (encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
}
if (!encoding) {
return;
}
const activeControl = this.editorService.activeControl;
if (!activeControl) {
return;
}
const encodingSupport = toEditorWithEncodingSupport(activeControl.input);
if (typeof encoding.id !== 'undefined' && encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
}
});
});

View File

@@ -14,17 +14,16 @@ import { buttonBackground, buttonForeground, editorBackground, editorForeground,
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { Schemas } from 'vs/base/common/network';
import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
import { extname } from 'vs/base/common/paths';
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { Disposable, dispose } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { isEqual } from 'vs/base/common/resources';
import { IFileService } from 'vs/platform/files/common/files';
export class FloatingClickWidget extends Widget implements IOverlayWidget {
private _onClick: Emitter<void> = this._register(new Emitter<void>());
private readonly _onClick: Emitter<void> = this._register(new Emitter<void>());
get onClick(): Event<void> { return this._onClick.event; }
private _domNode: HTMLElement;
@@ -108,7 +107,8 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
private editor: ICodeEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IWindowService private readonly windowService: IWindowService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IFileService private readonly fileService: IFileService
) {
super();
@@ -139,8 +139,12 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
return false; // we need a model
}
if (model.uri.scheme !== Schemas.file || extname(model.uri.fsPath) !== `.${WORKSPACE_EXTENSION}`) {
return false; // we need a local workspace file
if (!hasWorkspaceFileExtension(model.uri.fsPath)) {
return false; // we need a workspace file
}
if (!this.fileService.canHandleResource(model.uri)) {
return false; // needs to be backed by a file service
}
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
@@ -159,7 +163,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
this._register(this.openWorkspaceButton.onClick(() => {
const model = this.editor.getModel();
if (model) {
this.windowService.openWindow([model.uri]);
this.windowService.openWindow([{ uri: model.uri, typeHint: 'file' }]);
}
}));

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.2;}
.st1{fill:#646464;}
</style>
<g class="st0">
<path class="st1" d="M12.7,19.2L12.1,19H7l-1.4,3.8L3.1,16L2,19H0v1.5C0,22.4,3.1,24,7,24c2.3,0,4.3-0.6,5.6-1.4l-0.1-0.3l-0.4-0.9
l-0.4-0.9l0.9-0.4l0.3-0.1c0-0.1,0-0.2,0-0.2c0-0.1,0-0.2,0-0.2L12.7,19.2z"/>
<path class="st1" d="M12.5,11c-1.3,0.8-3.3,1.3-5.5,1.3c-3.9,0-7-1.6-7-3.5V18h1.3l1.8-5l2.5,6.8L6.3,18h5.8l0-0.1l0.4-0.9l0.4-0.9
l0.9,0.4l0.2,0.1V11L12.5,11L12.5,11z"/>
<g>
<path class="st1" d="M19,4c0.4,0,0.8,0.1,1.2,0.2c0.4,0.2,0.7,0.4,1,0.6c0.3,0.3,0.5,0.6,0.6,0.9C21.9,6.2,22,6.6,22,7
c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.6C19.8,9.9,19.4,10,19,10h-9c-0.6,0-1.1-0.1-1.6-0.3
C8,9.5,7.5,9.2,7.2,8.8C6.8,8.5,6.5,8,6.3,7.6C6.1,7.1,6,6.6,6,6c0-0.6,0.1-1.1,0.3-1.6C6.5,4,6.8,3.5,7.2,3.2
c0.4-0.4,0.8-0.6,1.3-0.9C8.9,2.1,9.4,2,10,2c0.2,0,0.5,0,0.7,0.1c0.2-0.3,0.4-0.6,0.7-0.9c0.3-0.3,0.6-0.5,0.9-0.7
c0.3-0.2,0.7-0.3,1-0.4C13.7,0.1,14.1,0,14.5,0c0.6,0,1.1,0.1,1.6,0.3c0.5,0.2,1,0.5,1.4,0.8c0.4,0.4,0.7,0.8,1,1.3
C18.7,2.9,18.9,3.4,19,4z"/>
</g>
<path class="st1" d="M21,19.5c0,0.3,0,0.6-0.1,0.9l1.1,0.4l-0.4,0.9l-1.1-0.4c-0.3,0.5-0.7,0.9-1.2,1.2l0.4,1.1L18.8,24l-0.4-1.1
C18.1,23,17.8,23,17.5,23s-0.6,0-0.9-0.1L16.2,24l-0.9-0.4l0.4-1.1c-0.5-0.3-0.9-0.7-1.2-1.2l-1.1,0.4L13,20.8l1.1-0.4
C14,20.1,14,19.8,14,19.5s0-0.6,0.1-0.9L13,18.2l0.4-0.9l1.1,0.4c0.3-0.5,0.7-0.9,1.2-1.2l-0.4-1.1l0.9-0.4l0.4,1.1
c0.3-0.1,0.6-0.1,0.9-0.1s0.6,0,0.9,0.1l0.4-1.1l0.9,0.4l-0.4,1.1c0.5,0.3,0.9,0.7,1.2,1.2l1.1-0.4l0.4,0.9l-1.1,0.4
C21,19,21,19.2,21,19.5z M17.5,22c0.3,0,0.7-0.1,1-0.2c0.3-0.1,0.6-0.3,0.8-0.5s0.4-0.5,0.5-0.8c0.1-0.3,0.2-0.6,0.2-1
s-0.1-0.7-0.2-1c-0.1-0.3-0.3-0.6-0.5-0.8s-0.5-0.4-0.8-0.5c-0.3-0.1-0.6-0.2-1-0.2c-0.3,0-0.7,0.1-1,0.2c-0.3,0.1-0.6,0.3-0.8,0.5
c-0.2,0.2-0.4,0.5-0.5,0.8c-0.1,0.3-0.2,0.6-0.2,1c0,0.3,0.1,0.7,0.2,1c0.1,0.3,0.3,0.6,0.5,0.8s0.5,0.4,0.8,0.5
C16.8,22,17.1,22,17.5,22z"/>
<path class="st1" d="M9.9,11c-0.6,0-1.3-0.1-1.8-0.4C7.4,10.4,6.9,10,6.5,9.5C6,9.1,5.7,8.6,5.4,8C5.2,7.5,5.1,7,5,6.5
C2.5,6.9,1,8,1,8.8c0,1,2.3,2.5,6,2.5C8,11.3,9,11.2,9.9,11z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g>
<path style="fill:#646464;" d="M12.654,19.244l-0.572-0.236H6.958l-1.374,3.777l-2.48-6.824l-1.108,3.047H0v1.488
c0,1.932,3.131,3.496,6.997,3.496c2.304,0,4.348-0.555,5.621-1.416l-0.139-0.346l-0.377-0.93l-0.371-0.924l0.922-0.375l0.348-0.143
c-0.005-0.08-0.007-0.158-0.007-0.236c0-0.08,0.002-0.16,0.007-0.238L12.654,19.244z"/>
<path style="fill:#646464;" d="M12.491,10.999c-1.28,0.812-3.265,1.334-5.494,1.334C3.132,12.333,0,10.766,0,8.833v9.175h1.296
l1.808-4.973l2.48,6.824l0.675-1.852h5.822l0.024-0.064l0.376-0.93l0.377-0.934l0.931,0.383l0.204,0.086v-5.55H12.491z"/>
<g>
<path style="fill:#646464;" d="M18.968,4.002c0.419,0,0.809,0.078,1.175,0.234c0.368,0.156,0.688,0.369,0.962,0.636
c0.274,0.27,0.49,0.586,0.647,0.949C21.91,6.188,21.99,6.578,21.99,7c0,0.418-0.078,0.806-0.234,1.168
c-0.155,0.362-0.371,0.68-0.645,0.952c-0.273,0.273-0.593,0.488-0.955,0.645c-0.365,0.158-0.755,0.234-1.164,0.234H9.995
c-0.551,0-1.069-0.104-1.558-0.312C7.951,9.48,7.526,9.195,7.164,8.833c-0.36-0.36-0.646-0.786-0.854-1.272
c-0.209-0.487-0.312-1.008-0.312-1.56c0-0.551,0.104-1.069,0.312-1.559c0.206-0.484,0.492-0.91,0.854-1.272
C7.526,2.809,7.95,2.523,8.437,2.314c0.489-0.208,1.008-0.312,1.559-0.312c0.237,0,0.48,0.023,0.727,0.071
c0.209-0.322,0.448-0.613,0.722-0.871c0.276-0.258,0.572-0.474,0.897-0.651c0.321-0.176,0.666-0.312,1.028-0.407
c0.365-0.093,0.738-0.14,1.125-0.14c0.577,0,1.122,0.101,1.636,0.305c0.513,0.203,0.968,0.483,1.362,0.839
c0.396,0.357,0.723,0.781,0.98,1.271C18.729,2.906,18.893,3.437,18.968,4.002z"/>
</g>
<path style="fill:#646464;" d="M20.991,19.537c0,0.287-0.037,0.576-0.109,0.867l1.068,0.436l-0.374,0.93l-1.079-0.445
c-0.308,0.512-0.712,0.918-1.216,1.221l0.445,1.066l-0.93,0.385l-0.439-1.07c-0.291,0.074-0.58,0.109-0.867,0.109
S16.915,23,16.623,22.926l-0.437,1.07l-0.929-0.385l0.444-1.066c-0.511-0.303-0.918-0.709-1.219-1.221l-1.078,0.445l-0.373-0.93
l1.069-0.436c-0.072-0.291-0.108-0.58-0.108-0.867s0.036-0.574,0.111-0.867l-1.071-0.438l0.374-0.93l1.079,0.443
c0.3-0.504,0.706-0.91,1.218-1.217l-0.445-1.078l0.931-0.375l0.437,1.068c0.29-0.072,0.58-0.107,0.866-0.107
s0.577,0.035,0.867,0.107l0.438-1.068l0.93,0.375l-0.444,1.078c0.503,0.307,0.908,0.713,1.217,1.217l1.078-0.443l0.374,0.93
l-1.068,0.438C20.954,18.963,20.991,19.25,20.991,19.537z M17.492,22.035c0.344,0,0.666-0.064,0.969-0.195
c0.302-0.129,0.566-0.309,0.792-0.535s0.407-0.492,0.539-0.797c0.133-0.303,0.199-0.627,0.199-0.971s-0.066-0.666-0.199-0.969
c-0.132-0.303-0.313-0.566-0.539-0.793s-0.49-0.406-0.792-0.539c-0.303-0.133-0.625-0.199-0.969-0.199
c-0.343,0-0.667,0.066-0.973,0.199c-0.304,0.133-0.568,0.312-0.797,0.539c-0.228,0.227-0.404,0.49-0.534,0.793
c-0.131,0.303-0.195,0.625-0.195,0.969c0,0.342,0.064,0.668,0.195,0.971c0.13,0.305,0.309,0.57,0.534,0.797
s0.493,0.406,0.797,0.535C16.825,21.971,17.149,22.035,17.492,22.035z"/>
<path style="fill:#646464;" d="M9.886,10.988c-0.644-0.014-1.265-0.136-1.839-0.383C7.443,10.351,6.91,9.992,6.458,9.54
C6.01,9.092,5.65,8.558,5.391,7.954C5.193,7.492,5.094,6.997,5.045,6.487C2.525,6.88,1,7.981,1,8.833c0,1.044,2.281,2.5,5.997,2.5
C8.014,11.333,9.004,11.209,9.886,10.988z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g style="opacity:0.2;">
<path style="fill:#646464;" d="M12.654,19.244l-0.572-0.236H6.958l-1.374,3.777l-2.48-6.824l-1.108,3.047H0v1.488
c0,1.932,3.131,3.496,6.997,3.496c2.304,0,4.348-0.555,5.621-1.416l-0.139-0.346l-0.377-0.93l-0.371-0.924l0.922-0.375l0.348-0.143
c-0.005-0.08-0.007-0.158-0.007-0.236c0-0.08,0.002-0.16,0.007-0.238L12.654,19.244z"/>
<path style="fill:#646464;" d="M12.491,10.999c-1.28,0.812-3.265,1.334-5.494,1.334C3.132,12.333,0,10.766,0,8.833v9.174h1.296
l1.808-4.973l2.48,6.824l0.675-1.852h5.822l0.024-0.064l0.376-0.93l0.377-0.934l0.931,0.383l0.204,0.086v-5.55H12.491z"/>
<g>
<path style="fill:#646464;" d="M18.968,4.002c0.419,0,0.809,0.078,1.175,0.234c0.368,0.156,0.688,0.369,0.962,0.636
c0.274,0.27,0.49,0.586,0.647,0.949C21.91,6.188,21.99,6.579,21.99,7c0,0.418-0.078,0.806-0.234,1.168
c-0.155,0.363-0.371,0.68-0.645,0.953s-0.593,0.488-0.955,0.645c-0.365,0.158-0.755,0.234-1.164,0.234H9.995
c-0.551,0-1.069-0.103-1.558-0.312C7.951,9.48,7.526,9.195,7.164,8.833C6.804,8.473,6.518,8.047,6.31,7.561
c-0.209-0.488-0.312-1.008-0.312-1.56c0-0.551,0.104-1.069,0.312-1.559c0.206-0.484,0.492-0.91,0.854-1.272
C7.526,2.809,7.95,2.524,8.437,2.315c0.489-0.208,1.008-0.312,1.559-0.312c0.237,0,0.48,0.023,0.727,0.071
c0.209-0.322,0.448-0.613,0.722-0.871c0.276-0.258,0.572-0.474,0.897-0.652c0.321-0.176,0.666-0.312,1.028-0.407
c0.365-0.093,0.738-0.14,1.125-0.14c0.577,0,1.122,0.1,1.636,0.304c0.513,0.203,0.968,0.483,1.362,0.839
c0.396,0.357,0.723,0.782,0.98,1.271C18.729,2.907,18.893,3.437,18.968,4.002z"/>
</g>
<path style="fill:#646464;" d="M20.991,19.537c0,0.287-0.037,0.576-0.109,0.867l1.068,0.436l-0.374,0.93l-1.079-0.445
c-0.308,0.512-0.712,0.918-1.216,1.221l0.445,1.066l-0.93,0.385l-0.439-1.07c-0.291,0.074-0.58,0.109-0.867,0.109
S16.915,23,16.623,22.926l-0.437,1.07l-0.929-0.385l0.444-1.066c-0.511-0.303-0.918-0.709-1.219-1.221l-1.078,0.445l-0.373-0.93
l1.069-0.436c-0.072-0.291-0.108-0.58-0.108-0.867s0.036-0.574,0.111-0.867l-1.071-0.438l0.374-0.93l1.079,0.443
c0.3-0.504,0.706-0.91,1.218-1.217l-0.445-1.078l0.931-0.375l0.437,1.068c0.29-0.072,0.58-0.107,0.866-0.107
s0.577,0.035,0.867,0.107l0.438-1.068l0.93,0.375l-0.444,1.078c0.503,0.307,0.908,0.713,1.217,1.217l1.078-0.443l0.374,0.93
l-1.068,0.438C20.954,18.963,20.991,19.25,20.991,19.537z M17.492,22.035c0.344,0,0.666-0.064,0.969-0.195
c0.302-0.129,0.566-0.309,0.792-0.535s0.407-0.492,0.539-0.797c0.133-0.303,0.199-0.627,0.199-0.971s-0.066-0.666-0.199-0.969
c-0.132-0.303-0.313-0.566-0.539-0.793s-0.49-0.406-0.792-0.539c-0.303-0.133-0.625-0.199-0.969-0.199
c-0.343,0-0.667,0.066-0.973,0.199c-0.304,0.133-0.568,0.312-0.797,0.539c-0.228,0.227-0.404,0.49-0.534,0.793
c-0.131,0.303-0.195,0.625-0.195,0.969c0,0.342,0.064,0.668,0.195,0.971c0.13,0.305,0.309,0.57,0.534,0.797
s0.493,0.406,0.797,0.535C16.825,21.971,17.149,22.035,17.492,22.035z"/>
<path style="fill:#646464;" d="M9.886,10.988c-0.644-0.014-1.265-0.135-1.839-0.382C7.443,10.351,6.91,9.992,6.458,9.541
C6.01,9.092,5.65,8.558,5.391,7.954C5.193,7.493,5.094,6.997,5.045,6.488C2.525,6.88,1,7.982,1,8.833c0,1.044,2.281,2.5,5.997,2.5
C8.014,11.333,9.004,11.209,9.886,10.988z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -54,15 +54,14 @@
background-image: none;
}
/* {{SQL CARBON EDIT}} */
.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
content: '/';
.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
content: '\\';
}
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before,
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before,
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before,
.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before {
.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before {
/* workspace folder, item following workspace folder, or relative path -> hide first seperator */
display: none;
}

View File

@@ -4,19 +4,20 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/notabstitlecontrol';
import { toResource, Verbosity, IEditorInput } from 'vs/workbench/common/editor';
import { toResource, Verbosity, IEditorInput, IEditorPartOptions } from 'vs/workbench/common/editor';
import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl';
import { ResourceLabel, IResourceLabel } from 'vs/workbench/browser/labels';
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom';
import { IEditorPartOptions, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
import { EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
import { IAction } from 'vs/base/common/actions';
import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
import { Color } from 'vs/base/common/color';
import { withNullAsUndefined } from 'vs/base/common/types';
interface IRenderedEditorLabel {
editor: IEditorInput;
editor?: IEditorInput;
pinned: boolean;
}
@@ -72,8 +73,16 @@ export class NoTabsTitleControl extends TitleControl {
this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
// Context Menu
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => {
if (this.group.activeEditor) {
this.onContextMenu(this.group.activeEditor, e, this.titleContainer);
}
}));
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => {
if (this.group.activeEditor) {
this.onContextMenu(this.group.activeEditor, e, this.titleContainer);
}
}));
}
private onTitleLabelClick(e: MouseEvent): void {
@@ -95,7 +104,9 @@ export class NoTabsTitleControl extends TitleControl {
if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) {
EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */);
this.group.closeEditor(this.group.activeEditor);
if (this.group.activeEditor) {
this.group.closeEditor(this.group.activeEditor);
}
}
}
@@ -167,7 +178,7 @@ export class NoTabsTitleControl extends TitleControl {
if (
!this.activeLabel.editor && this.group.activeEditor || // active editor changed from null => editor
this.activeLabel.editor && !this.group.activeEditor || // active editor changed from editor => null
!this.group.isActive(this.activeLabel.editor) // active editor changed from editorA => editorB
(!this.activeLabel.editor || !this.group.isActive(this.activeLabel.editor)) // active editor changed from editorA => editorB
) {
fn();
@@ -195,9 +206,9 @@ export class NoTabsTitleControl extends TitleControl {
}
private redraw(): void {
const editor = this.group.activeEditor;
const editor = withNullAsUndefined(this.group.activeEditor);
const isEditorPinned = this.group.isPinned(this.group.activeEditor);
const isEditorPinned = this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false;
const isGroupActive = this.accessor.activeGroup === this.group;
this.activeLabel = { editor, pinned: isEditorPinned };
@@ -244,7 +255,7 @@ export class NoTabsTitleControl extends TitleControl {
title = ''; // dont repeat what is already shown
}
this.editorLabel.setResource({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
this.editorLabel.setResource({ name, description, resource: resource || undefined }, { title: typeof title === 'string' ? title : undefined, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
if (isGroupActive) {
this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
} else {
@@ -256,7 +267,7 @@ export class NoTabsTitleControl extends TitleControl {
}
}
private getVerbosity(style: string): Verbosity {
private getVerbosity(style: string | undefined): Verbosity {
switch (style) {
case 'short': return Verbosity.SHORT;
case 'long': return Verbosity.LONG;

View File

@@ -27,7 +27,7 @@ export interface IResourceDescriptor {
readonly resource: URI;
readonly name: string;
readonly size: number;
readonly etag: string;
readonly etag?: string;
readonly mime: string;
}
@@ -236,7 +236,7 @@ export class ZoomStatusbarItem extends Themable implements IStatusbarItem {
static instance: ZoomStatusbarItem;
showTimeout: any;
private showTimeout: any;
private statusBarItem: HTMLElement;
private onSelectScale?: (scale: Scale) => void;

View File

@@ -13,7 +13,7 @@ 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';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { SplitView, Sizing, Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { Event, Relay, Emitter } from 'vs/base/common/event';
import { IStorageService } from 'vs/platform/storage/common/storage';

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/tabstitlecontrol';
import { isMacintosh } from 'vs/base/common/platform';
import { shorten } from 'vs/base/common/labels';
import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner } from 'vs/workbench/common/editor';
import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner, IEditorPartOptions } 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';
@@ -32,25 +32,22 @@ import { ResourcesDropHandler, fillResourceDataTransfers, DraggedEditorIdentifie
import { Color } from 'vs/base/common/color';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { MergeGroupMode, IMergeGroupOptions } from 'vs/workbench/services/group/common/editorGroupsService';
import { MergeGroupMode, IMergeGroupOptions } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { addClass, addDisposableListener, hasClass, EventType, EventHelper, removeClass, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode } from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { IFileService } from 'vs/platform/files/common/files';
import { withNullAsUndefined } from 'vs/base/common/types';
// {{SQL CARBON EDIT}} -- Display the editor's tab color
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { GlobalNewUntitledFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import * as QueryConstants from 'sql/parts/query/common/constants';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/fileActions';
// {{SQL CARBON EDIT}} -- End
interface IEditorInputLabel {
@@ -74,7 +71,7 @@ export class TabsTitleControl extends TitleControl {
private tabDisposeables: IDisposable[] = [];
private dimension: Dimension;
private layoutScheduled: IDisposable;
private layoutScheduled?: IDisposable;
private blockRevealActiveTab: boolean;
constructor(
@@ -95,11 +92,7 @@ export class TabsTitleControl extends TitleControl {
@IConfigurationService configurationService: IConfigurationService,
@IFileService fileService: IFileService,
// {{SQL CARBON EDIT}} -- Display the editor's tab color
@IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService,
@ICommandService private commandService: ICommandService,
@IConnectionManagementService private connectionService: IConnectionManagementService,
@IQueryEditorService private queryEditorService: IQueryEditorService,
@IObjectExplorerService private objectExplorerService: IObjectExplorerService,
// {{SQL CARBON EDIT}} -- End
) {
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService, fileService);
@@ -222,7 +215,7 @@ export class TabsTitleControl extends TitleControl {
// Return if transfer is unsupported
if (!this.isSupportedDropTransfer(e)) {
e.dataTransfer.dropEffect = 'none';
e.dataTransfer!.dropEffect = 'none';
return;
}
@@ -231,9 +224,9 @@ export class TabsTitleControl extends TitleControl {
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
isLocalDragAndDrop = true;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
if (this.group.id === localDraggedEditor.groupId && this.group.getIndexOfEditor(localDraggedEditor.editor) === this.group.count - 1) {
e.dataTransfer.dropEffect = 'none';
e.dataTransfer!.dropEffect = 'none';
return;
}
}
@@ -241,7 +234,7 @@ export class TabsTitleControl extends TitleControl {
// Update the dropEffect to "copy" if there is no local data to be dragged because
// in that case we can only copy the data into and not move it from its source
if (!isLocalDragAndDrop) {
e.dataTransfer.dropEffect = 'copy';
e.dataTransfer!.dropEffect = 'copy';
}
this.updateDropFeedback(this.tabsContainer, true);
@@ -317,7 +310,7 @@ export class TabsTitleControl extends TitleControl {
(this.tabsContainer.lastChild as HTMLElement).remove();
// Remove associated tab label and widget
this.tabDisposeables.pop().dispose();
this.tabDisposeables.pop()!.dispose();
}
// A removal of a label requires to recompute all labels
@@ -488,7 +481,10 @@ export class TabsTitleControl extends TitleControl {
}
// Open tabs editor
this.group.openEditor(this.group.getEditor(index));
const input = this.group.getEditor(index);
if (input) {
this.group.openEditor(input);
}
return undefined;
};
@@ -496,7 +492,10 @@ export class TabsTitleControl extends TitleControl {
const showContextMenu = (e: Event) => {
EventHelper.stop(e);
this.onContextMenu(this.group.getEditor(index), e, tab);
const input = this.group.getEditor(index);
if (input) {
this.onContextMenu(input, e, tab);
}
};
// Open on Click / Touch
@@ -543,7 +542,10 @@ export class TabsTitleControl extends TitleControl {
// Run action on Enter/Space
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
handled = true;
this.group.openEditor(this.group.getEditor(index));
const input = this.group.getEditor(index);
if (input) {
this.group.openEditor(input);
}
}
// Navigate in editors
@@ -581,22 +583,29 @@ export class TabsTitleControl extends TitleControl {
disposables.push(addDisposableListener(tab, EventType.DBLCLICK, (e: MouseEvent) => {
EventHelper.stop(e);
this.group.pinEditor(this.group.getEditor(index));
this.group.pinEditor(this.group.getEditor(index) || undefined);
}));
// Context menu
disposables.push(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => {
EventHelper.stop(e, true);
this.onContextMenu(this.group.getEditor(index), e, tab);
const input = this.group.getEditor(index);
if (input) {
this.onContextMenu(input, e, tab);
}
}, true /* use capture to fix https://github.com/Microsoft/vscode/issues/19145 */));
// Drag support
disposables.push(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => {
const editor = this.group.getEditor(index);
if (!editor) {
return;
}
this.editorTransfer.setData([new DraggedEditorIdentifier({ editor, groupId: this.group.id })], DraggedEditorIdentifier.prototype);
e.dataTransfer.effectAllowed = 'copyMove';
e.dataTransfer!.effectAllowed = 'copyMove';
// Apply some datatransfer types to allow for dragging the element outside of the application
const resource = toResource(editor, { supportSideBySide: true });
@@ -618,7 +627,7 @@ export class TabsTitleControl extends TitleControl {
// Return if transfer is unsupported
if (!this.isSupportedDropTransfer(e)) {
e.dataTransfer.dropEffect = 'none';
e.dataTransfer!.dropEffect = 'none';
return;
}
@@ -627,9 +636,9 @@ export class TabsTitleControl extends TitleControl {
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
isLocalDragAndDrop = true;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) {
e.dataTransfer.dropEffect = 'none';
e.dataTransfer!.dropEffect = 'none';
return;
}
}
@@ -637,7 +646,7 @@ export class TabsTitleControl extends TitleControl {
// Update the dropEffect to "copy" if there is no local data to be dragged because
// in that case we can only copy the data into and not move it from its source
if (!isLocalDragAndDrop) {
e.dataTransfer.dropEffect = 'copy';
e.dataTransfer!.dropEffect = 'copy';
}
this.updateDropFeedback(tab, true, index);
@@ -668,7 +677,7 @@ export class TabsTitleControl extends TitleControl {
private isSupportedDropTransfer(e: DragEvent): boolean {
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0];
const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0];
if (group.identifier === this.group.id) {
return false; // groups cannot be dropped on title area it originates from
}
@@ -680,7 +689,7 @@ export class TabsTitleControl extends TitleControl {
return true; // (local) editors can always be dropped
}
if (e.dataTransfer.types.length > 0) {
if (e.dataTransfer && e.dataTransfer.types.length > 0) {
return true; // optimistically allow external data (// see https://github.com/Microsoft/vscode/issues/25789)
}
@@ -689,7 +698,8 @@ export class TabsTitleControl extends TitleControl {
private updateDropFeedback(element: HTMLElement, isDND: boolean, index?: number): void {
const isTab = (typeof index === 'number');
const isActiveTab = isTab && this.group.isActive(this.group.getEditor(index));
const editor = typeof index === 'number' ? this.group.getEditor(index) : null;
const isActiveTab = isTab && !!editor && this.group.isActive(editor);
// Background
const noDNDBackgroundColor = isTab ? this.getColor(isActiveTab ? TAB_ACTIVE_BACKGROUND : TAB_INACTIVE_BACKGROUND) : null;
@@ -728,9 +738,9 @@ export class TabsTitleControl extends TitleControl {
// Build labels and descriptions for each editor
const labels = this.group.editors.map(editor => ({
editor,
name: editor.getName(),
description: editor.getDescription(verbosity),
title: editor.getTitle(Verbosity.LONG)
name: editor.getName()!,
description: withNullAsUndefined(editor.getDescription(verbosity)),
title: withNullAsUndefined(editor.getTitle(Verbosity.LONG))
}));
// Shorten labels as needed
@@ -782,7 +792,7 @@ export class TabsTitleControl extends TitleControl {
if (useLongDescriptions) {
mapDescriptionToDuplicates.clear();
duplicateTitles.forEach(label => {
label.description = label.editor.getDescription(Verbosity.LONG);
label.description = withNullAsUndefined(label.editor.getDescription(Verbosity.LONG));
getOrSet(mapDescriptionToDuplicates, label.description, []).push(label);
});
}
@@ -793,7 +803,7 @@ export class TabsTitleControl extends TitleControl {
// Remove description if all descriptions are identical
if (descriptions.length === 1) {
for (const label of mapDescriptionToDuplicates.get(descriptions[0])) {
for (const label of mapDescriptionToDuplicates.get(descriptions[0]) || []) {
label.description = '';
}
@@ -803,14 +813,14 @@ export class TabsTitleControl extends TitleControl {
// Shorten descriptions
const shortenedDescriptions = shorten(descriptions);
descriptions.forEach((description, i) => {
for (const label of mapDescriptionToDuplicates.get(description)) {
for (const label of mapDescriptionToDuplicates.get(description) || []) {
label.description = shortenedDescriptions[i];
}
});
});
}
private getLabelConfigFlags(value: string) {
private getLabelConfigFlags(value: string | undefined) {
switch (value) {
case 'short':
return { verbosity: Verbosity.SHORT, shortenDuplicates: false };
@@ -891,7 +901,7 @@ export class TabsTitleControl extends TitleControl {
tabContainer.title = title;
// Label
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) || undefined }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
// {{SQL CARBON EDIT}} -- Display the editor's tab color
const isTabActive = this.group.isActive(editor);
@@ -961,7 +971,7 @@ export class TabsTitleControl extends TitleControl {
// Highlight modified tabs with a border if configured
if (this.accessor.partOptions.highlightModifiedTabs) {
let modifiedBorderColor: string;
let modifiedBorderColor: string | null;
if (isGroupActive && isTabActive) {
modifiedBorderColor = this.getColor(TAB_ACTIVE_MODIFIED_BORDER);
} else if (isGroupActive && !isTabActive) {
@@ -998,7 +1008,7 @@ export class TabsTitleControl extends TitleControl {
layout(dimension: Dimension): void {
this.dimension = dimension;
const activeTab = this.getTab(this.group.activeEditor);
const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined;
if (!activeTab || !this.dimension) {
return;
}
@@ -1015,7 +1025,7 @@ export class TabsTitleControl extends TitleControl {
}
private doLayout(dimension: Dimension): void {
const activeTab = this.getTab(this.group.activeEditor);
const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined;
if (!activeTab) {
return;
}
@@ -1050,25 +1060,25 @@ export class TabsTitleControl extends TitleControl {
// Reveal the active one
const containerScrollPosX = this.tabsScrollbar.getScrollPosition().scrollLeft;
const activeTabFits = activeTabWidth <= visibleContainerWidth;
const activeTabFits = activeTabWidth! <= visibleContainerWidth;
// Tab is overflowing to the right: Scroll minimally until the element is fully visible to the right
// Note: only try to do this if we actually have enough width to give to show the tab fully!
if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX + activeTabWidth) {
if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX! + activeTabWidth!) {
this.tabsScrollbar.setScrollPosition({
scrollLeft: containerScrollPosX + ((activeTabPosX + activeTabWidth) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */)
scrollLeft: containerScrollPosX + ((activeTabPosX! + activeTabWidth!) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */)
});
}
// Tab is overlflowng to the left or does not fit: Scroll it into view to the left
else if (containerScrollPosX > activeTabPosX || !activeTabFits) {
else if (containerScrollPosX > activeTabPosX! || !activeTabFits) {
this.tabsScrollbar.setScrollPosition({
scrollLeft: activeTabPosX
scrollLeft: activeTabPosX!
});
}
}
private getTab(editor: IEditorInput): HTMLElement {
private getTab(editor: IEditorInput): HTMLElement | undefined {
const editorIndex = this.group.getIndexOfEditor(editor);
if (editorIndex >= 0) {
return this.tabsContainer.children[editorIndex] as HTMLElement;
@@ -1106,17 +1116,20 @@ export class TabsTitleControl extends TitleControl {
// Local Editor DND
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
// Move editor to target position and index
if (this.isMoveOperation(e, draggedEditor.groupId)) {
sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex });
}
if (sourceGroup) {
// Copy editor to target position and index
else {
sourceGroup.copyEditor(draggedEditor.editor, this.group, { index: targetIndex });
// Move editor to target position and index
if (this.isMoveOperation(e, draggedEditor.groupId)) {
sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex });
}
// Copy editor to target position and index
else {
sourceGroup.copyEditor(draggedEditor.editor, this.group, { index: targetIndex });
}
}
this.group.focus();
@@ -1125,15 +1138,17 @@ export class TabsTitleControl extends TitleControl {
// Local Editor Group DND
else if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier);
const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier);
const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex };
if (!this.isMoveOperation(e, sourceGroup.id)) {
mergeGroupOptions.mode = MergeGroupMode.COPY_EDITORS;
if (sourceGroup) {
const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex };
if (!this.isMoveOperation(e, sourceGroup.id)) {
mergeGroupOptions.mode = MergeGroupMode.COPY_EDITORS;
}
this.accessor.mergeGroup(sourceGroup, this.group, mergeGroupOptions);
}
this.accessor.mergeGroup(sourceGroup, this.group, mergeGroupOptions);
this.group.focus();
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
}
@@ -1154,7 +1169,7 @@ export class TabsTitleControl extends TitleControl {
// {{SQL CARBON EDIT}} -- Display the editor's tab color
private setEditorTabColor(editor: IEditorInput, tabContainer: HTMLElement, isTabActive: boolean) {
let sqlEditor = editor as any;
let tabColorMode = WorkbenchUtils.getSqlConfigValue<string>(this.workspaceConfigurationService, 'tabColorMode');
let tabColorMode = WorkbenchUtils.getSqlConfigValue<string>(this.configurationService, 'tabColorMode');
if (tabColorMode === QueryConstants.tabColorModeOff || (tabColorMode !== QueryConstants.tabColorModeBorder && tabColorMode !== QueryConstants.tabColorModeFill)
|| this.themeService.getTheme().type === HIGH_CONTRAST || !sqlEditor.tabColor) {
tabContainer.style.borderTopColor = '';
@@ -1176,7 +1191,8 @@ export class TabsTitleControl extends TitleControl {
dispose(): void {
super.dispose();
this.layoutScheduled = dispose(this.layoutScheduled);
dispose(this.layoutScheduled);
this.layoutScheduled = undefined;
}
}
@@ -1206,6 +1222,16 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
`);
}
// High Contrast Border Color for Editor Actions
const contrastBorderColor = theme.getColor(contrastBorder);
if (contrastBorder) {
collector.addRule(`
.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions {
outline: 1px solid ${contrastBorderColor}
}
`);
}
// Hover Background
const tabHoverBackground = theme.getColor(TAB_HOVER_BACKGROUND);
if (tabHoverBackground) {
@@ -1251,12 +1277,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const editorGroupHeaderTabsBackground = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND);
const editorDragAndDropBackground = theme.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
let adjustedTabBackground: Color;
let adjustedTabBackground: Color | undefined;
if (editorGroupHeaderTabsBackground && editorBackgroundColor) {
adjustedTabBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorBackgroundColor, workbenchBackground);
}
let adjustedTabDragBackground: Color;
let adjustedTabDragBackground: Color | undefined;
if (editorGroupHeaderTabsBackground && editorBackgroundColor && editorDragAndDropBackground && editorBackgroundColor) {
adjustedTabDragBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorDragAndDropBackground, editorBackgroundColor, workbenchBackground);
}

View File

@@ -27,7 +27,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor';
@@ -61,7 +61,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted
}
getTitle(): string {
getTitle(): string | null {
if (this.input) {
return this.input.getName();
}
@@ -120,6 +120,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
// Readonly flag
diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() });
return undefined;
}, error => {
// In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff.
@@ -262,7 +263,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
return super.loadTextEditorViewState(resource) as IDiffEditorViewState; // overridden for text diff editor support
}
private saveTextDiffEditorViewState(input: EditorInput): void {
private saveTextDiffEditorViewState(input: EditorInput | null): void {
if (!(input instanceof DiffEditorInput)) {
return; // only supported for diff editor inputs
}
@@ -288,11 +289,11 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
}
}
protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState {
protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState | null {
return this.retrieveTextDiffEditorViewState(resource); // overridden for text diff editor support
}
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState {
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState | null {
const control = this.getControl();
const model = control.getModel();
if (!model || !model.modified || !model.original) {
@@ -311,9 +312,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
return control.saveViewState();
}
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI {
let original: URI;
let modified: URI;
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI | null {
let original: URI | null;
let modified: URI | null;
if (modelOrInput instanceof DiffEditorInput) {
original = modelOrInput.originalInput.getResource();

View File

@@ -20,7 +20,7 @@ import { ITextFileService, SaveReason, AutoSaveMode } from 'vs/workbench/service
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { isDiffEditor, isCodeEditor, ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWindowService } from 'vs/platform/windows/common/windows';

View File

@@ -20,7 +20,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { Event } from 'vs/base/common/event';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWindowService } from 'vs/platform/windows/common/windows';
@@ -46,7 +46,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, windowService);
}
getTitle(): string {
getTitle(): string | null {
if (this.input) {
return this.input.getName();
}
@@ -168,7 +168,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
super.saveState();
}
private saveTextResourceEditorViewState(input: EditorInput): void {
private saveTextResourceEditorViewState(input: EditorInput | null): void {
if (!(input instanceof UntitledEditorInput) && !(input instanceof ResourceEditorInput)) {
return; // only enabled for untitled and resource inputs
}

View File

@@ -32,13 +32,14 @@ import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceData
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions } from 'vs/workbench/browser/parts/editor/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource } from 'vs/workbench/common/editor';
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource, IEditorPartOptions } from 'vs/workbench/common/editor';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { Themable } from 'vs/workbench/common/theme';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { IFileService } from 'vs/platform/files/common/files';
import { withUndefinedAsNull } from 'vs/base/common/types';
export interface IToolbarActions {
primary: IAction[];
@@ -50,7 +51,7 @@ export abstract class TitleControl extends Themable {
protected readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
protected readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
protected breadcrumbsControl: BreadcrumbsControl;
protected breadcrumbsControl?: BreadcrumbsControl;
private currentPrimaryEditorActionIds: string[] = [];
private currentSecondaryEditorActionIds: string[] = [];
@@ -155,18 +156,18 @@ export abstract class TitleControl extends Themable {
}));
}
private actionItemProvider(action: Action): IActionItem {
private actionItemProvider(action: Action): IActionItem | null {
const activeControl = this.group.activeControl;
// Check Active Editor
let actionItem: IActionItem;
let actionItem: IActionItem | null = null;
if (activeControl instanceof BaseEditor) {
actionItem = activeControl.getActionItem(action);
}
// Check extensions
if (!actionItem) {
actionItem = createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
actionItem = withUndefinedAsNull(createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService));
}
return actionItem;
@@ -218,7 +219,7 @@ export abstract class TitleControl extends Themable {
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
// Update the resource context
this.resourceContext.set(toResource(this.group.activeEditor, { supportSideBySide: true }));
this.resourceContext.set(this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: true }) : null);
// Editor actions require the editor control to be there, so we retrieve it via service
const activeControl = this.group.activeControl;
@@ -254,23 +255,25 @@ export abstract class TitleControl extends Themable {
// Set editor group as transfer
this.groupTransfer.setData([new DraggedEditorGroupIdentifier(this.group.id)], DraggedEditorGroupIdentifier.prototype);
e.dataTransfer.effectAllowed = 'copyMove';
e.dataTransfer!.effectAllowed = 'copyMove';
// If tabs are disabled, treat dragging as if an editor tab was dragged
if (!this.accessor.partOptions.showTabs) {
const resource = toResource(this.group.activeEditor, { supportSideBySide: true });
const resource = this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: true }) : null;
if (resource) {
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
}
}
// Drag Image
let label = this.group.activeEditor.getName();
if (this.accessor.partOptions.showTabs && this.group.count > 1) {
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
}
if (this.group.activeEditor) {
let label = this.group.activeEditor.getName();
if (this.accessor.partOptions.showTabs && this.group.count > 1) {
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
}
applyDragImage(e, label, 'monaco-editor-group-drag-image');
applyDragImage(e, label, 'monaco-editor-group-drag-image');
}
}));
// Drag end
@@ -305,7 +308,7 @@ export abstract class TitleControl extends Themable {
onHide: () => {
// restore previous context
this.resourceContext.set(currentContext);
this.resourceContext.set(currentContext || null);
// restore focus to active group
this.accessor.activeGroup.focus();
@@ -313,14 +316,14 @@ export abstract class TitleControl extends Themable {
});
}
private getKeybinding(action: IAction): ResolvedKeybinding {
private getKeybinding(action: IAction): ResolvedKeybinding | undefined {
return this.keybindingService.lookupKeybinding(action.id);
}
protected getKeybindingLabel(action: IAction): string {
protected getKeybindingLabel(action: IAction): string | undefined {
const keybinding = this.getKeybinding(action);
return keybinding ? keybinding.getLabel() : undefined;
return keybinding ? keybinding.getLabel() || undefined : undefined;
}
abstract openEditor(editor: IEditorInput): void;
@@ -356,7 +359,8 @@ export abstract class TitleControl extends Themable {
}
dispose(): void {
this.breadcrumbsControl = dispose(this.breadcrumbsControl);
dispose(this.breadcrumbsControl);
this.breadcrumbsControl = undefined;
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
super.dispose();