mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 01:00:29 -04:00
Merge from master
This commit is contained in:
@@ -2,18 +2,16 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Panel } from 'vs/workbench/browser/panel';
|
||||
import { EditorInput, EditorOptions, IEditor, GroupIdentifier, IEditorMemento } from 'vs/workbench/common/editor';
|
||||
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 { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { once, Event } from 'vs/base/common/event';
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
import { DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
|
||||
@@ -43,16 +41,17 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
||||
readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = Event.None;
|
||||
|
||||
protected _input: EditorInput;
|
||||
protected _options: EditorOptions;
|
||||
|
||||
private _options: EditorOptions;
|
||||
private _group: IEditorGroup;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
get input(): EditorInput {
|
||||
@@ -82,7 +81,7 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
||||
this._input = input;
|
||||
this._options = options;
|
||||
|
||||
return TPromise.wrap<void>(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,15 +104,11 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void; // create is sync for editors
|
||||
create(parent: HTMLElement): TPromise<void>;
|
||||
create(parent: HTMLElement): TPromise<void> {
|
||||
const res = super.create(parent);
|
||||
create(parent: HTMLElement): void {
|
||||
super.create(parent);
|
||||
|
||||
// Create Editor
|
||||
this.createEditor(parent);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,15 +116,10 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
||||
*/
|
||||
protected abstract createEditor(parent: HTMLElement): void;
|
||||
|
||||
setVisible(visible: boolean, group?: IEditorGroup): void; // setVisible is sync for editors
|
||||
setVisible(visible: boolean, group?: IEditorGroup): TPromise<void>;
|
||||
setVisible(visible: boolean, group?: IEditorGroup): TPromise<void> {
|
||||
const promise = super.setVisible(visible);
|
||||
|
||||
setVisible(visible: boolean, group?: IEditorGroup): void {
|
||||
super.setVisible(visible);
|
||||
// Propagate to Editor
|
||||
this.setEditorVisible(visible, group);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,28 +133,28 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
||||
this._group = group;
|
||||
}
|
||||
|
||||
protected getEditorMemento<T>(storageService: IStorageService, editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
protected getEditorMemento<T>(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
const mementoKey = `${this.getId()}${key}`;
|
||||
|
||||
let editorMemento = BaseEditor.EDITOR_MEMENTOS.get(mementoKey);
|
||||
if (!editorMemento) {
|
||||
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(storageService), limit, editorGroupService);
|
||||
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService);
|
||||
BaseEditor.EDITOR_MEMENTOS.set(mementoKey, editorMemento);
|
||||
}
|
||||
|
||||
return editorMemento;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
|
||||
// Shutdown all editor memento for this editor type
|
||||
// Save all editor memento for this editor type
|
||||
BaseEditor.EDITOR_MEMENTOS.forEach(editorMemento => {
|
||||
if (editorMemento.id === this.getId()) {
|
||||
editorMemento.shutdown();
|
||||
editorMemento.saveState();
|
||||
}
|
||||
});
|
||||
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -195,9 +185,9 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
saveState(group: IEditorGroup, resource: URI, state: T): void;
|
||||
saveState(group: IEditorGroup, editor: EditorInput, state: T): void;
|
||||
saveState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, state: T): void {
|
||||
saveEditorState(group: IEditorGroup, resource: URI, state: T): void;
|
||||
saveEditorState(group: IEditorGroup, editor: EditorInput, state: T): void;
|
||||
saveEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, state: T): void {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (!resource || !group) {
|
||||
return; // we are not in a good state to save any state for a resource
|
||||
@@ -216,14 +206,14 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
// Automatically clear when editor input gets disposed if any
|
||||
if (resourceOrEditor instanceof EditorInput) {
|
||||
once(resourceOrEditor.onDispose)(() => {
|
||||
this.clearState(resource);
|
||||
this.clearEditorState(resource);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadState(group: IEditorGroup, resource: URI): T;
|
||||
loadState(group: IEditorGroup, editor: EditorInput): T;
|
||||
loadState(group: IEditorGroup, resourceOrEditor: URI | EditorInput): T {
|
||||
loadEditorState(group: IEditorGroup, resource: URI): T;
|
||||
loadEditorState(group: IEditorGroup, editor: EditorInput): T;
|
||||
loadEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput): T {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (!resource || !group) {
|
||||
return void 0; // we are not in a good state to load any state for a resource
|
||||
@@ -239,13 +229,21 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
clearState(resource: URI): void;
|
||||
clearState(editor: EditorInput): void;
|
||||
clearState(resourceOrEditor: URI | EditorInput): void {
|
||||
clearEditorState(resource: URI, group?: IEditorGroup): void;
|
||||
clearEditorState(editor: EditorInput, group?: IEditorGroup): void;
|
||||
clearEditorState(resourceOrEditor: URI | EditorInput, group?: IEditorGroup): void {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (resource) {
|
||||
const cache = this.doLoad();
|
||||
cache.delete(resource.toString());
|
||||
|
||||
if (group) {
|
||||
const resourceViewState = cache.get(resource.toString());
|
||||
if (resourceViewState) {
|
||||
delete resourceViewState[group.id];
|
||||
}
|
||||
} else {
|
||||
cache.delete(resource.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +269,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
saveState(): void {
|
||||
const cache = this.doLoad();
|
||||
|
||||
// Cleanup once during shutdown
|
||||
@@ -300,4 +298,4 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
@@ -12,6 +10,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
/**
|
||||
* An implementation of editor for diffing binary files like images or videos.
|
||||
@@ -23,9 +22,10 @@ export class BinaryResourceDiffEditor extends SideBySideEditor {
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(telemetryService, instantiationService, themeService);
|
||||
super(telemetryService, instantiationService, themeService, storageService);
|
||||
}
|
||||
|
||||
getMetadata(): string {
|
||||
|
||||
@@ -3,12 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
|
||||
@@ -17,13 +13,15 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Dimension, size, clearNode } from 'vs/base/browser/dom';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export interface IOpenCallbacks {
|
||||
openInternal: (input: EditorInput, options: EditorOptions) => void;
|
||||
openInternal: (input: EditorInput, options: EditorOptions) => Thenable<void>;
|
||||
openExternal: (uri: URI) => void;
|
||||
}
|
||||
|
||||
@@ -35,9 +33,12 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
private readonly _onMetadataChanged: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onMetadataChanged(): Event<void> { return this._onMetadataChanged.event; }
|
||||
|
||||
private readonly _onDidOpenInPlace: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onDidOpenInPlace(): Event<void> { return this._onDidOpenInPlace.event; }
|
||||
|
||||
private callbacks: IOpenCallbacks;
|
||||
private metadata: string;
|
||||
private binaryContainer: Builder;
|
||||
private binaryContainer: HTMLElement;
|
||||
private scrollbar: DomScrollableElement;
|
||||
private resourceViewerContext: ResourceViewerContext;
|
||||
|
||||
@@ -47,8 +48,9 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
@@ -60,14 +62,13 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
protected createEditor(parent: HTMLElement): void {
|
||||
|
||||
// Container for Binary
|
||||
const binaryContainerElement = document.createElement('div');
|
||||
binaryContainerElement.className = 'binary-container';
|
||||
this.binaryContainer = $(binaryContainerElement);
|
||||
this.binaryContainer.style('outline', 'none');
|
||||
this.binaryContainer.tabindex(0); // enable focus support from the editor part (do not remove)
|
||||
this.binaryContainer = document.createElement('div');
|
||||
this.binaryContainer.className = 'binary-container';
|
||||
this.binaryContainer.style.outline = 'none';
|
||||
this.binaryContainer.tabIndex = 0; // enable focus support from the editor part (do not remove)
|
||||
|
||||
// Custom Scrollbars
|
||||
this.scrollbar = this._register(new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto }));
|
||||
this.scrollbar = this._register(new DomScrollableElement(this.binaryContainer, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto }));
|
||||
parent.appendChild(this.scrollbar.getDomNode());
|
||||
}
|
||||
|
||||
@@ -82,16 +83,16 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
|
||||
// Assert Model instance
|
||||
if (!(model instanceof BinaryEditorModel)) {
|
||||
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
|
||||
return Promise.reject(new Error('Unable to open file as binary'));
|
||||
}
|
||||
|
||||
// Render Input
|
||||
this.resourceViewerContext = ResourceViewer.show(
|
||||
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() },
|
||||
this._fileService,
|
||||
this.binaryContainer.getHTMLElement(),
|
||||
this.binaryContainer,
|
||||
this.scrollbar,
|
||||
resource => this.callbacks.openInternal(input, options),
|
||||
resource => this.handleOpenInternalCallback(input, options),
|
||||
resource => this.callbacks.openExternal(resource),
|
||||
meta => this.handleMetadataChanged(meta)
|
||||
);
|
||||
@@ -101,6 +102,14 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
});
|
||||
}
|
||||
|
||||
private handleOpenInternalCallback(input: EditorInput, options: EditorOptions) {
|
||||
this.callbacks.openInternal(input, options).then(() => {
|
||||
|
||||
// Signal to listeners that the binary editor has been opened in-place
|
||||
this._onDidOpenInPlace.fire();
|
||||
});
|
||||
}
|
||||
|
||||
private handleMetadataChanged(meta: string): void {
|
||||
this.metadata = meta;
|
||||
|
||||
@@ -116,8 +125,9 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
// Clear Meta
|
||||
this.handleMetadataChanged(null);
|
||||
|
||||
// Empty HTML Container
|
||||
$(this.binaryContainer).empty();
|
||||
// Clear Resource Viewer
|
||||
clearNode(this.binaryContainer);
|
||||
this.resourceViewerContext = dispose(this.resourceViewerContext);
|
||||
|
||||
super.clearInput();
|
||||
}
|
||||
@@ -125,21 +135,21 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
layout(dimension: Dimension): void {
|
||||
|
||||
// Pass on to Binary Container
|
||||
this.binaryContainer.size(dimension.width, dimension.height);
|
||||
size(this.binaryContainer, dimension.width, dimension.height);
|
||||
this.scrollbar.scanDomNode();
|
||||
if (this.resourceViewerContext) {
|
||||
if (this.resourceViewerContext && this.resourceViewerContext.layout) {
|
||||
this.resourceViewerContext.layout(dimension);
|
||||
}
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.binaryContainer.domFocus();
|
||||
this.binaryContainer.focus();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.binaryContainer.remove();
|
||||
|
||||
// Destroy Container
|
||||
this.binaryContainer.destroy();
|
||||
this.resourceViewerContext = dispose(this.resourceViewerContext);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -3,18 +3,17 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BreadcrumbsWidget } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
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 { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
|
||||
export const IBreadcrumbsService = createDecorator<IBreadcrumbsService>('IEditorBreadcrumbsService');
|
||||
|
||||
@@ -57,8 +56,10 @@ registerSingleton(IBreadcrumbsService, BreadcrumbsService);
|
||||
export abstract class BreadcrumbsConfig<T> {
|
||||
|
||||
name: string;
|
||||
value: T;
|
||||
onDidChange: Event<T>;
|
||||
onDidChange: Event<void>;
|
||||
|
||||
abstract getValue(overrides?: IConfigurationOverrides): T;
|
||||
abstract updateValue(value: T, overrides?: IConfigurationOverrides): Thenable<void>;
|
||||
abstract dispose(): void;
|
||||
|
||||
private constructor() {
|
||||
@@ -69,30 +70,31 @@ export abstract class BreadcrumbsConfig<T> {
|
||||
static UseQuickPick = BreadcrumbsConfig._stub<boolean>('breadcrumbs.useQuickPick');
|
||||
static FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath');
|
||||
static SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath');
|
||||
static SymbolSortOrder = BreadcrumbsConfig._stub<'position' | 'name' | 'type'>('breadcrumbs.symbolSortOrder');
|
||||
static FilterOnType = BreadcrumbsConfig._stub<boolean>('breadcrumbs.filterOnType');
|
||||
|
||||
static FileExcludes = BreadcrumbsConfig._stub<glob.IExpression>('files.exclude');
|
||||
|
||||
private static _stub<T>(name: string): { bindTo(service: IConfigurationService): BreadcrumbsConfig<T> } {
|
||||
return {
|
||||
bindTo(service) {
|
||||
let value: T = service.getValue(name);
|
||||
let onDidChange = new Emitter<T>();
|
||||
let onDidChange = new Emitter<void>();
|
||||
|
||||
let listener = service.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(name)) {
|
||||
value = service.getValue(name);
|
||||
onDidChange.fire(value);
|
||||
onDidChange.fire(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
name,
|
||||
get value() {
|
||||
return value;
|
||||
},
|
||||
set value(newValue: T) {
|
||||
service.updateValue(name, newValue);
|
||||
value = newValue;
|
||||
},
|
||||
onDidChange: onDidChange.event,
|
||||
return new class implements BreadcrumbsConfig<T>{
|
||||
readonly name = name;
|
||||
readonly onDidChange = onDidChange.event;
|
||||
getValue(overrides?: IConfigurationOverrides): T {
|
||||
return service.getValue(name, overrides);
|
||||
}
|
||||
updateValue(newValue: T, overrides?: IConfigurationOverrides): Thenable<void> {
|
||||
return service.updateValue(name, newValue, overrides);
|
||||
}
|
||||
dispose(): void {
|
||||
listener.dispose();
|
||||
onDidChange.dispose();
|
||||
@@ -141,6 +143,22 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
|
||||
localize('symbolpath.last', "Only show the current symbol in the breadcrumbs view."),
|
||||
]
|
||||
},
|
||||
'breadcrumbs.symbolSortOrder': {
|
||||
description: localize('symbolSortOrder', "Controls how symbols are sorted in the breadcrumbs outline view."),
|
||||
type: 'string',
|
||||
default: 'position',
|
||||
enum: ['position', 'name', 'type'],
|
||||
enumDescriptions: [
|
||||
localize('symbolSortOrder.position', "Show symbol outline in file position order."),
|
||||
localize('symbolSortOrder.name', "Show symbol outline in alphabetical order."),
|
||||
localize('symbolSortOrder.type', "Show symbol outline in symbol type order."),
|
||||
]
|
||||
},
|
||||
// 'breadcrumbs.filterOnType': {
|
||||
// description: localize('filterOnType', "Controls whether the breadcrumb picker filters or highlights when typing."),
|
||||
// type: 'boolean',
|
||||
// default: false
|
||||
// },
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,43 +3,48 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { BreadcrumbsItem, BreadcrumbsWidget, IBreadcrumbsItemEvent } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget';
|
||||
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { tail } from 'vs/base/common/arrays';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { combinedDisposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/breadcrumbscontrol';
|
||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { symbolKindToCssClass } from 'vs/editor/common/modes';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { FileKind, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IListService, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { ColorIdentifier, ColorFunction } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { FileLabel } from 'vs/workbench/browser/labels';
|
||||
import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs';
|
||||
import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
|
||||
import { createBreadcrumbsPicker, BreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker';
|
||||
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
|
||||
import { IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
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 { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { tail } from 'vs/base/common/arrays';
|
||||
import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService';
|
||||
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';
|
||||
|
||||
class Item extends BreadcrumbsItem {
|
||||
|
||||
@@ -116,20 +121,22 @@ export interface IBreadcrumbsControlOptions {
|
||||
showFileIcons: boolean;
|
||||
showSymbolIcons: boolean;
|
||||
showDecorationColors: boolean;
|
||||
extraClasses: string[];
|
||||
breadcrumbsBackground: ColorIdentifier | ColorFunction;
|
||||
}
|
||||
|
||||
export class BreadcrumbsControl {
|
||||
|
||||
static HEIGHT = 25;
|
||||
static HEIGHT = 22;
|
||||
|
||||
static readonly Payload_Reveal = {};
|
||||
static readonly Payload_RevealAside = {};
|
||||
static readonly Payload_Pick = {};
|
||||
|
||||
static CK_BreadcrumbsPossible = new RawContextKey('breadcrumbsPossible', false);
|
||||
static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false);
|
||||
static CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false);
|
||||
|
||||
private readonly _ckBreadcrumbsPossible: IContextKey<boolean>;
|
||||
private readonly _ckBreadcrumbsVisible: IContextKey<boolean>;
|
||||
private readonly _ckBreadcrumbsActive: IContextKey<boolean>;
|
||||
|
||||
@@ -145,29 +152,31 @@ export class BreadcrumbsControl {
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
private readonly _options: IBreadcrumbsControlOptions,
|
||||
private readonly _editorGroup: EditorGroupView,
|
||||
private readonly _editorGroup: IEditorGroupView,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IContextViewService private readonly _contextViewService: IContextViewService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
|
||||
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
@IQuickOpenService private readonly _quickOpenService: IQuickOpenService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IBreadcrumbsService breadcrumbsService: IBreadcrumbsService,
|
||||
) {
|
||||
this.domNode = document.createElement('div');
|
||||
dom.addClass(this.domNode, 'breadcrumbs-control');
|
||||
dom.addClasses(this.domNode, ..._options.extraClasses);
|
||||
dom.append(container, this.domNode);
|
||||
|
||||
this._widget = new BreadcrumbsWidget(this.domNode);
|
||||
this._widget.onDidSelectItem(this._onSelectEvent, this, this._disposables);
|
||||
this._widget.onDidFocusItem(this._onFocusEvent, this, this._disposables);
|
||||
this._widget.onDidChangeFocus(this._updateCkBreadcrumbsActive, this, this._disposables);
|
||||
this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService));
|
||||
this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService, { breadcrumbsBackground: _options.breadcrumbsBackground }));
|
||||
|
||||
this._ckBreadcrumbsPossible = BreadcrumbsControl.CK_BreadcrumbsPossible.bindTo(this._contextKeyService);
|
||||
this._ckBreadcrumbsVisible = BreadcrumbsControl.CK_BreadcrumbsVisible.bindTo(this._contextKeyService);
|
||||
this._ckBreadcrumbsActive = BreadcrumbsControl.CK_BreadcrumbsActive.bindTo(this._contextKeyService);
|
||||
|
||||
@@ -179,6 +188,7 @@ export class BreadcrumbsControl {
|
||||
dispose(): void {
|
||||
this._disposables = dispose(this._disposables);
|
||||
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
|
||||
this._ckBreadcrumbsPossible.reset();
|
||||
this._ckBreadcrumbsVisible.reset();
|
||||
this._ckBreadcrumbsActive.reset();
|
||||
this._cfUseQuickPick.dispose();
|
||||
@@ -201,12 +211,18 @@ export class BreadcrumbsControl {
|
||||
}
|
||||
|
||||
update(): boolean {
|
||||
const input = this._editorGroup.activeEditor;
|
||||
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
|
||||
|
||||
// honor diff editors and such
|
||||
let input = this._editorGroup.activeEditor;
|
||||
if (input instanceof SideBySideEditorInput) {
|
||||
input = input.master;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!this.isHidden()) {
|
||||
this.hide();
|
||||
return true;
|
||||
@@ -217,9 +233,10 @@ export class BreadcrumbsControl {
|
||||
|
||||
dom.toggleClass(this.domNode, 'hidden', false);
|
||||
this._ckBreadcrumbsVisible.set(true);
|
||||
this._ckBreadcrumbsPossible.set(true);
|
||||
|
||||
let control = this._editorGroup.activeControl.getControl() as ICodeEditor;
|
||||
let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService, this._configurationService);
|
||||
let editor = this._getActiveCodeEditor();
|
||||
let model = new EditorBreadcrumbsModel(input.getResource(), editor, this._workspaceService, this._configurationService);
|
||||
dom.toggleClass(this.domNode, 'relative-path', model.isRelative());
|
||||
|
||||
let updateBreadcrumbs = () => {
|
||||
@@ -243,6 +260,17 @@ export class BreadcrumbsControl {
|
||||
return true;
|
||||
}
|
||||
|
||||
private _getActiveCodeEditor(): ICodeEditor {
|
||||
let control = this._editorGroup.activeControl.getControl();
|
||||
let editor: ICodeEditor;
|
||||
if (isCodeEditor(control)) {
|
||||
editor = control as ICodeEditor;
|
||||
} else if (isDiffEditor(control)) {
|
||||
editor = control.getModifiedEditor();
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
private _onFocusEvent(event: IBreadcrumbsItemEvent): void {
|
||||
if (event.item && this._breadcrumbsPickerShowing) {
|
||||
return this._widget.setSelection(event.item);
|
||||
@@ -254,8 +282,15 @@ export class BreadcrumbsControl {
|
||||
return;
|
||||
}
|
||||
|
||||
this._editorGroup.focus();
|
||||
const { element } = event.item as Item;
|
||||
this._editorGroup.focus();
|
||||
|
||||
/* __GDPR__
|
||||
"breadcrumbs/select" : {
|
||||
"type": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('breadcrumbs/select', { type: element instanceof TreeElement ? 'symbol' : 'file' });
|
||||
|
||||
const group = this._getEditorGroup(event.payload);
|
||||
if (group !== undefined) {
|
||||
@@ -266,7 +301,7 @@ export class BreadcrumbsControl {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._cfUseQuickPick.value) {
|
||||
if (this._cfUseQuickPick.getValue()) {
|
||||
// using quick pick
|
||||
this._widget.setFocused(undefined);
|
||||
this._widget.setSelection(undefined);
|
||||
@@ -276,41 +311,86 @@ export class BreadcrumbsControl {
|
||||
|
||||
// show picker
|
||||
let picker: BreadcrumbsPicker;
|
||||
let editor = this._getActiveCodeEditor();
|
||||
let editorDecorations: string[] = [];
|
||||
let editorViewState: ICodeEditorViewState;
|
||||
|
||||
this._contextViewService.showContextView({
|
||||
render: (parent: HTMLElement) => {
|
||||
picker = createBreadcrumbsPicker(this._instantiationService, parent, element);
|
||||
let listener = picker.onDidPickElement(data => {
|
||||
let selectListener = picker.onDidPickElement(data => {
|
||||
if (data.target) {
|
||||
editorViewState = undefined;
|
||||
}
|
||||
this._contextViewService.hideContextView(this);
|
||||
this._revealInEditor(event, data.target, this._getEditorGroup(data.payload && data.payload.originalEvent));
|
||||
this._revealInEditor(event, data.target, this._getEditorGroup(data.payload && data.payload.originalEvent), (data.payload && data.payload.originalEvent && data.payload.originalEvent.middleButton));
|
||||
/* __GDPR__
|
||||
"breadcrumbs/open" : {
|
||||
"type": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('breadcrumbs/open', { type: !data ? 'nothing' : data.target instanceof TreeElement ? 'symbol' : 'file' });
|
||||
});
|
||||
let focusListener = picker.onDidFocusElement(data => {
|
||||
if (!editor || !(data.target instanceof OutlineElement)) {
|
||||
return;
|
||||
}
|
||||
if (!editorViewState) {
|
||||
editorViewState = editor.saveViewState();
|
||||
}
|
||||
const { symbol } = data.target;
|
||||
editor.revealRangeInCenter(symbol.range, ScrollType.Smooth);
|
||||
editorDecorations = editor.deltaDecorations(editorDecorations, [{
|
||||
range: symbol.range,
|
||||
options: {
|
||||
className: 'rangeHighlight',
|
||||
isWholeLine: true
|
||||
}
|
||||
}]);
|
||||
|
||||
});
|
||||
this._breadcrumbsPickerShowing = true;
|
||||
this._updateCkBreadcrumbsActive();
|
||||
|
||||
return combinedDisposable([listener, picker]);
|
||||
return combinedDisposable([selectListener, focusListener, picker]);
|
||||
},
|
||||
getAnchor() {
|
||||
getAnchor: () => {
|
||||
let maxInnerWidth = window.innerWidth - 8 /*a little less the full widget*/;
|
||||
let maxHeight = Math.min(window.innerHeight * .7, 300);
|
||||
|
||||
let pickerHeight = 330;
|
||||
let pickerWidth = Math.max(220, dom.getTotalWidth(event.node));
|
||||
let pickerWidth = Math.min(maxInnerWidth, Math.max(240, maxInnerWidth / 4.17));
|
||||
let pickerArrowSize = 8;
|
||||
let pickerArrowOffset: number;
|
||||
|
||||
let data = dom.getDomNodePagePosition(event.node.firstChild as HTMLElement);
|
||||
let y = data.top + data.height - pickerArrowSize;
|
||||
let y = data.top + data.height + pickerArrowSize;
|
||||
if (y + maxHeight >= window.innerHeight) {
|
||||
maxHeight = window.innerHeight - y - 30 /* room for shadow and status bar*/;
|
||||
}
|
||||
let x = data.left;
|
||||
if (x + pickerWidth >= window.innerWidth) {
|
||||
x = window.innerWidth - pickerWidth;
|
||||
if (x + pickerWidth >= maxInnerWidth) {
|
||||
x = maxInnerWidth - pickerWidth;
|
||||
}
|
||||
if (event.payload instanceof StandardMouseEvent) {
|
||||
pickerArrowOffset = event.payload.posx - x - pickerArrowSize;
|
||||
let maxPickerArrowOffset = pickerWidth - 2 * pickerArrowSize;
|
||||
pickerArrowOffset = event.payload.posx - x;
|
||||
if (pickerArrowOffset > maxPickerArrowOffset) {
|
||||
x = Math.min(maxInnerWidth - pickerWidth, x + pickerArrowOffset - maxPickerArrowOffset);
|
||||
pickerArrowOffset = maxPickerArrowOffset;
|
||||
}
|
||||
} else {
|
||||
pickerArrowOffset = (data.left + (data.width * .3)) - x;
|
||||
}
|
||||
picker.layout(pickerHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
|
||||
picker.setInput(element);
|
||||
picker.setInput(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
|
||||
return { x, y };
|
||||
},
|
||||
onHide: (data) => {
|
||||
if (editor) {
|
||||
editor.deltaDecorations(editorDecorations, []);
|
||||
if (editorViewState) {
|
||||
editor.restoreViewState(editorViewState);
|
||||
}
|
||||
}
|
||||
this._breadcrumbsPickerShowing = false;
|
||||
this._updateCkBreadcrumbsActive();
|
||||
if (data === this) {
|
||||
@@ -326,11 +406,11 @@ export class BreadcrumbsControl {
|
||||
this._ckBreadcrumbsActive.set(value);
|
||||
}
|
||||
|
||||
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): void {
|
||||
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, pinned: boolean = false): void {
|
||||
if (element instanceof FileElement) {
|
||||
if (element.kind === FileKind.FILE) {
|
||||
// open file in editor
|
||||
this._editorService.openEditor({ resource: element.uri }, group);
|
||||
// open file in any editor
|
||||
this._editorService.openEditor({ resource: element.uri, options: { pinned: pinned } }, group);
|
||||
} else {
|
||||
// show next picker
|
||||
let items = this._widget.getItems();
|
||||
@@ -340,12 +420,15 @@ export class BreadcrumbsControl {
|
||||
}
|
||||
|
||||
} else if (element instanceof OutlineElement) {
|
||||
// open symbol in editor
|
||||
// open symbol in code editor
|
||||
let model = OutlineModel.get(element);
|
||||
this._editorService.openEditor({
|
||||
this._codeEditorService.openCodeEditor({
|
||||
resource: model.textModel.uri,
|
||||
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
|
||||
}, group);
|
||||
options: {
|
||||
selection: Range.collapseToStart(element.symbol.selectionRange),
|
||||
revealInCenterIfOutsideViewport: true
|
||||
}
|
||||
}, this._getActiveCodeEditor(), group === SIDE_GROUP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,16 +445,12 @@ export class BreadcrumbsControl {
|
||||
|
||||
//#region commands
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: 'breadcrumbs.focusAndSelect',
|
||||
title: localize('cmd.focus', "Focus Breadcrumbs")
|
||||
}
|
||||
});
|
||||
// toggle command
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: 'breadcrumbs.toggle',
|
||||
title: localize('cmd.toggle', "Toggle Breadcrumbs")
|
||||
title: { value: localize('cmd.toggle', "Toggle Breadcrumbs"), original: 'Toggle Breadcrumbs' },
|
||||
category: localize('cmd.category', "View")
|
||||
}
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
@@ -379,42 +458,73 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
order: 99,
|
||||
command: {
|
||||
id: 'breadcrumbs.toggle',
|
||||
title: localize('cmd.toggle', "Toggle Breadcrumbs")
|
||||
title: localize('miToggleBreadcrumbs', "Toggle &&Breadcrumbs"),
|
||||
toggled: ContextKeyExpr.equals('config.breadcrumbs.enabled', true)
|
||||
}
|
||||
});
|
||||
CommandsRegistry.registerCommand('breadcrumbs.toggle', accessor => {
|
||||
let config = accessor.get(IConfigurationService);
|
||||
let value = BreadcrumbsConfig.IsEnabled.bindTo(config).value;
|
||||
BreadcrumbsConfig.IsEnabled.bindTo(config).value = !value;
|
||||
let value = BreadcrumbsConfig.IsEnabled.bindTo(config).getValue();
|
||||
BreadcrumbsConfig.IsEnabled.bindTo(config).updateValue(!value);
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.focus',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_SEMICOLON,
|
||||
when: BreadcrumbsControl.CK_BreadcrumbsVisible,
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
// focus/focus-and-select
|
||||
function focusAndSelectHandler(accessor: ServicesAccessor, select: boolean): void {
|
||||
// find widget and focus/select
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
if (widget) {
|
||||
const item = tail(widget.getItems());
|
||||
widget.setFocused(item);
|
||||
if (select) {
|
||||
widget.setSelection(item, BreadcrumbsControl.Payload_Pick);
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: 'breadcrumbs.focusAndSelect',
|
||||
title: { value: localize('cmd.focus', "Focus Breadcrumbs"), original: 'Focus Breadcrumbs' },
|
||||
precondition: BreadcrumbsControl.CK_BreadcrumbsVisible
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.focusAndSelect',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT,
|
||||
when: BreadcrumbsControl.CK_BreadcrumbsVisible,
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
const item = tail(widget.getItems());
|
||||
widget.setFocused(item);
|
||||
widget.setSelection(item, BreadcrumbsControl.Payload_Pick);
|
||||
when: BreadcrumbsControl.CK_BreadcrumbsPossible,
|
||||
handler: accessor => focusAndSelectHandler(accessor, true)
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.focus',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_SEMICOLON,
|
||||
when: BreadcrumbsControl.CK_BreadcrumbsPossible,
|
||||
handler: accessor => focusAndSelectHandler(accessor, false)
|
||||
});
|
||||
|
||||
// this commands is only enabled when breadcrumbs are
|
||||
// disabled which it then enables and focuses
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.toggleToOn',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT,
|
||||
when: ContextKeyExpr.not('config.breadcrumbs.enabled'),
|
||||
handler: async accessor => {
|
||||
const instant = accessor.get(IInstantiationService);
|
||||
const config = accessor.get(IConfigurationService);
|
||||
// check if enabled and iff not enable
|
||||
const isEnabled = BreadcrumbsConfig.IsEnabled.bindTo(config);
|
||||
if (!isEnabled.getValue()) {
|
||||
await isEnabled.updateValue(true);
|
||||
await timeout(50); // hacky - the widget might not be ready yet...
|
||||
}
|
||||
return instant.invokeFunction(focusAndSelectHandler, true);
|
||||
}
|
||||
});
|
||||
|
||||
// navigation
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.focusNext',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
@@ -492,10 +602,26 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Enter,
|
||||
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive, WorkbenchListFocusContextKey),
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_RevealAside);
|
||||
const editors = accessor.get(IEditorService);
|
||||
const lists = accessor.get(IListService);
|
||||
const element = <OutlineElement | IFileStat>lists.lastFocusedList.getFocus();
|
||||
if (element instanceof OutlineElement) {
|
||||
// open symbol in editor
|
||||
return editors.openEditor({
|
||||
resource: OutlineModel.get(element).textModel.uri,
|
||||
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
|
||||
}, SIDE_GROUP);
|
||||
|
||||
} else if (URI.isUri(element.resource)) {
|
||||
// open file in editor
|
||||
return editors.openEditor({
|
||||
resource: element.resource,
|
||||
}, SIDE_GROUP);
|
||||
|
||||
} else {
|
||||
// ignore
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
//#endregion
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
@@ -12,9 +10,8 @@ import { size } from 'vs/base/common/collections';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { isEqual, dirname } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
|
||||
@@ -82,16 +79,16 @@ export class EditorBreadcrumbsModel {
|
||||
let result: BreadcrumbElement[] = [];
|
||||
|
||||
// file path elements
|
||||
if (this._cfgFilePath.value === 'on') {
|
||||
if (this._cfgFilePath.getValue() === 'on') {
|
||||
result = result.concat(this._fileInfo.path);
|
||||
} else if (this._cfgFilePath.value === 'last' && this._fileInfo.path.length > 0) {
|
||||
} else if (this._cfgFilePath.getValue() === 'last' && this._fileInfo.path.length > 0) {
|
||||
result = result.concat(this._fileInfo.path.slice(-1));
|
||||
}
|
||||
|
||||
// symbol path elements
|
||||
if (this._cfgSymbolPath.value === 'on') {
|
||||
if (this._cfgSymbolPath.getValue() === 'on') {
|
||||
result = result.concat(this._outlineElements);
|
||||
} else if (this._cfgSymbolPath.value === 'last' && this._outlineElements.length > 0) {
|
||||
} else if (this._cfgSymbolPath.getValue() === 'last' && this._outlineElements.length > 0) {
|
||||
result = result.concat(this._outlineElements.slice(-1));
|
||||
}
|
||||
|
||||
@@ -117,7 +114,7 @@ export class EditorBreadcrumbsModel {
|
||||
break;
|
||||
}
|
||||
info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER));
|
||||
uri = uri.with({ path: paths.dirname(uri.path) });
|
||||
uri = dirname(uri);
|
||||
}
|
||||
|
||||
if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
@@ -177,7 +174,7 @@ export class EditorBreadcrumbsModel {
|
||||
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
|
||||
this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => {
|
||||
timeout.cancelAndSet(() => {
|
||||
if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId()) {
|
||||
if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && this._editor.getModel()) {
|
||||
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
|
||||
}
|
||||
}, 150);
|
||||
|
||||
@@ -3,37 +3,47 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
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 * as glob from 'vs/base/common/glob';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { dirname, isEqual } from 'vs/base/common/resources';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { basename, dirname, isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
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 } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { OutlineDataSource, OutlineItemComparator, OutlineRenderer, OutlineItemCompareType } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { localize } from 'vs/nls';
|
||||
import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { HighlightingWorkbenchTree, IHighlightingTreeConfiguration, IHighlightingRenderer } from 'vs/platform/list/browser/listService';
|
||||
import { IThemeService, DARK } from 'vs/platform/theme/common/themeService';
|
||||
import { FileLabel } from 'vs/workbench/browser/labels';
|
||||
import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { breadcrumbsPickerBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { FuzzyScore, createMatches, fuzzyScore } from 'vs/base/common/filters';
|
||||
import { IWorkspaceContextService, IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfigurationService } 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 { breadcrumbsPickerBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { FileLabel } 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';
|
||||
|
||||
export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker {
|
||||
let ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
|
||||
return instantiationService.createInstance(ctor, parent);
|
||||
}
|
||||
|
||||
interface ILayoutInfo {
|
||||
maxHeight: number;
|
||||
width: number;
|
||||
arrowSize: number;
|
||||
arrowOffset: number;
|
||||
inputHeight: number;
|
||||
}
|
||||
|
||||
export abstract class BreadcrumbsPicker {
|
||||
|
||||
protected readonly _disposables = new Array<IDisposable>();
|
||||
@@ -42,14 +52,20 @@ export abstract class BreadcrumbsPicker {
|
||||
protected readonly _treeContainer: HTMLDivElement;
|
||||
protected readonly _tree: HighlightingWorkbenchTree;
|
||||
protected readonly _focus: dom.IFocusTracker;
|
||||
protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>;
|
||||
private _layoutInfo: ILayoutInfo;
|
||||
|
||||
private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>();
|
||||
readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event;
|
||||
|
||||
private readonly _onDidFocusElement = new Emitter<{ target: any, payload: any }>();
|
||||
readonly onDidFocusElement: Event<{ target: any, payload: any }> = this._onDidFocusElement.event;
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
|
||||
@IThemeService protected readonly _themeService: IThemeService,
|
||||
@IWorkbenchThemeService protected readonly _themeService: IWorkbenchThemeService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
) {
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons';
|
||||
@@ -57,42 +73,71 @@ export abstract class BreadcrumbsPicker {
|
||||
|
||||
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.style.width = '0';
|
||||
this._arrow.style.borderStyle = 'solid';
|
||||
this._arrow.style.borderWidth = '8px';
|
||||
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 ${(theme.type === DARK ? color.darken(.6) : color.darken(.2))}`;
|
||||
this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`;
|
||||
this._domNode.appendChild(this._treeContainer);
|
||||
|
||||
const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined });
|
||||
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,
|
||||
treeConifg,
|
||||
{ useShadows: false },
|
||||
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._getTargetFromSelectionEvent(e);
|
||||
if (!target) {
|
||||
return;
|
||||
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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
@@ -102,14 +147,20 @@ export abstract class BreadcrumbsPicker {
|
||||
this._onDidPickElement.dispose();
|
||||
this._tree.dispose();
|
||||
this._focus.dispose();
|
||||
this._symbolSortOrder.dispose();
|
||||
}
|
||||
|
||||
setInput(input: any): void {
|
||||
setInput(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void {
|
||||
let actualInput = this._getInput(input);
|
||||
this._tree.setInput(actualInput).then(() => {
|
||||
|
||||
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
|
||||
this._layout();
|
||||
|
||||
// use proper selection, reveal
|
||||
let selection = this._getInitialSelection(this._tree, input);
|
||||
if (selection) {
|
||||
this._tree.reveal(selection, .5).then(() => {
|
||||
return this._tree.reveal(selection, .5).then(() => {
|
||||
this._tree.setSelection([selection], this._tree);
|
||||
this._tree.setFocus(selection);
|
||||
this._tree.domFocus();
|
||||
@@ -118,25 +169,37 @@ export abstract class BreadcrumbsPicker {
|
||||
this._tree.focusFirst();
|
||||
this._tree.setSelection([this._tree.getFocus()], this._tree);
|
||||
this._tree.domFocus();
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
|
||||
layout(height: number, width: number, arrowSize: number, arrowOffset: number) {
|
||||
this._domNode.style.height = `${height}px`;
|
||||
this._domNode.style.width = `${width}px`;
|
||||
this._arrow.style.borderWidth = `${arrowSize}px`;
|
||||
this._arrow.style.marginLeft = `${arrowOffset}px`;
|
||||
private _layout(info: ILayoutInfo = this._layoutInfo): void {
|
||||
|
||||
this._treeContainer.style.height = `${height - 2 * arrowSize}px`;
|
||||
this._treeContainer.style.width = `${width}px`;
|
||||
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;
|
||||
|
||||
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._treeContainer.style.height = `${treeHeight}px`;
|
||||
this._treeContainer.style.width = `${info.width}px`;
|
||||
this._tree.layout();
|
||||
this._layoutInfo = info;
|
||||
|
||||
}
|
||||
|
||||
protected abstract _getInput(input: BreadcrumbElement): any;
|
||||
protected abstract _getInitialSelection(tree: ITree, input: BreadcrumbElement): any;
|
||||
protected abstract _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration;
|
||||
protected abstract _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined;
|
||||
protected abstract _getTargetFromEvent(element: any, payload: any): any | undefined;
|
||||
}
|
||||
|
||||
//#region - Files
|
||||
@@ -167,7 +230,7 @@ export class FileDataSource implements IDataSource {
|
||||
|
||||
getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): TPromise<IWorkspaceFolder[] | IFileStat[]> {
|
||||
if (IWorkspace.isIWorkspace(element)) {
|
||||
return TPromise.as(element.folders).then(folders => {
|
||||
return Promise.resolve(element.folders).then(folders => {
|
||||
for (let child of folders) {
|
||||
this._parents.set(element, child);
|
||||
}
|
||||
@@ -191,13 +254,80 @@ export class FileDataSource implements IDataSource {
|
||||
}
|
||||
|
||||
getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): TPromise<IWorkspaceFolder | IFileStat> {
|
||||
return TPromise.as(this._parents.get(element));
|
||||
return Promise.resolve(this._parents.get(element));
|
||||
}
|
||||
}
|
||||
|
||||
export class FileRenderer implements IRenderer, IHighlightingRenderer {
|
||||
export class FileFilter implements IFilter {
|
||||
|
||||
private readonly _scores = new Map<string, FuzzyScore>();
|
||||
private readonly _cachedExpressions = new Map<string, glob.ParsedExpression>();
|
||||
private readonly _disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
|
||||
@IConfigurationService configService: IConfigurationService,
|
||||
) {
|
||||
const config = BreadcrumbsConfig.FileExcludes.bindTo(configService);
|
||||
const update = () => {
|
||||
_workspaceService.getWorkspace().folders.forEach(folder => {
|
||||
const excludesConfig = config.getValue({ resource: folder.uri });
|
||||
if (!excludesConfig) {
|
||||
return;
|
||||
}
|
||||
// adjust patterns to be absolute in case they aren't
|
||||
// free floating (**/)
|
||||
const adjustedConfig: glob.IExpression = {};
|
||||
for (const pattern in excludesConfig) {
|
||||
if (typeof excludesConfig[pattern] !== 'boolean') {
|
||||
continue;
|
||||
}
|
||||
let patternAbs = pattern.indexOf('**/') !== 0
|
||||
? join(folder.uri.path, pattern)
|
||||
: pattern;
|
||||
|
||||
adjustedConfig[patternAbs] = excludesConfig[pattern];
|
||||
}
|
||||
this._cachedExpressions.set(folder.uri.toString(), glob.parse(adjustedConfig));
|
||||
});
|
||||
};
|
||||
update();
|
||||
this._disposables.push(
|
||||
config,
|
||||
config.onDidChange(update),
|
||||
_workspaceService.onDidChangeWorkspaceFolders(update)
|
||||
);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._disposables);
|
||||
}
|
||||
|
||||
isVisible(tree: ITree, element: IWorkspaceFolder | IFileStat): boolean {
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
|
||||
// not a file
|
||||
return true;
|
||||
}
|
||||
const folder = this._workspaceService.getWorkspaceFolder(element.resource);
|
||||
if (!folder || !this._cachedExpressions.has(folder.uri.toString())) {
|
||||
// no folder or no filer
|
||||
return true;
|
||||
}
|
||||
|
||||
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(
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@@ -231,29 +361,14 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer {
|
||||
fileKind,
|
||||
hidePath: true,
|
||||
fileDecorations: fileDecorations,
|
||||
matches: createMatches((this._scores.get(resource.toString()) || [, []])[1])
|
||||
matches: createMatches((tree as HighlightingWorkbenchTree).getHighlighterScore(element)),
|
||||
extraClasses: ['picker-item']
|
||||
});
|
||||
}
|
||||
|
||||
disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void {
|
||||
templateData.dispose();
|
||||
}
|
||||
|
||||
updateHighlights(tree: ITree, pattern: string): any {
|
||||
let nav = tree.getNavigator(undefined, false);
|
||||
let topScore: FuzzyScore;
|
||||
let topElement: any;
|
||||
while (nav.next()) {
|
||||
let element = nav.current() as IFileStat | IWorkspaceFolder;
|
||||
let score = fuzzyScore(pattern, element.name, undefined, true);
|
||||
this._scores.set(IWorkspaceFolder.isIWorkspaceFolder(element) ? element.uri.toString() : element.resource.toString(), score);
|
||||
if (!topScore || score && topScore[0] < score[0]) {
|
||||
topScore = score;
|
||||
topElement = element;
|
||||
}
|
||||
}
|
||||
return topElement;
|
||||
}
|
||||
}
|
||||
|
||||
export class FileSorter implements ISorter {
|
||||
@@ -278,10 +393,11 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
|
||||
@IConfigurationService configService: IConfigurationService,
|
||||
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
|
||||
) {
|
||||
super(parent, instantiationService, themeService);
|
||||
super(parent, instantiationService, themeService, configService);
|
||||
}
|
||||
|
||||
protected _getInput(input: BreadcrumbElement): any {
|
||||
@@ -308,16 +424,20 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
|
||||
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);
|
||||
config.renderer = this._instantiationService.createInstance(FileRenderer);
|
||||
config.sorter = new FileSorter();
|
||||
config.highlighter = new FileHighlighter();
|
||||
config.filter = filter;
|
||||
return config;
|
||||
}
|
||||
|
||||
protected _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined {
|
||||
let [first] = e.selection;
|
||||
if (first && !IWorkspaceFolder.isIWorkspaceFolder(first) && !(first as IFileStat).isDirectory) {
|
||||
return new FileElement((first as IFileStat).resource, FileKind.FILE);
|
||||
protected _getTargetFromEvent(element: any, _payload: any): any | undefined {
|
||||
if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) {
|
||||
return new FileElement((element as IFileStat).resource, FileKind.FILE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,11 +445,10 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
|
||||
//#region - Symbols
|
||||
|
||||
class HighlightingOutlineRenderer extends OutlineRenderer implements IHighlightingRenderer {
|
||||
|
||||
updateHighlights(tree: ITree, pattern: string): any {
|
||||
let model = OutlineModel.get(tree.getInput());
|
||||
return model.updateMatches(pattern);
|
||||
class OutlineHighlighter implements IHighlighter {
|
||||
getHighlights(tree: ITree, element: OutlineElement, pattern: string): FuzzyScore {
|
||||
OutlineModel.get(element).updateMatches(pattern);
|
||||
return element.score;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,18 +467,30 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
|
||||
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
|
||||
config.dataSource = this._instantiationService.createInstance(OutlineDataSource);
|
||||
config.renderer = this._instantiationService.createInstance(HighlightingOutlineRenderer);
|
||||
config.sorter = new OutlineItemComparator();
|
||||
config.renderer = this._instantiationService.createInstance(OutlineRenderer);
|
||||
config.sorter = new OutlineItemComparator(this._getOutlineItemComparator());
|
||||
config.highlighter = new OutlineHighlighter();
|
||||
return config;
|
||||
}
|
||||
|
||||
protected _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined {
|
||||
if (e.payload && e.payload.didClickOnTwistie) {
|
||||
protected _getTargetFromEvent(element: any, payload: any): any | undefined {
|
||||
if (payload && payload.didClickOnTwistie) {
|
||||
return;
|
||||
}
|
||||
let [first] = e.selection;
|
||||
if (first instanceof OutlineElement) {
|
||||
return first;
|
||||
if (element instanceof OutlineElement) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
private _getOutlineItemComparator(): OutlineItemCompareType {
|
||||
switch (this._symbolSortOrder.getValue()) {
|
||||
case 'name':
|
||||
return OutlineItemCompareType.ByName;
|
||||
case 'type':
|
||||
return OutlineItemCompareType.ByKind;
|
||||
case 'position':
|
||||
default:
|
||||
return OutlineItemCompareType.ByPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import * as nls from 'vs/nls';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { IEditorQuickOpenEntry, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { StatusbarItemDescriptor, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext } from 'vs/workbench/common/editor';
|
||||
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
|
||||
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
@@ -24,7 +24,7 @@ import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/bina
|
||||
import { ChangeEncodingAction, ChangeEOLAction, ChangeModeAction, EditorStatus } from 'vs/workbench/browser/parts/editor/editorStatus';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actions';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId, IMenuItem } from 'vs/platform/actions/common/actions';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import {
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
SplitEditorUpAction, SplitEditorDownAction, MoveEditorToLeftGroupAction, MoveEditorToRightGroupAction, MoveEditorToAboveGroupAction, MoveEditorToBelowGroupAction, CloseAllEditorGroupsAction,
|
||||
JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction,
|
||||
EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoRowsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction,
|
||||
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction
|
||||
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -48,6 +48,8 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { AllEditorsPicker, ActiveEditorGroupPicker } from 'vs/workbench/browser/parts/editor/editorPicker';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { OpenWorkspaceButtonContribution } from 'vs/workbench/browser/parts/editor/editorWidgets';
|
||||
|
||||
// Register String Editor
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
@@ -217,6 +219,9 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(SideBySideEditorInput.ID, SideBySideEditorInputFactory);
|
||||
|
||||
// Register Editor Contributions
|
||||
registerEditorContribution(OpenWorkspaceButtonContribution);
|
||||
|
||||
// Register Editor Status
|
||||
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
|
||||
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(EditorStatus, StatusbarAlignment.RIGHT, 100 /* towards the left of the right hand side */));
|
||||
@@ -322,6 +327,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction,
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorGroupsAction, CloseAllEditorGroupsAction.ID, CloseAllEditorGroupsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W) }), 'View: Close All Editor Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors in Group to the Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInOtherGroupsAction, CloseEditorsInOtherGroupsAction.ID, CloseEditorsInOtherGroupsAction.LABEL), 'View: Close Editors in Other Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorInAllGroupsAction, CloseEditorInAllGroupsAction.ID, CloseEditorInAllGroupsAction.LABEL), 'View: Close Editor in All Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorOrthogonalAction, SplitEditorOrthogonalAction.ID, SplitEditorOrthogonalAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH) }), 'View: Split Editor Orthogonal', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorLeftAction, SplitEditorLeftAction.ID, SplitEditorLeftAction.LABEL), 'View: Split Editor Left', category);
|
||||
@@ -332,7 +338,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, J
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(JoinAllGroupsAction, JoinAllGroupsAction.ID, JoinAllGroupsAction.LABEL), 'View: Join Editors of All Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ResetGroupSizesAction, ResetGroupSizesAction.ID, ResetGroupSizesAction.LABEL), 'View: Reset Editor Group Sizes', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Sidebar', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Side Bar', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Maximize Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category);
|
||||
@@ -361,8 +367,9 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupLeftActi
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupRightAction, NewEditorGroupRightAction.ID, NewEditorGroupRightAction.LABEL), 'View: New Editor Group to the Right', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupAboveAction, NewEditorGroupAboveAction.ID, NewEditorGroupAboveAction.LABEL), 'View: New Editor Group Above', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupBelowAction, NewEditorGroupBelowAction.ID, NewEditorGroupBelowAction.LABEL), 'View: New Editor Group Below', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateToLastEditLocationAction, NavigateToLastEditLocationAction.ID, NavigateToLastEditLocationAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_Q) }), 'Go to Last Edit Location');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLastAction, NavigateLastAction.ID, NavigateLastAction.LABEL), 'Go Last');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History');
|
||||
@@ -438,15 +445,15 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '5_split', order: 40 });
|
||||
|
||||
// Editor Title Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_INLINE_MODE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('toggleSideBySideView', "Toggle Side By Side View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
|
||||
interface IEditorToolItem { id: string; title: string; iconDark: string; iconLight: string; }
|
||||
|
||||
function appendEditorToolItem(primary: IEditorToolItem, alternative: IEditorToolItem, when: ContextKeyExpr, order: number): void {
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr, order: number, alternative?: IEditorToolItem): void {
|
||||
const item: IMenuItem = {
|
||||
command: {
|
||||
id: primary.id,
|
||||
title: primary.title,
|
||||
@@ -455,18 +462,23 @@ function appendEditorToolItem(primary: IEditorToolItem, alternative: IEditorTool
|
||||
light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconLight}`))
|
||||
}
|
||||
},
|
||||
alt: {
|
||||
group: 'navigation',
|
||||
when,
|
||||
order
|
||||
};
|
||||
|
||||
if (alternative) {
|
||||
item.alt = {
|
||||
id: alternative.id,
|
||||
title: alternative.title,
|
||||
iconLocation: {
|
||||
dark: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${alternative.iconDark}`)),
|
||||
light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${alternative.iconLight}`))
|
||||
}
|
||||
},
|
||||
group: 'navigation',
|
||||
when,
|
||||
order
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, item);
|
||||
}
|
||||
|
||||
// Editor Title Menu: Split Editor
|
||||
@@ -476,14 +488,15 @@ appendEditorToolItem(
|
||||
title: nls.localize('splitEditorRight', "Split Editor Right"),
|
||||
iconDark: 'split-editor-horizontal-inverse.svg',
|
||||
iconLight: 'split-editor-horizontal.svg'
|
||||
}, {
|
||||
},
|
||||
ContextKeyExpr.not('splitEditorsVertically'),
|
||||
100000, // towards the end
|
||||
{
|
||||
id: editorCommands.SPLIT_EDITOR_DOWN,
|
||||
title: nls.localize('splitEditorDown', "Split Editor Down"),
|
||||
iconDark: 'split-editor-vertical-inverse.svg',
|
||||
iconLight: 'split-editor-vertical.svg'
|
||||
},
|
||||
ContextKeyExpr.not('splitEditorsVertically'),
|
||||
100000 /* towards the end */
|
||||
}
|
||||
);
|
||||
|
||||
appendEditorToolItem(
|
||||
@@ -492,14 +505,15 @@ appendEditorToolItem(
|
||||
title: nls.localize('splitEditorDown', "Split Editor Down"),
|
||||
iconDark: 'split-editor-vertical-inverse.svg',
|
||||
iconLight: 'split-editor-vertical.svg'
|
||||
}, {
|
||||
},
|
||||
ContextKeyExpr.has('splitEditorsVertically'),
|
||||
100000, // towards the end
|
||||
{
|
||||
id: editorCommands.SPLIT_EDITOR_RIGHT,
|
||||
title: nls.localize('splitEditorRight', "Split Editor Right"),
|
||||
iconDark: 'split-editor-horizontal-inverse.svg',
|
||||
iconLight: 'split-editor-horizontal.svg'
|
||||
},
|
||||
ContextKeyExpr.has('splitEditorsVertically'),
|
||||
100000 // towards the end
|
||||
}
|
||||
);
|
||||
|
||||
// Editor Title Menu: Close Group (tabs disabled)
|
||||
@@ -509,14 +523,15 @@ appendEditorToolItem(
|
||||
title: nls.localize('close', "Close"),
|
||||
iconDark: 'close-big-inverse-alt.svg',
|
||||
iconLight: 'close-big-alt.svg'
|
||||
}, {
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.not('groupActiveEditorDirty')),
|
||||
1000000, // towards the far end
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
title: nls.localize('closeAll', "Close All"),
|
||||
iconDark: 'closeall-editors-inverse.svg',
|
||||
iconLight: 'closeall-editors.svg'
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.not('groupActiveEditorDirty')),
|
||||
1000000 // towards the end
|
||||
}
|
||||
);
|
||||
|
||||
appendEditorToolItem(
|
||||
@@ -525,22 +540,71 @@ appendEditorToolItem(
|
||||
title: nls.localize('close', "Close"),
|
||||
iconDark: 'close-dirty-inverse-alt.svg',
|
||||
iconLight: 'close-dirty-alt.svg'
|
||||
}, {
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.has('groupActiveEditorDirty')),
|
||||
1000000, // towards the far end
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
title: nls.localize('closeAll', "Close All"),
|
||||
iconDark: 'closeall-editors-inverse.svg',
|
||||
iconLight: 'closeall-editors.svg'
|
||||
}
|
||||
);
|
||||
|
||||
// Diff Editor Title Menu: Previous Change
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.GOTO_PREVIOUS_CHANGE,
|
||||
title: nls.localize('navigate.prev.label', "Previous Change"),
|
||||
iconDark: 'previous-diff-inverse.svg',
|
||||
iconLight: 'previous-diff.svg'
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.has('groupActiveEditorDirty')),
|
||||
1000000 // towards the end
|
||||
TextCompareEditorActiveContext,
|
||||
10
|
||||
);
|
||||
|
||||
// Diff Editor Title Menu: Next Change
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.GOTO_NEXT_CHANGE,
|
||||
title: nls.localize('navigate.next.label', "Next Change"),
|
||||
iconDark: 'next-diff-inverse.svg',
|
||||
iconLight: 'next-diff.svg'
|
||||
},
|
||||
TextCompareEditorActiveContext,
|
||||
11
|
||||
);
|
||||
|
||||
// Diff Editor Title Menu: Toggle Ignore Trim Whitespace (Enabled)
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
|
||||
title: nls.localize('ignoreTrimWhitespace.label', "Ignore Trim Whitespace"),
|
||||
iconDark: 'paragraph-inverse.svg',
|
||||
iconLight: 'paragraph.svg'
|
||||
},
|
||||
ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', true)),
|
||||
20
|
||||
);
|
||||
|
||||
// Diff Editor Title Menu: Toggle Ignore Trim Whitespace (Disabled)
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
|
||||
title: nls.localize('showTrimWhitespace.label', "Show Trim Whitespace"),
|
||||
iconDark: 'paragraph-disabled-inverse.svg',
|
||||
iconLight: 'paragraph-disabled.svg'
|
||||
},
|
||||
ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', false)),
|
||||
20
|
||||
);
|
||||
|
||||
// Editor Commands for Command Palette
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepEditor', "Keep Editor"), category }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeEditorsInGroup', "Close All Editors in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOtherEditors', "Close Other Editors in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRightEditors', "Close Editors to the Right in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: { value: nls.localize('keepEditor', "Keep Editor"), original: 'View: Keep Editor' }, category }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: { value: nls.localize('closeEditorsInGroup', "Close All Editors in Group"), original: 'View: Close All Editors in Group' }, category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: { value: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), original: 'View: Close Saved Editors in Group' }, category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: { value: nls.localize('closeOtherEditors', "Close Other Editors in Group"), original: 'View: Close Other Editors in Group' }, category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: { value: nls.localize('closeRightEditors', "Close Editors to the Right in Group"), original: 'View: Close Editors to the Right in Group' }, category } });
|
||||
|
||||
// File menu
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, {
|
||||
@@ -676,3 +740,177 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
},
|
||||
order: 9
|
||||
});
|
||||
|
||||
// Main Menu Bar Contributions:
|
||||
|
||||
// Forward/Back
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: '1_fwd_back',
|
||||
command: {
|
||||
id: 'workbench.action.navigateBack',
|
||||
title: nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back"),
|
||||
precondition: ContextKeyExpr.has('canNavigateBack')
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: '1_fwd_back',
|
||||
command: {
|
||||
id: 'workbench.action.navigateForward',
|
||||
title: nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward"),
|
||||
precondition: ContextKeyExpr.has('canNavigateForward')
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
// Switch Editor
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
|
||||
group: '1_any',
|
||||
command: {
|
||||
id: 'workbench.action.nextEditor',
|
||||
title: nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
|
||||
group: '1_any',
|
||||
command: {
|
||||
id: 'workbench.action.previousEditor',
|
||||
title: nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
|
||||
group: '2_used',
|
||||
command: {
|
||||
id: 'workbench.action.openNextRecentlyUsedEditorInGroup',
|
||||
title: nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
|
||||
group: '2_used',
|
||||
command: {
|
||||
id: 'workbench.action.openPreviousRecentlyUsedEditorInGroup',
|
||||
title: nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: '2_switch',
|
||||
title: nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor"),
|
||||
submenu: MenuId.MenubarSwitchEditorMenu,
|
||||
order: 1
|
||||
});
|
||||
|
||||
// Switch Group
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusFirstEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "Group &&1")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusSecondEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "Group &&2")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusThirdEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "Group &&3")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusFourthEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusFourthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&4")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusFifthEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusFifthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&5")
|
||||
},
|
||||
order: 5
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '2_next_prev',
|
||||
command: {
|
||||
id: 'workbench.action.focusNextGroup',
|
||||
title: nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '2_next_prev',
|
||||
command: {
|
||||
id: 'workbench.action.focusPreviousGroup',
|
||||
title: nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '3_directional',
|
||||
command: {
|
||||
id: 'workbench.action.focusLeftGroup',
|
||||
title: nls.localize({ key: 'miFocusLeftGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Left")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '3_directional',
|
||||
command: {
|
||||
id: 'workbench.action.focusRightGroup',
|
||||
title: nls.localize({ key: 'miFocusRightGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Right")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '3_directional',
|
||||
command: {
|
||||
id: 'workbench.action.focusAboveGroup',
|
||||
title: nls.localize({ key: 'miFocusAboveGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Above")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '3_directional',
|
||||
command: {
|
||||
id: 'workbench.action.focusBelowGroup',
|
||||
title: nls.localize({ key: 'miFocusBelowGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Below")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: '2_switch',
|
||||
title: nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group"),
|
||||
submenu: MenuId.MenubarSwitchGroupMenu,
|
||||
order: 2
|
||||
});
|
||||
@@ -3,9 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration, IWorkbenchEditorPartConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent } 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';
|
||||
@@ -30,6 +27,7 @@ export interface IEditorPartOptions extends IWorkbenchEditorPartConfiguration {
|
||||
|
||||
export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
|
||||
showTabs: true,
|
||||
highlightModifiedTabs: false,
|
||||
tabCloseButton: 'right',
|
||||
tabSizing: 'fit',
|
||||
showIcons: true,
|
||||
@@ -104,7 +102,7 @@ export interface IEditorGroupsAccessor {
|
||||
|
||||
export interface IEditorGroupView extends IDisposable, ISerializableView, IEditorGroup {
|
||||
readonly group: EditorGroup;
|
||||
readonly whenRestored: TPromise<void>;
|
||||
readonly whenRestored: Thenable<void>;
|
||||
readonly disposed: boolean;
|
||||
|
||||
readonly onDidFocus: Event<void>;
|
||||
@@ -118,8 +116,6 @@ export interface IEditorGroupView extends IDisposable, ISerializableView, IEdito
|
||||
setActive(isActive: boolean): void;
|
||||
setLabel(label: string): void;
|
||||
relayout(): void;
|
||||
|
||||
shutdown(): void;
|
||||
}
|
||||
|
||||
export function getActiveTextEditorOptions(group: IEditorGroup, expectedActiveEditor?: IEditorInput, presetOptions?: EditorOptions): EditorOptions {
|
||||
@@ -159,5 +155,5 @@ export interface EditorGroupsServiceImpl extends IEditorGroupsService {
|
||||
/**
|
||||
* A promise that resolves when groups have been restored.
|
||||
*/
|
||||
readonly whenRestored: TPromise<void>;
|
||||
readonly whenRestored: Thenable<void>;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
@@ -37,7 +35,7 @@ export class ExecuteCommandAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
return this.commandService.executeCommand(this.commandId, this.commandArgs);
|
||||
}
|
||||
}
|
||||
@@ -71,10 +69,10 @@ export class BaseSplitEditorAction extends Action {
|
||||
}));
|
||||
}
|
||||
|
||||
run(context?: IEditorIdentifier): TPromise<any> {
|
||||
run(context?: IEditorIdentifier): Promise<any> {
|
||||
splitEditor(this.editorGroupService, this.direction, context);
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -189,7 +187,7 @@ export class JoinTwoGroupsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(context?: IEditorIdentifier): TPromise<any> {
|
||||
run(context?: IEditorIdentifier): Promise<any> {
|
||||
let sourceGroup: IEditorGroup;
|
||||
if (context && typeof context.groupId === 'number') {
|
||||
sourceGroup = this.editorGroupService.getGroup(context.groupId);
|
||||
@@ -203,11 +201,11 @@ export class JoinTwoGroupsAction extends Action {
|
||||
if (targetGroup && sourceGroup !== targetGroup) {
|
||||
this.editorGroupService.mergeGroup(sourceGroup, targetGroup);
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,10 +222,10 @@ export class JoinAllGroupsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(context?: IEditorIdentifier): TPromise<any> {
|
||||
run(context?: IEditorIdentifier): Promise<any> {
|
||||
mergeAllGroups(this.editorGroupService);
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,11 +242,11 @@ export class NavigateBetweenGroupsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const nextGroup = this.editorGroupService.findGroup({ location: GroupLocation.NEXT }, this.editorGroupService.activeGroup, true);
|
||||
nextGroup.focus();
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,10 +263,10 @@ export class FocusActiveGroupAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,13 +281,13 @@ export abstract class BaseFocusGroupAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const group = this.editorGroupService.findGroup(this.scope, this.editorGroupService.activeGroup, true);
|
||||
if (group) {
|
||||
group.focus();
|
||||
}
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +423,7 @@ export class OpenToSideFromQuickOpenAction extends Action {
|
||||
this.class = (preferredDirection === GroupDirection.RIGHT) ? 'quick-open-sidebyside-vertical' : 'quick-open-sidebyside-horizontal';
|
||||
}
|
||||
|
||||
run(context: any): TPromise<any> {
|
||||
run(context: any): Thenable<any> {
|
||||
const entry = toEditorQuickOpenEntry(context);
|
||||
if (entry) {
|
||||
const input = entry.getInput();
|
||||
@@ -439,7 +437,7 @@ export class OpenToSideFromQuickOpenAction extends Action {
|
||||
return this.editorService.openEditor(resourceInput, SIDE_GROUP);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +472,7 @@ export class CloseEditorAction extends Action {
|
||||
super(id, label, 'close-editor-action');
|
||||
}
|
||||
|
||||
run(context?: IEditorCommandsContext): TPromise<any> {
|
||||
run(context?: IEditorCommandsContext): Promise<any> {
|
||||
return this.commandService.executeCommand(CLOSE_EDITOR_COMMAND_ID, void 0, context);
|
||||
}
|
||||
}
|
||||
@@ -492,7 +490,7 @@ export class CloseOneEditorAction extends Action {
|
||||
super(id, label, 'close-editor-action');
|
||||
}
|
||||
|
||||
run(context?: IEditorCommandsContext): TPromise<any> {
|
||||
run(context?: IEditorCommandsContext): Thenable<any> {
|
||||
let group: IEditorGroup;
|
||||
let editorIndex: number;
|
||||
if (context) {
|
||||
@@ -520,7 +518,7 @@ export class CloseOneEditorAction extends Action {
|
||||
return group.closeEditor(group.activeEditor);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,7 +535,7 @@ export class RevertAndCloseEditorAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const activeControl = this.editorService.activeControl;
|
||||
if (activeControl) {
|
||||
const editor = activeControl.input;
|
||||
@@ -553,7 +551,7 @@ export class RevertAndCloseEditorAction extends Action {
|
||||
});
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,13 +569,13 @@ export class CloseLeftEditorsInGroupAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(context?: IEditorIdentifier): TPromise<any> {
|
||||
run(context?: IEditorIdentifier): Thenable<any> {
|
||||
const { group, editor } = getTarget(this.editorService, this.editorGroupService, context);
|
||||
if (group && editor) {
|
||||
return group.closeEditors({ direction: CloseDirection.LEFT, except: editor });
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,7 +614,7 @@ export abstract class BaseCloseAllAction extends Action {
|
||||
return groupsToClose;
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
|
||||
// Just close all if there are no or one dirty editor
|
||||
if (this.textFileService.getDirty().length < 2) {
|
||||
@@ -629,7 +627,7 @@ export abstract class BaseCloseAllAction extends Action {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
let saveOrRevertPromise: TPromise<boolean>;
|
||||
let saveOrRevertPromise: Thenable<boolean>;
|
||||
if (confirm === ConfirmResult.DONT_SAVE) {
|
||||
saveOrRevertPromise = this.textFileService.revertAll(null, { soft: true }).then(() => true);
|
||||
} else {
|
||||
@@ -646,7 +644,7 @@ export abstract class BaseCloseAllAction extends Action {
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract doCloseAll(): TPromise<any>;
|
||||
protected abstract doCloseAll(): Thenable<any>;
|
||||
}
|
||||
|
||||
export class CloseAllEditorsAction extends BaseCloseAllAction {
|
||||
@@ -663,8 +661,8 @@ export class CloseAllEditorsAction extends BaseCloseAllAction {
|
||||
super(id, label, 'action-close-all-files', textFileService, editorGroupService);
|
||||
}
|
||||
|
||||
protected doCloseAll(): TPromise<any> {
|
||||
return TPromise.join(this.groupsToClose.map(g => g.closeAllEditors()));
|
||||
protected doCloseAll(): Promise<any> {
|
||||
return Promise.all(this.groupsToClose.map(g => g.closeAllEditors()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,8 +680,8 @@ export class CloseAllEditorGroupsAction extends BaseCloseAllAction {
|
||||
super(id, label, void 0, textFileService, editorGroupService);
|
||||
}
|
||||
|
||||
protected doCloseAll(): TPromise<any> {
|
||||
return TPromise.join(this.groupsToClose.map(g => g.closeAllEditors())).then(() => {
|
||||
protected doCloseAll(): Promise<any> {
|
||||
return Promise.all(this.groupsToClose.map(g => g.closeAllEditors())).then(() => {
|
||||
this.groupsToClose.forEach(group => this.editorGroupService.removeGroup(group));
|
||||
});
|
||||
}
|
||||
@@ -702,11 +700,11 @@ export class CloseEditorsInOtherGroupsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(context?: IEditorIdentifier): TPromise<any> {
|
||||
run(context?: IEditorIdentifier): Thenable<any> {
|
||||
const groupToSkip = context ? this.editorGroupService.getGroup(context.groupId) : this.editorGroupService.activeGroup;
|
||||
return TPromise.join(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => {
|
||||
return Promise.all(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => {
|
||||
if (g.id === groupToSkip.id) {
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return g.closeAllEditors();
|
||||
@@ -714,6 +712,30 @@ export class CloseEditorsInOtherGroupsAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseEditorInAllGroupsAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.closeEditorInAllGroups';
|
||||
static readonly LABEL = nls.localize('closeEditorInAllGroups', "Close Editor in All Groups");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorGroupsService private editorGroupService: IEditorGroupsService,
|
||||
@IEditorService private editorService: IEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Thenable<any> {
|
||||
const activeEditor = this.editorService.activeEditor;
|
||||
if (activeEditor) {
|
||||
return Promise.all(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => g.closeEditor(activeEditor)));
|
||||
}
|
||||
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseMoveGroupAction extends Action {
|
||||
|
||||
constructor(
|
||||
@@ -725,7 +747,7 @@ export class BaseMoveGroupAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(context?: IEditorIdentifier): TPromise<any> {
|
||||
run(context?: IEditorIdentifier): Promise<any> {
|
||||
let sourceGroup: IEditorGroup;
|
||||
if (context && typeof context.groupId === 'number') {
|
||||
sourceGroup = this.editorGroupService.getGroup(context.groupId);
|
||||
@@ -738,7 +760,7 @@ export class BaseMoveGroupAction extends Action {
|
||||
this.editorGroupService.moveGroup(sourceGroup, targetGroup, this.direction);
|
||||
}
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
private findTargetGroup(sourceGroup: IEditorGroup): IEditorGroup {
|
||||
@@ -834,10 +856,10 @@ export class MinimizeOtherGroupsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.editorGroupService.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS);
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,10 +872,10 @@ export class ResetGroupSizesAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.editorGroupService.arrangeGroups(GroupsArrangement.EVEN);
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,14 +894,13 @@ export class MaximizeGroupAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
if (this.editorService.activeEditor) {
|
||||
this.editorGroupService.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS);
|
||||
|
||||
return this.partService.setSideBarHidden(true);
|
||||
this.partService.setSideBarHidden(true);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,15 +915,15 @@ export abstract class BaseNavigateEditorAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const result = this.navigate();
|
||||
if (!result) {
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const { groupId, editor } = result;
|
||||
if (!editor) {
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const group = this.editorGroupService.getGroup(groupId);
|
||||
@@ -1081,10 +1102,10 @@ export class NavigateForwardAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.historyService.forward();
|
||||
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1097,10 +1118,26 @@ export class NavigateBackwardsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.historyService.back();
|
||||
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class NavigateToLastEditLocationAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.navigateToLastEditLocation';
|
||||
static readonly LABEL = nls.localize('navigateToLastEditLocation', "Go to Last Edit Location");
|
||||
|
||||
constructor(id: string, label: string, @IHistoryService private historyService: IHistoryService) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Thenable<any> {
|
||||
this.historyService.openLastEditLocation();
|
||||
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1113,10 +1150,10 @@ export class NavigateLastAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.historyService.last();
|
||||
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1133,10 +1170,10 @@ export class ReopenClosedEditorAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.historyService.reopenLastClosedEditor();
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1148,15 +1185,21 @@ export class ClearRecentFilesAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWindowsService private windowsService: IWindowsService
|
||||
@IWindowsService private windowsService: IWindowsService,
|
||||
@IHistoryService private historyService: IHistoryService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
|
||||
// Clear global recently opened
|
||||
this.windowsService.clearRecentlyOpened();
|
||||
|
||||
return TPromise.as(false);
|
||||
// Clear workspace specific recently opened
|
||||
this.historyService.clearRecentlyOpened();
|
||||
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1195,14 +1238,14 @@ export class BaseQuickOpenEditorInGroupAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const keys = this.keybindingService.lookupKeybindings(this.id);
|
||||
|
||||
|
||||
|
||||
this.quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX, { quickNavigateConfiguration: { keybindings: keys } });
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1250,12 +1293,12 @@ export class OpenPreviousEditorFromHistoryAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const keys = this.keybindingService.lookupKeybindings(this.id);
|
||||
|
||||
this.quickOpenService.show(null, { quickNavigateConfiguration: { keybindings: keys } });
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1268,10 +1311,10 @@ export class OpenNextRecentlyUsedEditorAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.historyService.forward(true);
|
||||
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1284,10 +1327,10 @@ export class OpenPreviousRecentlyUsedEditorAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.historyService.back(true);
|
||||
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1304,12 +1347,12 @@ export class ClearEditorHistoryAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
|
||||
// Editor history
|
||||
this.historyService.clear();
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1576,10 +1619,10 @@ export class BaseCreateEditorGroupAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
this.editorGroupService.addGroup(this.editorGroupService.activeGroup, this.direction, { activate: true });
|
||||
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,17 +12,16 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
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';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
|
||||
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
|
||||
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
|
||||
@@ -36,7 +35,11 @@ export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor';
|
||||
export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups';
|
||||
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
|
||||
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
|
||||
export const TOGGLE_DIFF_INLINE_MODE = 'toggle.diff.editorMode';
|
||||
|
||||
export const TOGGLE_DIFF_SIDE_BY_SIDE = 'toggle.diff.renderSideBySide';
|
||||
export const GOTO_NEXT_CHANGE = 'workbench.action.compareEditor.nextChange';
|
||||
export const GOTO_PREVIOUS_CHANGE = 'workbench.action.compareEditor.previousChange';
|
||||
export const TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE = 'toggle.diff.ignoreTrimWhitespace';
|
||||
|
||||
export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp';
|
||||
export const SPLIT_EDITOR_DOWN = 'workbench.action.splitEditorDown';
|
||||
@@ -46,6 +49,8 @@ export const SPLIT_EDITOR_RIGHT = 'workbench.action.splitEditorRight';
|
||||
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
|
||||
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';
|
||||
@@ -77,7 +82,7 @@ function registerActiveEditorMoveCommand(): void {
|
||||
id: MOVE_ACTIVE_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: EditorContextKeys.editorTextFocus,
|
||||
primary: null,
|
||||
primary: 0,
|
||||
handler: (accessor, args: any) => moveActiveEditor(args, accessor),
|
||||
description: {
|
||||
description: nls.localize('editorCommand.activeEditorMove.description', "Move the active editor by tabs or groups"),
|
||||
@@ -221,18 +226,18 @@ export function mergeAllGroups(editorGroupService: IEditorGroupsService): void {
|
||||
|
||||
function registerDiffEditorCommands(): void {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.compareEditor.nextChange',
|
||||
id: GOTO_NEXT_CHANGE,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: TextCompareEditorVisibleContext,
|
||||
primary: null,
|
||||
primary: KeyMod.Alt | KeyCode.F5,
|
||||
handler: accessor => navigateInDiffEditor(accessor, true)
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.compareEditor.previousChange',
|
||||
id: GOTO_PREVIOUS_CHANGE,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: TextCompareEditorVisibleContext,
|
||||
primary: null,
|
||||
primary: KeyMod.Alt | KeyMod.Shift | KeyCode.F5,
|
||||
handler: accessor => navigateInDiffEditor(accessor, false)
|
||||
});
|
||||
|
||||
@@ -245,27 +250,66 @@ function registerDiffEditorCommands(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDiffSideBySide(accessor: ServicesAccessor): void {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
const newValue = !configurationService.getValue<boolean>('diffEditor.renderSideBySide');
|
||||
configurationService.updateValue('diffEditor.renderSideBySide', newValue, ConfigurationTarget.USER);
|
||||
}
|
||||
|
||||
function toggleDiffIgnoreTrimWhitespace(accessor: ServicesAccessor): void {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
const newValue = !configurationService.getValue<boolean>('diffEditor.ignoreTrimWhitespace');
|
||||
configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue, ConfigurationTarget.USER);
|
||||
}
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: TOGGLE_DIFF_INLINE_MODE,
|
||||
id: TOGGLE_DIFF_SIDE_BY_SIDE,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
handler: accessor => toggleDiffSideBySide(accessor)
|
||||
});
|
||||
|
||||
const { control } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
|
||||
if (control instanceof TextDiffEditor) {
|
||||
const widget = control.getControl();
|
||||
const isInlineMode = !widget.renderSideBySide;
|
||||
widget.updateOptions(<IDiffEditorOptions>{
|
||||
renderSideBySide: isInlineMode
|
||||
});
|
||||
}
|
||||
}
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: TOGGLE_DIFF_SIDE_BY_SIDE,
|
||||
title: {
|
||||
value: nls.localize('toggleInlineView', "Toggle Inline View"),
|
||||
original: 'Compare: Toggle Inline View'
|
||||
},
|
||||
category: nls.localize('compare', "Compare")
|
||||
},
|
||||
when: ContextKeyExpr.has('textCompareEditorActive')
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: accessor => toggleDiffIgnoreTrimWhitespace(accessor)
|
||||
});
|
||||
}
|
||||
|
||||
function registerOpenEditorAtIndexCommands(): void {
|
||||
const openEditorAtIndex: ICommandHandler = (accessor: ServicesAccessor, editorIndex: number): void => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const activeControl = editorService.activeControl;
|
||||
if (activeControl) {
|
||||
const editor = activeControl.group.getEditor(editorIndex);
|
||||
if (editor) {
|
||||
editorService.openEditor(editor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This command takes in the editor index number to open as an argument
|
||||
CommandsRegistry.registerCommand({
|
||||
id: OPEN_EDITOR_AT_INDEX_COMMAND_ID,
|
||||
handler: openEditorAtIndex
|
||||
});
|
||||
|
||||
// Keybindings to focus a specific index in the tab folder if tabs are enabled
|
||||
for (let i = 0; i < 9; i++) {
|
||||
@@ -273,24 +317,12 @@ function registerOpenEditorAtIndexCommands(): void {
|
||||
const visibleIndex = i + 1;
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.openEditorAtIndex' + visibleIndex,
|
||||
id: OPEN_EDITOR_AT_INDEX_COMMAND_ID + visibleIndex,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: KeyMod.Alt | toKeyCode(visibleIndex),
|
||||
mac: { primary: KeyMod.WinCtrl | toKeyCode(visibleIndex) },
|
||||
handler: accessor => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
const activeControl = editorService.activeControl;
|
||||
if (activeControl) {
|
||||
const editor = activeControl.group.getEditor(editorIndex);
|
||||
if (editor) {
|
||||
return editorService.openEditor(editor).then(() => void 0);
|
||||
}
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
handler: accessor => openEditorAtIndex(accessor, editorIndex)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -434,7 +466,7 @@ function registerCloseEditorCommands() {
|
||||
contexts.push({ groupId: activeGroup.id }); // active group as fallback
|
||||
}
|
||||
|
||||
return TPromise.join(distinct(contexts.map(c => c.groupId)).map(groupId =>
|
||||
return Promise.all(distinct(contexts.map(c => c.groupId)).map(groupId =>
|
||||
editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
|
||||
));
|
||||
}
|
||||
@@ -454,7 +486,7 @@ function registerCloseEditorCommands() {
|
||||
distinctGroupIds.push(editorGroupService.activeGroup.id);
|
||||
}
|
||||
|
||||
return TPromise.join(distinctGroupIds.map(groupId =>
|
||||
return Promise.all(distinctGroupIds.map(groupId =>
|
||||
editorGroupService.getGroup(groupId).closeAllEditors()
|
||||
));
|
||||
}
|
||||
@@ -477,7 +509,7 @@ function registerCloseEditorCommands() {
|
||||
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
|
||||
return TPromise.join(groupIds.map(groupId => {
|
||||
return Promise.all(groupIds.map(groupId => {
|
||||
const group = editorGroupService.getGroup(groupId);
|
||||
const editors = contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
@@ -526,7 +558,7 @@ function registerCloseEditorCommands() {
|
||||
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
|
||||
return TPromise.join(groupIds.map(groupId => {
|
||||
return Promise.all(groupIds.map(groupId => {
|
||||
const group = editorGroupService.getGroup(groupId);
|
||||
const editors = contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
@@ -551,7 +583,7 @@ function registerCloseEditorCommands() {
|
||||
return group.closeEditors({ direction: CloseDirection.RIGHT, except: editor });
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -568,7 +600,7 @@ function registerCloseEditorCommands() {
|
||||
return group.pinEditor(editor);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -647,7 +679,7 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
|
||||
|
||||
// First check for a focused list to return the selected items from
|
||||
const list = listService.lastFocusedList;
|
||||
if (list instanceof List && list.isDOMFocused()) {
|
||||
if (list instanceof List && list.getHTMLElement() === document.activeElement) {
|
||||
const elementToContext = (element: IEditorIdentifier | IEditorGroup) => {
|
||||
if (isEditorGroup(element)) {
|
||||
return { groupId: element.id, editorIndex: void 0 };
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { dispose, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { Dimension, show, hide, addClass } from 'vs/base/browser/dom';
|
||||
@@ -15,7 +13,6 @@ import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
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 { toWinJsPromise } from 'vs/base/common/async';
|
||||
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';
|
||||
|
||||
@@ -171,11 +168,11 @@ 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.isCreated() ? 800 : 3200);
|
||||
const operation = this.editorOperation.start(this.partService.isRestored() ? 800 : 3200);
|
||||
|
||||
// Call into editor control
|
||||
const editorWillChange = !inputMatches;
|
||||
return toWinJsPromise(control.setInput(editor, options, operation.token)).then(() => {
|
||||
return TPromise.wrap(control.setInput(editor, options, operation.token)).then(() => {
|
||||
|
||||
// Focus (unless prevented or another operation is running)
|
||||
if (operation.isCurrent()) {
|
||||
@@ -233,15 +230,9 @@ export class EditorControl extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
|
||||
// Forward to all editor controls
|
||||
this.controls.forEach(editor => editor.shutdown());
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.activeControlDisposeables = dispose(this.activeControlDisposeables);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/editordroptarget';
|
||||
import { LocalSelectionTransfer, DraggedEditorIdentifier, ResourcesDropHandler, DraggedEditorGroupIdentifier, DragAndDropObserver } from 'vs/workbench/browser/dnd';
|
||||
import { addDisposableListener, EventType, EventHelper, isAncestor, toggleClass, addClass, removeClass } from 'vs/base/browser/dom';
|
||||
@@ -17,6 +15,7 @@ import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
interface IDropOperation {
|
||||
splitDirection?: GroupDirection;
|
||||
@@ -32,6 +31,8 @@ class DropOverlay extends Themable {
|
||||
private currentDropOperation: IDropOperation;
|
||||
private _disposed: boolean;
|
||||
|
||||
private cleanupOverlayScheduler: RunOnceScheduler;
|
||||
|
||||
private readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
|
||||
private readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
|
||||
|
||||
@@ -43,6 +44,8 @@ class DropOverlay extends Themable {
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.cleanupOverlayScheduler = this._register(new RunOnceScheduler(() => this.dispose(), 300));
|
||||
|
||||
this.create();
|
||||
}
|
||||
|
||||
@@ -118,6 +121,11 @@ class DropOverlay extends Themable {
|
||||
|
||||
// Position overlay
|
||||
this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup);
|
||||
|
||||
// Make sure to stop any running cleanup scheduler to remove the overlay
|
||||
if (this.cleanupOverlayScheduler.isScheduled()) {
|
||||
this.cleanupOverlayScheduler.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
onDragLeave: e => this.dispose(),
|
||||
@@ -144,9 +152,9 @@ class DropOverlay extends Themable {
|
||||
// To protect against this issue we always destroy the overlay as soon as we detect a
|
||||
// mouse event over it. The delay is used to guarantee we are not interfering with the
|
||||
// actual DROP event that can also trigger a mouse over event.
|
||||
setTimeout(() => {
|
||||
this.dispose();
|
||||
}, 300);
|
||||
if (!this.cleanupOverlayScheduler.isScheduled()) {
|
||||
this.cleanupOverlayScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/editorgroupview';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
@@ -26,7 +24,7 @@ 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, isErrorWithActions, IErrorWithActions } from 'vs/base/common/errors';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Severity, INotificationService, INotificationActions } from 'vs/platform/notification/common/notification';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
@@ -49,6 +47,9 @@ 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';
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
import { isErrorWithActions, IErrorWithActions } from 'vs/base/common/errorsWithActions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
@@ -99,7 +100,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
private active: boolean;
|
||||
private dimension: Dimension;
|
||||
|
||||
private _whenRestored: TPromise<void>;
|
||||
private _whenRestored: Thenable<void>;
|
||||
private isRestored: boolean;
|
||||
|
||||
private scopedInstantiationService: IInstantiationService;
|
||||
@@ -250,7 +251,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
if (this.isEmpty()) {
|
||||
EventHelper.stop(e);
|
||||
// {{SQL CARBON EDIT}}
|
||||
this.commandService.executeCommand(GlobalNewUntitledFileAction.ID).done(undefined, err => this.notificationService.warn(err));
|
||||
this.commandService.executeCommand(GlobalNewUntitledFileAction.ID).then(undefined, err => this.notificationService.warn(err));
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -313,8 +314,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
// Show it
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => TPromise.as(actions),
|
||||
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
|
||||
getActions: () => actions,
|
||||
onHide: () => this.focus()
|
||||
});
|
||||
}
|
||||
@@ -404,9 +404,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
}
|
||||
|
||||
private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): TPromise<void> {
|
||||
private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): Thenable<void> {
|
||||
if (this._group.count === 0) {
|
||||
return TPromise.as(void 0); // nothing to show
|
||||
return Promise.resolve(void 0); // nothing to show
|
||||
}
|
||||
|
||||
// Determine editor options
|
||||
@@ -421,11 +421,16 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
options.pinned = this._group.isPinned(activeEditor); // preserve pinned state
|
||||
options.preserveFocus = true; // handle focus after editor is opened
|
||||
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
// Show active editor
|
||||
return this.doShowEditor(activeEditor, true, options).then(() => {
|
||||
|
||||
// Set focused now if this is the active group
|
||||
if (this.accessor.activeGroup === this) {
|
||||
// Set focused now if this is the active group and focus has
|
||||
// not changed meanwhile. This prevents focus from being
|
||||
// stolen accidentally on startup when the user already
|
||||
// clicked somewhere.
|
||||
if (this.accessor.activeGroup === this && activeElement === document.activeElement) {
|
||||
this.focus();
|
||||
}
|
||||
});
|
||||
@@ -604,7 +609,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
get whenRestored(): TPromise<void> {
|
||||
get whenRestored(): Thenable<void> {
|
||||
return this._whenRestored;
|
||||
}
|
||||
|
||||
@@ -728,6 +733,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
openEditor(editor: EditorInput, options?: EditorOptions): TPromise<void> {
|
||||
|
||||
// Guard against invalid inputs
|
||||
if (!editor) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
// Editor opening event allows for prevention
|
||||
const event = new EditorOpeningEvent(this._group.id, editor, options);
|
||||
this._onWillOpenEditor.fire(event);
|
||||
@@ -764,13 +774,21 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this.accessor.activateGroup(this);
|
||||
}
|
||||
|
||||
// Actually move the editor if a specific index is provided and we figure
|
||||
// out that the editor is already opened at a different index. This
|
||||
// ensures the right set of events are fired to the outside.
|
||||
if (typeof openEditorOptions.index === 'number') {
|
||||
const indexOfEditor = this._group.indexOf(editor);
|
||||
if (indexOfEditor !== -1 && indexOfEditor !== openEditorOptions.index) {
|
||||
this.doMoveEditorInsideGroup(editor, openEditorOptions);
|
||||
}
|
||||
}
|
||||
|
||||
// Update model
|
||||
this._group.openEditor(editor, openEditorOptions);
|
||||
|
||||
// Show editor
|
||||
const showEditorResult = this.doShowEditor(editor, openEditorOptions.active, options);
|
||||
|
||||
return showEditorResult;
|
||||
return this.doShowEditor(editor, openEditorOptions.active, options);
|
||||
}
|
||||
|
||||
private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): TPromise<void> {
|
||||
@@ -846,7 +864,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
const startingIndex = this.getIndexOfEditor(editor) + 1;
|
||||
|
||||
// Open the other ones inactive
|
||||
return TPromise.join(editors.map(({ editor, options }, index) => {
|
||||
return Promise.all(editors.map(({ editor, options }, index) => {
|
||||
const adjustedEditorOptions = options || new EditorOptions();
|
||||
adjustedEditorOptions.inactive = true;
|
||||
adjustedEditorOptions.pinned = true;
|
||||
@@ -963,13 +981,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this.doCloseInactiveEditor(editor);
|
||||
}
|
||||
|
||||
// Forward to title control & breadcrumbs
|
||||
// Forward to title control
|
||||
this.titleAreaControl.closeEditor(editor);
|
||||
}
|
||||
|
||||
private doCloseActiveEditor(focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
|
||||
const editorToClose = this.activeEditor;
|
||||
const editorHasFocus = isAncestor(document.activeElement, this.element);
|
||||
const restoreFocus = this.shouldRestoreFocus(this.element);
|
||||
|
||||
// Optimization: if we are about to close the last editor in this group and settings
|
||||
// are configured to close the group since it will be empty, we first set the last
|
||||
@@ -983,7 +1001,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
const mostRecentlyActiveGroups = this.accessor.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE);
|
||||
const nextActiveGroup = mostRecentlyActiveGroups[1]; // [0] will be the current one, so take [1]
|
||||
if (nextActiveGroup) {
|
||||
if (editorHasFocus) {
|
||||
if (restoreFocus) {
|
||||
nextActiveGroup.focus();
|
||||
} else {
|
||||
this.accessor.activateGroup(nextActiveGroup);
|
||||
@@ -1020,7 +1038,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this.editorControl.closeEditor(editorToClose);
|
||||
|
||||
// Restore focus to group container as needed unless group gets closed
|
||||
if (editorHasFocus && !closeEmptyGroup) {
|
||||
if (restoreFocus && !closeEmptyGroup) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
@@ -1034,6 +1052,17 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
}
|
||||
|
||||
private shouldRestoreFocus(target: Element): boolean {
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
if (activeElement === document.body) {
|
||||
return true; // always restore focus if nothing is focused currently
|
||||
}
|
||||
|
||||
// otherwise check for the active element being an ancestor of the target
|
||||
return isAncestor(activeElement, target);
|
||||
}
|
||||
|
||||
private doCloseInactiveEditor(editor: EditorInput) {
|
||||
|
||||
// Update model
|
||||
@@ -1368,10 +1397,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
//#endregion
|
||||
|
||||
shutdown(): void {
|
||||
this.editorControl.shutdown();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposed = true;
|
||||
|
||||
@@ -1427,7 +1452,7 @@ registerThemingParticipant((theme, collector, environment) => {
|
||||
const letterpress = `resources/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('${join(environment.appRoot, letterpress)}')
|
||||
background-image: url('${URI.file(join(environment.appRoot, letterpress)).toString()}')
|
||||
}
|
||||
`);
|
||||
|
||||
|
||||
@@ -3,20 +3,18 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/workbench/browser/parts/editor/editor.contribution';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { Dimension, isAncestor, toggleClass, addClass, $ } from 'vs/base/browser/dom';
|
||||
import { Event, Emitter, once, Relay, anyEvent } from 'vs/base/common/event';
|
||||
import { contrastBorder, editorBackground, registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
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, ISerializedNode, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid';
|
||||
import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid';
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { EDITOR_GROUP_BORDER } from 'vs/workbench/common/theme';
|
||||
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 { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
|
||||
@@ -24,15 +22,14 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co
|
||||
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { Scope } from 'vs/workbench/common/memento';
|
||||
import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { TValueCallback, TPromise } from 'vs/base/common/winjs.base';
|
||||
import { always } from 'vs/base/common/async';
|
||||
import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTarget';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout';
|
||||
import { IView, orthogonal } from 'vs/base/browser/ui/grid/gridview';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
|
||||
@@ -86,12 +83,6 @@ class GridWidgetView<T extends IView> implements IView {
|
||||
}
|
||||
}
|
||||
|
||||
export const EDITOR_PANE_BACKGROUND = registerColor('editorPane.background', {
|
||||
dark: editorBackground,
|
||||
light: editorBackground,
|
||||
hc: editorBackground
|
||||
}, localize('editorPaneBackground', "Background color of the editor pane visible on the left and right side of the centered editor layout."));
|
||||
|
||||
export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditorGroupsAccessor {
|
||||
|
||||
_serviceBrand: any;
|
||||
@@ -128,7 +119,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
private dimension: Dimension;
|
||||
private _preferredSize: Dimension;
|
||||
|
||||
private memento: object;
|
||||
private workspaceMemento: object;
|
||||
private globalMemento: object;
|
||||
|
||||
private _partOptions: IEditorPartOptions;
|
||||
@@ -142,8 +133,8 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
private gridWidget: SerializableGrid<IEditorGroupView>;
|
||||
private gridWidgetView: GridWidgetView<IEditorGroupView>;
|
||||
|
||||
private _whenRestored: TPromise<void>;
|
||||
private whenRestoredComplete: TValueCallback<void>;
|
||||
private _whenRestored: Thenable<void>;
|
||||
private whenRestoredResolve: () => void;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -151,19 +142,18 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IStorageService private storageService: IStorageService
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService);
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
|
||||
this.gridWidgetView = new GridWidgetView<IEditorGroupView>();
|
||||
|
||||
this._partOptions = getEditorPartOptions(this.configurationService.getValue<IWorkbenchEditorConfiguration>());
|
||||
this.memento = this.getMemento(this.storageService, Scope.WORKSPACE);
|
||||
this.globalMemento = this.getMemento(this.storageService, Scope.GLOBAL);
|
||||
|
||||
this._whenRestored = new TPromise(resolve => {
|
||||
this.whenRestoredComplete = resolve;
|
||||
});
|
||||
this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE);
|
||||
this.globalMemento = this.getMemento(StorageScope.GLOBAL);
|
||||
|
||||
this._whenRestored = new Promise(resolve => (this.whenRestoredResolve = resolve));
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
@@ -236,7 +226,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
return this.gridWidget.orientation === Orientation.VERTICAL ? GroupOrientation.VERTICAL : GroupOrientation.HORIZONTAL;
|
||||
}
|
||||
|
||||
get whenRestored(): TPromise<void> {
|
||||
get whenRestored(): Thenable<void> {
|
||||
return this._whenRestored;
|
||||
}
|
||||
|
||||
@@ -376,7 +366,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
|
||||
applyLayout(layout: EditorGroupLayout): void {
|
||||
const gridHasFocus = isAncestor(document.activeElement, this.container);
|
||||
const restoreFocus = this.shouldRestoreFocus(this.container);
|
||||
|
||||
// Determine how many groups we need overall
|
||||
let layoutGroupsCount = 0;
|
||||
@@ -429,19 +419,33 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
// Mark preferred size as changed
|
||||
this.resetPreferredSize();
|
||||
|
||||
// Events for groupd that got added
|
||||
// Events for groups that got added
|
||||
this.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(groupView => {
|
||||
if (currentGroupViews.indexOf(groupView) === -1) {
|
||||
this._onDidAddGroup.fire(groupView);
|
||||
}
|
||||
});
|
||||
|
||||
// Update labels
|
||||
this.updateGroupLabels();
|
||||
|
||||
// Restore focus as needed
|
||||
if (gridHasFocus) {
|
||||
if (restoreFocus) {
|
||||
this._activeGroup.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private shouldRestoreFocus(target: Element): boolean {
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
if (activeElement === document.body) {
|
||||
return true; // always restore focus if nothing is focused currently
|
||||
}
|
||||
|
||||
// otherwise check for the active element being an ancestor of the target
|
||||
return isAncestor(activeElement, target);
|
||||
}
|
||||
|
||||
private isTwoDimensionalGrid(): boolean {
|
||||
const views = this.gridWidget.getViews();
|
||||
if (isGridBranchNode(views)) {
|
||||
@@ -485,6 +489,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
// Event
|
||||
this._onDidAddGroup.fire(newGroupView);
|
||||
|
||||
// Update labels
|
||||
this.updateGroupLabels();
|
||||
|
||||
return newGroupView;
|
||||
}
|
||||
|
||||
@@ -622,7 +629,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
|
||||
private doRemoveEmptyGroup(groupView: IEditorGroupView): void {
|
||||
const gridHasFocus = isAncestor(document.activeElement, this.container);
|
||||
const restoreFocus = this.shouldRestoreFocus(this.container);
|
||||
|
||||
// Activate next group if the removed one was active
|
||||
if (this._activeGroup === groupView) {
|
||||
@@ -637,16 +644,12 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
|
||||
// Restore focus if we had it previously (we run this after gridWidget.removeView() is called
|
||||
// because removing a view can mean to reparent it and thus focus would be removed otherwise)
|
||||
if (gridHasFocus) {
|
||||
if (restoreFocus) {
|
||||
this._activeGroup.focus();
|
||||
}
|
||||
|
||||
// Update labels: since our labels are created using the index of the
|
||||
// group, removing a group might produce gaps. So we iterate over all
|
||||
// groups and reassign the label based on the index.
|
||||
this.getGroups(GroupsOrder.CREATION_TIME).forEach((group, index) => {
|
||||
group.setLabel(this.getGroupLabel(index + 1));
|
||||
});
|
||||
// Update labels
|
||||
this.updateGroupLabels();
|
||||
|
||||
// Update container
|
||||
this.updateContainer();
|
||||
@@ -658,10 +661,6 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
this._onDidRemoveGroup.fire(groupView);
|
||||
}
|
||||
|
||||
private getGroupLabel(index: number): string {
|
||||
return localize('groupLabel', "Group {0}", index);
|
||||
}
|
||||
|
||||
moveGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView {
|
||||
const sourceView = this.assertGroupView(group);
|
||||
const targetView = this.assertGroupView(location);
|
||||
@@ -670,15 +669,14 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
throw new Error('Cannot move group into its own');
|
||||
}
|
||||
|
||||
const groupHasFocus = isAncestor(document.activeElement, sourceView.element);
|
||||
const restoreFocus = this.shouldRestoreFocus(sourceView.element);
|
||||
|
||||
// Move is a simple remove and add of the same view
|
||||
this.gridWidget.removeView(sourceView, Sizing.Distribute);
|
||||
this.gridWidget.addView(sourceView, Sizing.Distribute, targetView, this.toGridViewDirection(direction));
|
||||
// Move through grid widget API
|
||||
this.gridWidget.moveView(sourceView, Sizing.Distribute, targetView, this.toGridViewDirection(direction));
|
||||
|
||||
// Restore focus if we had it previously (we run this after gridWidget.removeView() is called
|
||||
// because removing a view can mean to reparent it and thus focus would be removed otherwise)
|
||||
if (groupHasFocus) {
|
||||
if (restoreFocus) {
|
||||
sourceView.focus();
|
||||
}
|
||||
|
||||
@@ -692,13 +690,13 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
const groupView = this.assertGroupView(group);
|
||||
const locationView = this.assertGroupView(location);
|
||||
|
||||
const groupHasFocus = isAncestor(document.activeElement, groupView.element);
|
||||
const restoreFocus = this.shouldRestoreFocus(groupView.element);
|
||||
|
||||
// Copy the group view
|
||||
const copiedGroupView = this.doAddGroup(locationView, direction, groupView);
|
||||
|
||||
// Restore focus if we had it
|
||||
if (groupHasFocus) {
|
||||
if (restoreFocus) {
|
||||
copiedGroupView.focus();
|
||||
}
|
||||
|
||||
@@ -712,7 +710,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
// Move/Copy editors over into target
|
||||
let index = (options && typeof options.index === 'number') ? options.index : targetView.count;
|
||||
sourceView.editors.forEach(editor => {
|
||||
const inactive = !sourceView.isActive(editor);
|
||||
const inactive = !sourceView.isActive(editor) || this._activeGroup !== sourceView;
|
||||
const copyOptions: ICopyEditorOptions = { index, inactive, preserveFocus: inactive };
|
||||
|
||||
if (options && options.mode === MergeGroupMode.COPY_EDITORS) {
|
||||
@@ -802,6 +800,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
|
||||
centerLayout(active: boolean): void {
|
||||
this.centeredLayoutWidget.activate(active);
|
||||
this._activeGroup.focus();
|
||||
}
|
||||
|
||||
isLayoutCentered(): boolean {
|
||||
@@ -825,27 +824,47 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
|
||||
// Signal restored
|
||||
always(TPromise.join(this.groups.map(group => group.whenRestored)), () => this.whenRestoredComplete(void 0));
|
||||
always(Promise.all(this.groups.map(group => group.whenRestored)), () => this.whenRestoredResolve());
|
||||
|
||||
// Update container
|
||||
this.updateContainer();
|
||||
}
|
||||
|
||||
private doCreateGridControlWithPreviousState(): void {
|
||||
const uiState = this.doGetPreviousState();
|
||||
const uiState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState;
|
||||
if (uiState && uiState.serializedGrid) {
|
||||
try {
|
||||
|
||||
// MRU
|
||||
this.mostRecentActiveGroups = uiState.mostRecentActiveGroups;
|
||||
// MRU
|
||||
this.mostRecentActiveGroups = uiState.mostRecentActiveGroups;
|
||||
|
||||
// Grid Widget
|
||||
this.doCreateGridControlWithState(uiState.serializedGrid, uiState.activeGroup);
|
||||
// Grid Widget
|
||||
this.doCreateGridControlWithState(uiState.serializedGrid, uiState.activeGroup);
|
||||
|
||||
// Ensure last active group has focus
|
||||
this._activeGroup.focus();
|
||||
// Ensure last active group has focus
|
||||
this._activeGroup.focus();
|
||||
} catch (error) {
|
||||
this.handleGridRestoreError(error, uiState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = void 0;
|
||||
this.mostRecentActiveGroups = [];
|
||||
}
|
||||
|
||||
private doCreateGridControlWithState(serializedGrid: ISerializedGrid, activeGroupId: GroupIdentifier, editorGroupViewsToReuse?: IEditorGroupView[]): void {
|
||||
|
||||
// Determine group views to reuse if any
|
||||
@@ -857,8 +876,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
|
||||
// Create new
|
||||
const groupViews: IEditorGroupView[] = [];
|
||||
const gridWidget = SerializableGrid.deserialize(serializedGrid, {
|
||||
fromJSON: (serializedEditorGroup: ISerializedEditorGroup) => {
|
||||
fromJSON: (serializedEditorGroup: ISerializedEditorGroup | null) => {
|
||||
let groupView: IEditorGroupView;
|
||||
if (reuseGroupViews.length > 0) {
|
||||
groupView = reuseGroupViews.shift();
|
||||
@@ -866,6 +886,8 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
groupView = this.doCreateGroupView(serializedEditorGroup);
|
||||
}
|
||||
|
||||
groupViews.push(groupView);
|
||||
|
||||
if (groupView.id === activeGroupId) {
|
||||
this.doSetGroupActive(groupView);
|
||||
}
|
||||
@@ -874,6 +896,18 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
}, { styles: { separatorBorder: this.gridSeparatorBorder } });
|
||||
|
||||
// If the active group was not found when restoring the grid
|
||||
// make sure to make at least one group active. We always need
|
||||
// an active group.
|
||||
if (!this._activeGroup) {
|
||||
this.doSetGroupActive(groupViews[0]);
|
||||
}
|
||||
|
||||
// Validate MRU group views matches grid widget state
|
||||
if (this.mostRecentActiveGroups.some(groupId => !this.getGroup(groupId))) {
|
||||
this.mostRecentActiveGroups = groupViews.map(group => group.id);
|
||||
}
|
||||
|
||||
// Set it
|
||||
this.doSetGridWidget(gridWidget);
|
||||
}
|
||||
@@ -893,127 +927,25 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
this.onDidSetGridWidget.fire();
|
||||
}
|
||||
|
||||
private doGetPreviousState(): IEditorPartUIState {
|
||||
const legacyState = this.doGetPreviousLegacyState();
|
||||
if (legacyState) {
|
||||
return legacyState; // TODO@ben remove after a while
|
||||
}
|
||||
|
||||
return this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState;
|
||||
}
|
||||
|
||||
private doGetPreviousLegacyState(): IEditorPartUIState {
|
||||
const LEGACY_EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.uiState';
|
||||
const LEGACY_STACKS_MODEL_STORAGE_KEY = 'editorStacks.model';
|
||||
|
||||
interface ILegacyEditorPartUIState {
|
||||
ratio: number[];
|
||||
groupOrientation: 'vertical' | 'horizontal';
|
||||
}
|
||||
|
||||
interface ISerializedLegacyEditorStacksModel {
|
||||
groups: ISerializedEditorGroup[];
|
||||
active: number;
|
||||
}
|
||||
|
||||
let legacyUIState: ISerializedLegacyEditorStacksModel;
|
||||
const legacyUIStateRaw = this.storageService.get(LEGACY_STACKS_MODEL_STORAGE_KEY, StorageScope.WORKSPACE);
|
||||
if (legacyUIStateRaw) {
|
||||
try {
|
||||
legacyUIState = JSON.parse(legacyUIStateRaw);
|
||||
} catch (error) { /* ignore */ }
|
||||
}
|
||||
|
||||
if (legacyUIState) {
|
||||
this.storageService.remove(LEGACY_STACKS_MODEL_STORAGE_KEY, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
const legacyPartState = this.memento[LEGACY_EDITOR_PART_UI_STATE_STORAGE_KEY] as ILegacyEditorPartUIState;
|
||||
if (legacyPartState) {
|
||||
delete this.memento[LEGACY_EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
}
|
||||
|
||||
if (legacyUIState && Array.isArray(legacyUIState.groups) && legacyUIState.groups.length > 0) {
|
||||
const splitHorizontally = legacyPartState && legacyPartState.groupOrientation === 'horizontal';
|
||||
|
||||
const legacyState: IEditorPartUIState = Object.create(null);
|
||||
|
||||
const positionOneGroup = legacyUIState.groups[0];
|
||||
const positionTwoGroup = legacyUIState.groups[1];
|
||||
const positionThreeGroup = legacyUIState.groups[2];
|
||||
|
||||
legacyState.activeGroup = legacyUIState.active;
|
||||
legacyState.mostRecentActiveGroups = [legacyUIState.active];
|
||||
|
||||
if (positionTwoGroup || positionThreeGroup) {
|
||||
if (!positionThreeGroup) {
|
||||
legacyState.mostRecentActiveGroups.push(legacyState.activeGroup === 0 ? 1 : 0);
|
||||
} else {
|
||||
if (legacyState.activeGroup === 0) {
|
||||
legacyState.mostRecentActiveGroups.push(1, 2);
|
||||
} else if (legacyState.activeGroup === 1) {
|
||||
legacyState.mostRecentActiveGroups.push(0, 2);
|
||||
} else {
|
||||
legacyState.mostRecentActiveGroups.push(0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const toNode = function (group: ISerializedEditorGroup, size: number): ISerializedNode {
|
||||
return {
|
||||
data: group,
|
||||
size,
|
||||
type: 'leaf'
|
||||
};
|
||||
};
|
||||
|
||||
const baseSize = 1200; // just some number because layout() was not called yet, but we only need the proportions
|
||||
|
||||
// No split editor
|
||||
if (!positionTwoGroup) {
|
||||
legacyState.serializedGrid = {
|
||||
width: baseSize,
|
||||
height: baseSize,
|
||||
orientation: splitHorizontally ? Orientation.VERTICAL : Orientation.HORIZONTAL,
|
||||
root: toNode(positionOneGroup, baseSize)
|
||||
};
|
||||
}
|
||||
|
||||
// Split editor (2 or 3 columns)
|
||||
else {
|
||||
const children: ISerializedNode[] = [];
|
||||
|
||||
const size = positionThreeGroup ? baseSize / 3 : baseSize / 2;
|
||||
|
||||
children.push(toNode(positionOneGroup, size));
|
||||
children.push(toNode(positionTwoGroup, size));
|
||||
|
||||
if (positionThreeGroup) {
|
||||
children.push(toNode(positionThreeGroup, size));
|
||||
}
|
||||
|
||||
legacyState.serializedGrid = {
|
||||
width: baseSize,
|
||||
height: baseSize,
|
||||
orientation: splitHorizontally ? Orientation.VERTICAL : Orientation.HORIZONTAL,
|
||||
root: {
|
||||
data: children,
|
||||
size: baseSize,
|
||||
type: 'branch'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return legacyState;
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
private updateContainer(): void {
|
||||
toggleClass(this.container, 'empty', this.isEmpty());
|
||||
}
|
||||
|
||||
private updateGroupLabels(): void {
|
||||
|
||||
// Since our labels are created using the index of the
|
||||
// group, adding/removing a group might produce gaps.
|
||||
// So we iterate over all groups and reassign the label
|
||||
// based on the index.
|
||||
this.getGroups(GroupsOrder.GRID_APPEARANCE).forEach((group, index) => {
|
||||
group.setLabel(this.getGroupLabel(index + 1));
|
||||
});
|
||||
}
|
||||
|
||||
private getGroupLabel(index: number): string {
|
||||
return localize('groupLabel', "Group {0}", index);
|
||||
}
|
||||
|
||||
private isEmpty(): boolean {
|
||||
return this.groupViews.size === 1 && this._activeGroup.isEmpty();
|
||||
}
|
||||
@@ -1041,7 +973,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
//this.editorGroupsControl.refreshTitles();
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
|
||||
// Persist grid UI state
|
||||
if (this.gridWidget) {
|
||||
@@ -1052,19 +984,21 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
};
|
||||
|
||||
if (this.isEmpty()) {
|
||||
delete this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
delete this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
} else {
|
||||
this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = uiState;
|
||||
this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = uiState;
|
||||
}
|
||||
}
|
||||
|
||||
// Persist centered view state
|
||||
this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = this.centeredLayoutWidget.state;
|
||||
const centeredLayoutState = this.centeredLayoutWidget.state;
|
||||
if (this.centeredLayoutWidget.isDefault(centeredLayoutState)) {
|
||||
delete this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY];
|
||||
} else {
|
||||
this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = centeredLayoutState;
|
||||
}
|
||||
|
||||
// Forward to all groups
|
||||
this.groupViews.forEach(group => group.shutdown());
|
||||
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/editorpicker';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import URI from 'vs/base/common/uri';
|
||||
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';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { getIconClasses } from 'vs/workbench/browser/labels';
|
||||
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';
|
||||
@@ -20,6 +18,7 @@ import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'v
|
||||
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';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
|
||||
@@ -91,10 +90,10 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
this.scorerCache = Object.create(null);
|
||||
}
|
||||
|
||||
getResults(searchValue: string): TPromise<QuickOpenModel> {
|
||||
getResults(searchValue: string, token: CancellationToken): Thenable<QuickOpenModel> {
|
||||
const editorEntries = this.getEditorEntries();
|
||||
if (!editorEntries.length) {
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
// Prepare search for scoring
|
||||
@@ -117,7 +116,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
|
||||
// Sorting
|
||||
if (query.value) {
|
||||
const groups = this.editorGroupService.getGroups(GroupsOrder.CREATION_TIME);
|
||||
const groups = this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE);
|
||||
entries.sort((e1, e2) => {
|
||||
if (e1.group !== e2.group) {
|
||||
return groups.indexOf(e1.group) - groups.indexOf(e2.group); // older groups first
|
||||
@@ -139,7 +138,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
});
|
||||
}
|
||||
|
||||
return TPromise.as(new QuickOpenModel(entries));
|
||||
return Promise.resolve(new QuickOpenModel(entries));
|
||||
}
|
||||
|
||||
onClose(canceled: boolean): void {
|
||||
@@ -206,7 +205,7 @@ export class AllEditorsPicker extends BaseEditorPicker {
|
||||
protected getEditorEntries(): EditorPickerEntry[] {
|
||||
const entries: EditorPickerEntry[] = [];
|
||||
|
||||
this.editorGroupService.getGroups(GroupsOrder.CREATION_TIME).forEach(group => {
|
||||
this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(group => {
|
||||
group.editors.forEach(editor => {
|
||||
entries.push(this.instantiationService.createInstance(EditorPickerEntry, editor, group));
|
||||
});
|
||||
@@ -232,4 +231,4 @@ export class AllEditorsPicker extends BaseEditorPicker {
|
||||
|
||||
return super.getAutoFocus(searchValue, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,17 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/editorstatus';
|
||||
import * as nls from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener, getDomNodePagePosition } from 'vs/base/browser/dom';
|
||||
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 * as types from 'vs/base/common/types';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
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 { IMode } from 'vs/editor/common/modes';
|
||||
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';
|
||||
@@ -31,11 +26,11 @@ import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpa
|
||||
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
|
||||
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
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 } from 'vs/editor/common/services/modeService';
|
||||
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
@@ -46,18 +41,16 @@ 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 { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
|
||||
import { widgetShadow, editorWidgetBackground, foreground, darken, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
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 { once } from 'vs/base/common/event';
|
||||
|
||||
class SideBySideEditorEncodingSupport implements IEncodingSupport {
|
||||
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
|
||||
@@ -294,7 +287,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
private activeEditorListeners: IDisposable[];
|
||||
private delayedRender: IDisposable;
|
||||
private toRender: StateChange;
|
||||
private screenReaderExplanation: ScreenReaderDetectedExplanation;
|
||||
private screenReaderNotification: INotificationHandle;
|
||||
|
||||
constructor(
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@@ -304,6 +297,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
@IModeService private modeService: IModeService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
|
||||
@INotificationService private readonly notificationService: INotificationService
|
||||
) {
|
||||
this.toDispose = [];
|
||||
this.activeEditorListeners = [];
|
||||
@@ -494,28 +488,39 @@ export class EditorStatus implements IStatusbarItem {
|
||||
private onModeClick(): void {
|
||||
const action = this.instantiationService.createInstance(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL);
|
||||
|
||||
action.run().done(null, errors.onUnexpectedError);
|
||||
action.run();
|
||||
action.dispose();
|
||||
}
|
||||
|
||||
private onIndentationClick(): void {
|
||||
const action = this.instantiationService.createInstance(ChangeIndentationAction, ChangeIndentationAction.ID, ChangeIndentationAction.LABEL);
|
||||
action.run().done(null, errors.onUnexpectedError);
|
||||
action.run();
|
||||
action.dispose();
|
||||
}
|
||||
|
||||
private onScreenReaderModeClick(): void {
|
||||
const showExplanation = !this.screenReaderExplanation || !this.screenReaderExplanation.visible;
|
||||
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?"),
|
||||
[{
|
||||
label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"),
|
||||
run: () => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
|
||||
}
|
||||
}, {
|
||||
label: nls.localize('screenReaderDetectedExplanation.answerNo', "No"),
|
||||
run: () => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
|
||||
}
|
||||
}],
|
||||
{ sticky: true }
|
||||
);
|
||||
|
||||
if (!this.screenReaderExplanation) {
|
||||
this.screenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation);
|
||||
this.toDispose.push(this.screenReaderExplanation);
|
||||
}
|
||||
|
||||
if (showExplanation) {
|
||||
this.screenReaderExplanation.show(this.screenRedearModeElement);
|
||||
} else {
|
||||
this.screenReaderExplanation.hide();
|
||||
once(this.screenReaderNotification.onDidClose)(() => {
|
||||
this.screenReaderNotification = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,14 +531,14 @@ export class EditorStatus implements IStatusbarItem {
|
||||
private onEOLClick(): void {
|
||||
const action = this.instantiationService.createInstance(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL);
|
||||
|
||||
action.run().done(null, errors.onUnexpectedError);
|
||||
action.run();
|
||||
action.dispose();
|
||||
}
|
||||
|
||||
private onEncodingClick(): void {
|
||||
const action = this.instantiationService.createInstance(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL);
|
||||
|
||||
action.run().done(null, errors.onUnexpectedError);
|
||||
action.run();
|
||||
action.dispose();
|
||||
}
|
||||
|
||||
@@ -617,6 +622,10 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.activeEditorListeners.push(editor.onMetadataChanged(metadata => {
|
||||
this.onMetadataChange(activeControl);
|
||||
}));
|
||||
|
||||
this.activeEditorListeners.push(editor.onDidOpenInPlace(() => {
|
||||
this.updateStatusBar();
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -689,8 +698,8 @@ export class EditorStatus implements IStatusbarItem {
|
||||
screenReaderMode = (editorWidget.getConfiguration().accessibilitySupport === AccessibilitySupport.Enabled);
|
||||
}
|
||||
|
||||
if (screenReaderMode === false && this.screenReaderExplanation && this.screenReaderExplanation.visible) {
|
||||
this.screenReaderExplanation.hide();
|
||||
if (screenReaderMode === false && this.screenReaderNotification) {
|
||||
this.screenReaderNotification.close();
|
||||
}
|
||||
|
||||
this.updateState({ screenReaderMode: screenReaderMode });
|
||||
@@ -819,7 +828,7 @@ export class ShowLanguageExtensionsAction extends Action {
|
||||
this.enabled = galleryService.isEnabled();
|
||||
}
|
||||
|
||||
run(): TPromise<void> {
|
||||
run(): Thenable<void> {
|
||||
return this.commandService.executeCommand('workbench.extensions.action.showExtensionsForLanguage', this.fileExtension).then(() => void 0);
|
||||
}
|
||||
}
|
||||
@@ -836,7 +845,7 @@ export class ChangeModeAction extends Action {
|
||||
@IModelService private modelService: IModelService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@IQuickInputService private quickInputService: IQuickInputService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService
|
||||
@@ -844,10 +853,10 @@ export class ChangeModeAction extends Action {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (!activeTextEditorWidget) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
const textModel = activeTextEditorWidget.getModel();
|
||||
@@ -868,7 +877,7 @@ export class ChangeModeAction extends Action {
|
||||
|
||||
// All languages are valid picks
|
||||
const languages = this.modeService.getRegisteredLanguageNames();
|
||||
const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => {
|
||||
const picks: QuickPickInput[] = languages.sort().map((lang, index) => {
|
||||
let description: string;
|
||||
if (currentModeId === lang) {
|
||||
description = nls.localize('languageDescription', "({0}) - Configured Language", this.modeService.getModeIdForLanguageName(lang.toLowerCase()));
|
||||
@@ -888,20 +897,20 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
return <IFilePickOpenEntry>{
|
||||
return <IQuickPickItem>{
|
||||
label: lang,
|
||||
resource: fakeResource,
|
||||
iconClasses: getIconClasses(this.modelService, this.modeService, fakeResource),
|
||||
description
|
||||
};
|
||||
});
|
||||
|
||||
if (hasLanguageSupport) {
|
||||
picks[0].separator = { border: true, label: nls.localize('languagesPicks', "languages (identifier)") };
|
||||
picks.unshift({ type: 'separator', label: nls.localize('languagesPicks', "languages (identifier)") });
|
||||
}
|
||||
|
||||
// Offer action to configure via settings
|
||||
let configureModeAssociations: IPickOpenEntry;
|
||||
let configureModeSettings: IPickOpenEntry;
|
||||
let configureModeAssociations: IQuickPickItem;
|
||||
let configureModeSettings: IQuickPickItem;
|
||||
let galleryAction: Action;
|
||||
if (hasLanguageSupport) {
|
||||
const ext = paths.extname(resource.fsPath) || paths.basename(resource.fsPath);
|
||||
@@ -918,7 +927,7 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
|
||||
// Offer to "Auto Detect"
|
||||
const autoDetectMode: IPickOpenEntry = {
|
||||
const autoDetectMode: IQuickPickItem = {
|
||||
label: nls.localize('autoDetect', "Auto Detect")
|
||||
};
|
||||
|
||||
@@ -926,7 +935,7 @@ export class ChangeModeAction extends Action {
|
||||
picks.unshift(autoDetectMode);
|
||||
}
|
||||
|
||||
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }).then(pick => {
|
||||
return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }).then(pick => {
|
||||
if (!pick) {
|
||||
return;
|
||||
}
|
||||
@@ -970,23 +979,22 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
|
||||
// Find mode
|
||||
let mode: TPromise<IMode>;
|
||||
let languageSelection: ILanguageSelection;
|
||||
if (pick === autoDetectMode) {
|
||||
mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true }).fsPath, textModel.getLineContent(1));
|
||||
// {{SQL CARBON EDIT}} - use activeEditor.input instead of activeEditor
|
||||
languageSelection = this.modeService.createByFilepathOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true }).fsPath, textModel.getLineContent(1));
|
||||
} else {
|
||||
mode = this.modeService.getOrCreateModeByLanguageName(pick.label);
|
||||
languageSelection = this.modeService.createByLanguageName(pick.label);
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Change mode
|
||||
models.forEach(textModel => {
|
||||
let self = this;
|
||||
mode.then((modeValue) => {
|
||||
QueryEditorService.sqlLanguageModeCheck(textModel, modeValue, activeEditor).then((newTextModel) => {
|
||||
if (newTextModel) {
|
||||
self.modelService.setMode(newTextModel, modeValue);
|
||||
}
|
||||
});
|
||||
QueryEditorService.sqlLanguageModeCheck(textModel, languageSelection, activeEditor).then((newTextModel) => {
|
||||
if (newTextModel) {
|
||||
self.modelService.setMode(newTextModel, languageSelection);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -995,21 +1003,21 @@ 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.getModeIdByFilenameOrFirstLine(basename);
|
||||
const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(basename);
|
||||
|
||||
const languages = this.modeService.getRegisteredLanguageNames();
|
||||
const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => {
|
||||
const picks: IQuickPickItem[] = languages.sort().map((lang, index) => {
|
||||
const id = this.modeService.getModeIdForLanguageName(lang.toLowerCase());
|
||||
|
||||
return <IPickOpenEntry>{
|
||||
return <IQuickPickItem>{
|
||||
id,
|
||||
label: lang,
|
||||
description: (id === currentAssociation) ? nls.localize('currentAssociation', "Current Association") : void 0
|
||||
};
|
||||
});
|
||||
|
||||
TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).done(() => {
|
||||
this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).done(language => {
|
||||
setTimeout(() => {
|
||||
this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).then(language => {
|
||||
if (language) {
|
||||
const fileAssociationsConfig = this.configurationService.inspect(FILES_ASSOCIATIONS_CONFIG);
|
||||
|
||||
@@ -1037,11 +1045,11 @@ export class ChangeModeAction extends Action {
|
||||
this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 50 /* quick open is sensitive to being opened so soon after another */);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IChangeEOLEntry extends IPickOpenEntry {
|
||||
export interface IChangeEOLEntry extends IQuickPickItem {
|
||||
eol: EndOfLineSequence;
|
||||
}
|
||||
|
||||
@@ -1054,22 +1062,22 @@ class ChangeIndentationAction extends Action {
|
||||
actionId: string,
|
||||
actionLabel: string,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService
|
||||
@IQuickInputService private quickInputService: IQuickInputService
|
||||
) {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (!activeTextEditorWidget) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
if (!isWritableCodeEditor(activeTextEditorWidget)) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
}
|
||||
|
||||
const picks = [
|
||||
const picks: QuickPickInput<IQuickPickItem & { run(): void }>[] = [
|
||||
activeTextEditorWidget.getAction(IndentUsingSpaces.ID),
|
||||
activeTextEditorWidget.getAction(IndentUsingTabs.ID),
|
||||
activeTextEditorWidget.getAction(DetectIndentation.ID),
|
||||
@@ -1088,10 +1096,10 @@ class ChangeIndentationAction extends Action {
|
||||
};
|
||||
});
|
||||
|
||||
(<IPickOpenEntry>picks[0]).separator = { label: nls.localize('indentView', "change view") };
|
||||
(<IPickOpenEntry>picks[3]).separator = { label: nls.localize('indentConvert', "convert file"), border: true };
|
||||
picks.splice(3, 0, { type: 'separator', label: nls.localize('indentConvert', "convert file") });
|
||||
picks.unshift({ type: 'separator', label: nls.localize('indentView', "change view") });
|
||||
|
||||
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }).then(action => action && action.run());
|
||||
return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }).then(action => action && action.run());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,19 +1112,19 @@ export class ChangeEOLAction extends Action {
|
||||
actionId: string,
|
||||
actionLabel: string,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService
|
||||
@IQuickInputService private quickInputService: IQuickInputService
|
||||
) {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (!activeTextEditorWidget) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
if (!isWritableCodeEditor(activeTextEditorWidget)) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
}
|
||||
|
||||
const textModel = activeTextEditorWidget.getModel();
|
||||
@@ -1128,7 +1136,7 @@ export class ChangeEOLAction extends Action {
|
||||
|
||||
const selectedIndex = (textModel && textModel.getEOL() === '\n') ? 0 : 1;
|
||||
|
||||
return this.quickOpenService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), autoFocus: { autoFocusIndex: selectedIndex } }).then(eol => {
|
||||
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)) {
|
||||
@@ -1149,28 +1157,26 @@ export class ChangeEncodingAction extends Action {
|
||||
actionId: string,
|
||||
actionLabel: string,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@IQuickInputService private quickInputService: IQuickInputService,
|
||||
@ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService,
|
||||
@IFileService private fileService: IFileService
|
||||
) {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
run(): Thenable<any> {
|
||||
if (!getCodeEditor(this.editorService.activeTextEditorWidget)) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
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);
|
||||
if (!encodingSupport) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]);
|
||||
return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]);
|
||||
}
|
||||
|
||||
let pickActionPromise: TPromise<IPickOpenEntry>;
|
||||
|
||||
let saveWithEncodingPick: IPickOpenEntry;
|
||||
let reopenWithEncodingPick: IPickOpenEntry;
|
||||
let saveWithEncodingPick: IQuickPickItem;
|
||||
let reopenWithEncodingPick: IQuickPickItem;
|
||||
if (language === LANGUAGE_DEFAULT) {
|
||||
saveWithEncodingPick = { label: nls.localize('saveWithEncoding', "Save with Encoding") };
|
||||
reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding") };
|
||||
@@ -1179,12 +1185,13 @@ export class ChangeEncodingAction extends Action {
|
||||
reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding"), detail: 'Reopen with Encoding' };
|
||||
}
|
||||
|
||||
let pickActionPromise: Promise<IQuickPickItem>;
|
||||
if (encodingSupport instanceof UntitledEditorInput) {
|
||||
pickActionPromise = TPromise.as(saveWithEncodingPick);
|
||||
pickActionPromise = Promise.resolve(saveWithEncodingPick);
|
||||
} else if (!isWritableBaseEditor(activeControl)) {
|
||||
pickActionPromise = TPromise.as(reopenWithEncodingPick);
|
||||
pickActionPromise = Promise.resolve(reopenWithEncodingPick);
|
||||
} else {
|
||||
pickActionPromise = this.quickOpenService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
|
||||
pickActionPromise = this.quickInputService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
|
||||
}
|
||||
|
||||
return pickActionPromise.then(action => {
|
||||
@@ -1194,10 +1201,10 @@ export class ChangeEncodingAction extends Action {
|
||||
|
||||
const resource = toResource(activeControl.input, { supportSideBySide: true });
|
||||
|
||||
return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */)
|
||||
return timeout(50 /* quick open is sensitive to being opened so soon after another */)
|
||||
.then(() => {
|
||||
if (!resource || !this.fileService.canHandleResource(resource)) {
|
||||
return TPromise.as(null); // encoding detection only possible for resources the file service can handle
|
||||
return Promise.resolve(null); // encoding detection only possible for resources the file service can handle
|
||||
}
|
||||
|
||||
return this.fileService.resolveContent(resource, { autoGuessEncoding: true, acceptTextOnly: true }).then(content => content.encoding, err => null);
|
||||
@@ -1211,7 +1218,7 @@ export class ChangeEncodingAction extends Action {
|
||||
let aliasMatchIndex: number;
|
||||
|
||||
// All encodings are valid picks
|
||||
const picks: IPickOpenEntry[] = Object.keys(SUPPORTED_ENCODINGS)
|
||||
const picks: QuickPickInput[] = Object.keys(SUPPORTED_ENCODINGS)
|
||||
.sort((k1, k2) => {
|
||||
if (k1 === configuredEncoding) {
|
||||
return -1;
|
||||
@@ -1238,15 +1245,17 @@ export class ChangeEncodingAction extends Action {
|
||||
return { id: key, label: SUPPORTED_ENCODINGS[key].labelLong, description: key };
|
||||
});
|
||||
|
||||
const items = picks.slice() as IQuickPickItem[];
|
||||
|
||||
// If we have a guessed encoding, show it first unless it matches the configured encoding
|
||||
if (guessedEncoding && configuredEncoding !== guessedEncoding && SUPPORTED_ENCODINGS[guessedEncoding]) {
|
||||
picks[0].separator = { border: true };
|
||||
picks.unshift({ type: 'separator' });
|
||||
picks.unshift({ id: guessedEncoding, label: SUPPORTED_ENCODINGS[guessedEncoding].labelLong, description: nls.localize('guessedEncoding', "Guessed from content") });
|
||||
}
|
||||
|
||||
return this.quickOpenService.pick(picks, {
|
||||
return this.quickInputService.pick(picks, {
|
||||
placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
|
||||
autoFocus: { autoFocusIndex: typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : void 0 }
|
||||
activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1]
|
||||
}).then(encoding => {
|
||||
if (encoding) {
|
||||
activeControl = this.editorService.activeControl;
|
||||
@@ -1260,149 +1269,3 @@ export class ChangeEncodingAction extends Action {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenReaderDetectedExplanation extends Themable {
|
||||
private container: HTMLElement;
|
||||
private hrElement: HTMLHRElement;
|
||||
private _visible: boolean;
|
||||
|
||||
constructor(
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
|
||||
) {
|
||||
super(themeService);
|
||||
}
|
||||
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
if (this.container) {
|
||||
const background = this.getColor(editorWidgetBackground);
|
||||
this.container.style.backgroundColor = background ? background.toString() : null;
|
||||
|
||||
const widgetShadowColor = this.getColor(widgetShadow);
|
||||
this.container.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
|
||||
|
||||
const contrastBorderColor = this.getColor(contrastBorder);
|
||||
this.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : null;
|
||||
|
||||
const foregroundColor = this.getColor(foreground);
|
||||
this.hrElement.style.backgroundColor = foregroundColor ? foregroundColor.toString() : null;
|
||||
}
|
||||
}
|
||||
|
||||
show(anchorElement: HTMLElement): void {
|
||||
this._visible = true;
|
||||
|
||||
this.contextViewService.showContextView({
|
||||
getAnchor: () => {
|
||||
const res = getDomNodePagePosition(anchorElement);
|
||||
|
||||
return {
|
||||
x: res.left,
|
||||
y: res.top - 9, /* above the status bar */
|
||||
width: res.width,
|
||||
height: res.height
|
||||
} as IAnchor;
|
||||
},
|
||||
render: (container) => {
|
||||
return this.renderContents(container);
|
||||
},
|
||||
onDOMEvent: (e, activeElement) => { },
|
||||
onHide: () => {
|
||||
this._visible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
this.contextViewService.hideContextView();
|
||||
}
|
||||
|
||||
protected renderContents(parent: HTMLElement): IDisposable {
|
||||
const toDispose: IDisposable[] = [];
|
||||
|
||||
this.container = $('div.screen-reader-detected-explanation', {
|
||||
'aria-hidden': 'true'
|
||||
});
|
||||
|
||||
const title = $('h2.title', {}, nls.localize('screenReaderDetectedExplanation.title', "Screen Reader Optimized"));
|
||||
this.container.appendChild(title);
|
||||
|
||||
const closeBtn = $('div.cancel');
|
||||
toDispose.push(addDisposableListener(closeBtn, 'click', () => {
|
||||
this.contextViewService.hideContextView();
|
||||
}));
|
||||
toDispose.push(addDisposableListener(closeBtn, 'mouseover', () => {
|
||||
const theme = this.themeService.getTheme();
|
||||
let darkenFactor: number;
|
||||
switch (theme.type) {
|
||||
case 'light':
|
||||
darkenFactor = 0.1;
|
||||
break;
|
||||
case 'dark':
|
||||
darkenFactor = 0.2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (darkenFactor) {
|
||||
closeBtn.style.backgroundColor = this.getColor(editorWidgetBackground, (color, theme) => darken(color, darkenFactor)(theme));
|
||||
}
|
||||
}));
|
||||
toDispose.push(addDisposableListener(closeBtn, 'mouseout', () => {
|
||||
closeBtn.style.backgroundColor = null;
|
||||
}));
|
||||
this.container.appendChild(closeBtn);
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
const question = $('p.question', {}, nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio?"));
|
||||
this.container.appendChild(question);
|
||||
|
||||
const buttonContainer = $('div.buttons');
|
||||
this.container.appendChild(buttonContainer);
|
||||
|
||||
const yesBtn = new Button(buttonContainer);
|
||||
yesBtn.label = nls.localize('screenReaderDetectedExplanation.answerYes', "Yes");
|
||||
toDispose.push(attachButtonStyler(yesBtn, this.themeService));
|
||||
toDispose.push(yesBtn.onDidClick(e => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
|
||||
this.contextViewService.hideContextView();
|
||||
}));
|
||||
|
||||
const noBtn = new Button(buttonContainer);
|
||||
noBtn.label = nls.localize('screenReaderDetectedExplanation.answerNo', "No");
|
||||
toDispose.push(attachButtonStyler(noBtn, this.themeService));
|
||||
toDispose.push(noBtn.onDidClick(e => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
|
||||
this.contextViewService.hideContextView();
|
||||
}));
|
||||
|
||||
const clear = $('div');
|
||||
clear.style.clear = 'both';
|
||||
this.container.appendChild(clear);
|
||||
|
||||
const br = $('br');
|
||||
this.container.appendChild(br);
|
||||
|
||||
this.hrElement = $('hr');
|
||||
this.container.appendChild(this.hrElement);
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
const explanation1 = $('p.body1', {}, nls.localize('screenReaderDetectedExplanation.body1', "Azure Data Studio is now optimized for usage with a screen reader."));
|
||||
this.container.appendChild(explanation1);
|
||||
|
||||
const explanation2 = $('p.body2', {}, nls.localize('screenReaderDetectedExplanation.body2', "Some editor features will have different behaviour: e.g. word wrapping, folding, etc."));
|
||||
this.container.appendChild(explanation2);
|
||||
|
||||
parent.appendChild(this.container);
|
||||
|
||||
this.updateStyles();
|
||||
|
||||
return {
|
||||
dispose: () => dispose(toDispose)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
179
src/vs/workbench/browser/parts/editor/editorWidgets.ts
Normal file
179
src/vs/workbench/browser/parts/editor/editorWidgets.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { IOverlayWidget, ICodeEditor, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { $, append } from 'vs/base/browser/dom';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
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 { 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';
|
||||
|
||||
export class FloatingClickWidget extends Widget implements IOverlayWidget {
|
||||
|
||||
private _onClick: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onClick(): Event<void> { return this._onClick.event; }
|
||||
|
||||
private _domNode: HTMLElement;
|
||||
|
||||
constructor(
|
||||
private editor: ICodeEditor,
|
||||
private label: string,
|
||||
keyBindingAction: string,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
|
||||
if (keyBindingAction) {
|
||||
const keybinding = keybindingService.lookupKeybinding(keyBindingAction);
|
||||
if (keybinding) {
|
||||
this.label += ` (${keybinding.getLabel()})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return 'editor.overlayWidget.floatingClickWidget';
|
||||
}
|
||||
|
||||
getDomNode(): HTMLElement {
|
||||
return this._domNode;
|
||||
}
|
||||
|
||||
getPosition(): IOverlayWidgetPosition {
|
||||
return {
|
||||
preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
this._domNode = $('.floating-click-widget');
|
||||
|
||||
this._register(attachStylerCallback(this.themeService, { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder }, colors => {
|
||||
const backgroundColor = colors.buttonBackground ? colors.buttonBackground : colors.editorBackground;
|
||||
if (backgroundColor) {
|
||||
this._domNode.style.backgroundColor = backgroundColor.toString();
|
||||
}
|
||||
|
||||
const foregroundColor = colors.buttonForeground ? colors.buttonForeground : colors.editorForeground;
|
||||
if (foregroundColor) {
|
||||
this._domNode.style.color = foregroundColor.toString();
|
||||
}
|
||||
|
||||
const borderColor = colors.contrastBorder ? colors.contrastBorder.toString() : null;
|
||||
this._domNode.style.borderWidth = borderColor ? '1px' : null;
|
||||
this._domNode.style.borderStyle = borderColor ? 'solid' : null;
|
||||
this._domNode.style.borderColor = borderColor;
|
||||
}));
|
||||
|
||||
append(this._domNode, $('')).textContent = this.label;
|
||||
|
||||
this.onclick(this._domNode, e => this._onClick.fire());
|
||||
|
||||
this.editor.addOverlayWidget(this);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.editor.removeOverlayWidget(this);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenWorkspaceButtonContribution extends Disposable implements IEditorContribution {
|
||||
|
||||
static get(editor: ICodeEditor): OpenWorkspaceButtonContribution {
|
||||
return editor.getContribution<OpenWorkspaceButtonContribution>(OpenWorkspaceButtonContribution.ID);
|
||||
}
|
||||
|
||||
private static readonly ID = 'editor.contrib.openWorkspaceButton';
|
||||
|
||||
private openWorkspaceButton: FloatingClickWidget;
|
||||
|
||||
constructor(
|
||||
private editor: ICodeEditor,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.update();
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.editor.onDidChangeModel(e => this.update()));
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return OpenWorkspaceButtonContribution.ID;
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
if (!this.shouldShowButton(this.editor)) {
|
||||
this.disposeOpenWorkspaceWidgetRenderer();
|
||||
return;
|
||||
}
|
||||
|
||||
this.createOpenWorkspaceWidgetRenderer();
|
||||
}
|
||||
|
||||
private shouldShowButton(editor: ICodeEditor): boolean {
|
||||
const model = editor.getModel();
|
||||
if (!model) {
|
||||
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 (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
const workspaceConfiguration = this.contextService.getWorkspace().configuration;
|
||||
if (workspaceConfiguration && isEqual(workspaceConfiguration, model.uri)) {
|
||||
return false; // already inside workspace
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private createOpenWorkspaceWidgetRenderer(): void {
|
||||
if (!this.openWorkspaceButton) {
|
||||
this.openWorkspaceButton = this.instantiationService.createInstance(FloatingClickWidget, this.editor, localize('openWorkspace', "Open Workspace"), null);
|
||||
this._register(this.openWorkspaceButton.onClick(() => {
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
this.windowService.openWindow([model.uri]);
|
||||
}
|
||||
}));
|
||||
|
||||
this.openWorkspaceButton.render();
|
||||
}
|
||||
}
|
||||
|
||||
private disposeOpenWorkspaceWidgetRenderer(): void {
|
||||
this.openWorkspaceButton = dispose(this.openWorkspaceButton);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposeOpenWorkspaceWidgetRenderer();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 381 B |
@@ -7,10 +7,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .breadcrumbs-control {
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .monaco-icon-label,
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .monaco-icon-label {
|
||||
text-decoration-line: underline;
|
||||
@@ -23,6 +19,17 @@
|
||||
|
||||
/* todo@joh move somewhere else */
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .picker-item {
|
||||
line-height: 22px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -44,15 +44,13 @@
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs {
|
||||
flex-wrap: wrap;
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title:not(.tabs) {
|
||||
display: flex; /* when tabs are not shown, use flex layout */
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.title-border-bottom::after {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 381 B |
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Title Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title > .label-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
@@ -11,8 +13,6 @@
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
/* Title Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label {
|
||||
line-height: 35px;
|
||||
overflow: hidden;
|
||||
@@ -34,7 +34,7 @@
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control {
|
||||
flex: 1 50%;
|
||||
overflow: hidden;
|
||||
padding: 0 6px;
|
||||
margin-left: .45em;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item {
|
||||
@@ -67,8 +67,8 @@
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after {
|
||||
/* use dot separator for workspace folder */
|
||||
content: '•';
|
||||
padding: 0 4px;
|
||||
content: '\00a0•\00a0';
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;opacity:0.5;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;opacity:0.5;}</style></defs><title>Paragraph_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,1V4H12V16H6V9.973A4.5,4.5,0,0,1,6.5,1Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M12,2V3H11V15H10V3H8V15H7V8.95A3.588,3.588,0,0,1,6.5,9a3.5,3.5,0,0,1,0-7Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 576 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;opacity:0.5;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;opacity:0.5;}</style></defs><title>Paragraph_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,1V4H12V16H6V9.973A4.5,4.5,0,0,1,6.5,1Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M12,2V3H11V15H10V3H8V15H7V8.95A3.588,3.588,0,0,1,6.5,9a3.5,3.5,0,0,1,0-7Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 576 B |
@@ -5,11 +5,15 @@
|
||||
|
||||
/* Title Container */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .monaco-scrollable-element {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container > .monaco-scrollable-element {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .monaco-scrollable-element .scrollbar {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container > .monaco-scrollable-element .scrollbar {
|
||||
z-index: 3; /* on top of tabs */
|
||||
cursor: default;
|
||||
}
|
||||
@@ -92,28 +96,33 @@
|
||||
display: none; /* hidden by default until a color is provided (see below) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 6; /* over possible title border */
|
||||
pointer-events: none;
|
||||
background-color: var(--tab-border-top-color);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container {
|
||||
top: 0;
|
||||
height: 1px;
|
||||
background-color: var(--tab-border-top-color);
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 6; /* over possible title border */
|
||||
pointer-events: none;
|
||||
background-color: var(--tab-border-bottom-color);
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--tab-border-bottom-color);
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container {
|
||||
top: 0;
|
||||
height: 2px;
|
||||
background-color: var(--tab-dirty-border-top-color);
|
||||
}
|
||||
|
||||
/* Tab Label */
|
||||
@@ -137,6 +146,10 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:focus > .tab-label::after {
|
||||
opacity: 0; /* when tab has the focus this shade breaks the tab border (fixes https://github.com/Microsoft/vscode/issues/57819) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
overflow: visible; /* fixes https://github.com/Microsoft/vscode/issues/20182 */
|
||||
@@ -168,8 +181,9 @@
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.close-button-right.sizing-shrink > .tab-close,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink:hover > .tab-close {
|
||||
overflow: visible; /* ...but still show the close button on hover and when dirty */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink:hover > .tab-close,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close:focus-within {
|
||||
overflow: visible; /* ...but still show the close button on hover, focus and when dirty */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close {
|
||||
@@ -259,23 +273,28 @@
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control {
|
||||
flex: 1 100%;
|
||||
height: 25px;
|
||||
height: 22px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before {
|
||||
height: 18px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
height: 22px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item {
|
||||
max-width: 260px;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
|
||||
min-width: 16px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
/* .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:not(:last-child):not(:hover):not(.focused):not(.file) {
|
||||
min-width: 33px;
|
||||
} */
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.vs .monaco-workbench .textdiff-editor-action.next {
|
||||
background: url('next-diff.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench .textdiff-editor-action.previous {
|
||||
background: url('previous-diff.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .textdiff-editor-action.next,
|
||||
.hc-black .monaco-workbench .textdiff-editor-action.next {
|
||||
background: url('next-diff-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .textdiff-editor-action.previous,
|
||||
.hc-black .monaco-workbench .textdiff-editor-action.previous {
|
||||
background: url('previous-diff-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace {
|
||||
opacity: 1;
|
||||
background: url('paragraph.svg') center center no-repeat;
|
||||
}
|
||||
.vs-dark .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace,
|
||||
.hc-black .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace {
|
||||
opacity: 1;
|
||||
background: url('paragraph-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace.is-checked {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.vs-dark .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace.is-checked,
|
||||
.hc-black .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace.is-checked {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/notabstitlecontrol';
|
||||
import { toResource, Verbosity, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
@@ -15,11 +13,17 @@ import { addDisposableListener, EventType, addClass, EventHelper, removeClass, t
|
||||
import { IEditorPartOptions, 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';
|
||||
|
||||
interface IRenderedEditorLabel {
|
||||
editor: IEditorInput;
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
export class NoTabsTitleControl extends TitleControl {
|
||||
private titleContainer: HTMLElement;
|
||||
private editorLabel: ResourceLabel;
|
||||
private lastRenderedActiveEditor: IEditorInput;
|
||||
private activeLabel: IRenderedEditorLabel = Object.create(null);
|
||||
|
||||
protected create(parent: HTMLElement): void {
|
||||
this.titleContainer = parent;
|
||||
@@ -40,7 +44,9 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e)));
|
||||
|
||||
// Breadcrumbs
|
||||
this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, extraClasses: ['no-tabs-breadcrumbs'] });
|
||||
this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: () => Color.transparent });
|
||||
toggleClass(this.titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl));
|
||||
this.toDispose.push({ dispose: () => removeClass(this.titleContainer, 'breadcrumbs') }); // import to remove because the container is a shared dom node
|
||||
|
||||
// Right Actions Container
|
||||
const actionsContainer = document.createElement('div');
|
||||
@@ -60,7 +66,7 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
|
||||
|
||||
// Detect mouse click
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.CLICK, (e: MouseEvent) => this.onTitleClick(e)));
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.MOUSE_UP, (e: MouseEvent) => this.onTitleClick(e)));
|
||||
|
||||
// Detect touch
|
||||
this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
|
||||
@@ -87,6 +93,8 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
|
||||
// Close editor on middle mouse click
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -96,7 +104,10 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
openEditor(editor: IEditorInput): void {
|
||||
this.ifActiveEditorChanged(() => this.redraw());
|
||||
const activeEditorChanged = this.ifActiveEditorChanged(() => this.redraw());
|
||||
if (!activeEditorChanged) {
|
||||
this.ifActiveEditorPropertiesChanged(() => this.redraw());
|
||||
}
|
||||
}
|
||||
|
||||
closeEditor(editor: IEditorInput): void {
|
||||
@@ -147,16 +158,36 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
private ifActiveEditorChanged(fn: () => void): void {
|
||||
protected handleBreadcrumbsEnablementChange(): void {
|
||||
toggleClass(this.titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl));
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
private ifActiveEditorChanged(fn: () => void): boolean {
|
||||
if (
|
||||
!this.lastRenderedActiveEditor && this.group.activeEditor || // active editor changed from null => editor
|
||||
this.lastRenderedActiveEditor && !this.group.activeEditor || // active editor changed from editor => null
|
||||
!this.group.isActive(this.lastRenderedActiveEditor) // active editor changed from editorA => editorB
|
||||
!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
|
||||
) {
|
||||
fn();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private ifActiveEditorPropertiesChanged(fn: () => void): void {
|
||||
if (!this.activeLabel.editor || !this.group.activeEditor) {
|
||||
return; // need an active editor to check for properties changed
|
||||
}
|
||||
|
||||
if (this.activeLabel.pinned !== this.group.isPinned(this.group.activeEditor)) {
|
||||
fn(); // only run if pinned state has changed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ifEditorIsActive(editor: IEditorInput, fn: () => void): void {
|
||||
if (this.group.isActive(editor)) {
|
||||
fn(); // only run if editor is current active
|
||||
@@ -165,11 +196,12 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
|
||||
private redraw(): void {
|
||||
const editor = this.group.activeEditor;
|
||||
this.lastRenderedActiveEditor = editor;
|
||||
|
||||
const isEditorPinned = this.group.isPinned(this.group.activeEditor);
|
||||
const isGroupActive = this.accessor.activeGroup === this.group;
|
||||
|
||||
this.activeLabel = { editor, pinned: isEditorPinned };
|
||||
|
||||
// Update Breadcrumbs
|
||||
if (this.breadcrumbsControl) {
|
||||
if (isGroupActive) {
|
||||
@@ -189,6 +221,7 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
|
||||
// Otherwise render it
|
||||
else {
|
||||
|
||||
// Dirty state
|
||||
this.updateEditorDirty(editor);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
@@ -21,8 +21,8 @@ export interface IRangeHighlightDecoration {
|
||||
|
||||
export class RangeHighlightDecorations extends Disposable {
|
||||
|
||||
private rangeHighlightDecorationId: string = null;
|
||||
private editor: ICodeEditor = null;
|
||||
private rangeHighlightDecorationId: string | null = null;
|
||||
private editor: ICodeEditor | null = null;
|
||||
private editorDisposables: IDisposable[] = [];
|
||||
|
||||
private readonly _onHighlightRemoved: Emitter<void> = this._register(new Emitter<void>());
|
||||
|
||||
@@ -6,20 +6,19 @@
|
||||
import 'vs/css!./media/resourceviewer';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as mimes from 'vs/base/common/mime';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { clamp } from 'vs/base/common/numbers';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IStatusbarItem, StatusbarItemDescriptor, IStatusbarRegistry, Extensions, StatusbarAlignment } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { IStatusbarItem, StatusbarItemDescriptor, IStatusbarRegistry, Extensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
@@ -61,8 +60,8 @@ class BinarySize {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ResourceViewerContext {
|
||||
layout(dimension: DOM.Dimension): void;
|
||||
export interface ResourceViewerContext extends IDisposable {
|
||||
layout?(dimension: DOM.Dimension): void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,10 +80,10 @@ export class ResourceViewer {
|
||||
openInternalClb: (uri: URI) => void,
|
||||
openExternalClb: (uri: URI) => void,
|
||||
metadataClb: (meta: string) => void
|
||||
): ResourceViewerContext | null {
|
||||
): ResourceViewerContext {
|
||||
|
||||
// Ensure CSS class
|
||||
$(container).setClass('monaco-resource-viewer');
|
||||
container.className = 'monaco-resource-viewer';
|
||||
|
||||
// Images
|
||||
if (ResourceViewer.isImageResource(descriptor)) {
|
||||
@@ -93,21 +92,20 @@ export class ResourceViewer {
|
||||
|
||||
// Large Files
|
||||
if (descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) {
|
||||
FileTooLargeFileView.create(container, descriptor, scrollbar, metadataClb);
|
||||
return FileTooLargeFileView.create(container, descriptor, scrollbar, metadataClb);
|
||||
}
|
||||
|
||||
// Seemingly Binary Files
|
||||
else {
|
||||
FileSeemsBinaryFileView.create(container, descriptor, scrollbar, openInternalClb, metadataClb);
|
||||
return FileSeemsBinaryFileView.create(container, descriptor, scrollbar, openInternalClb, metadataClb);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static isImageResource(descriptor: IResourceDescriptor) {
|
||||
const mime = getMime(descriptor);
|
||||
|
||||
return mime.indexOf('image/') >= 0;
|
||||
// Chrome does not support tiffs
|
||||
return mime.indexOf('image/') >= 0 && mime !== 'image/tiff';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,14 +120,12 @@ class ImageView {
|
||||
scrollbar: DomScrollableElement,
|
||||
openExternalClb: (uri: URI) => void,
|
||||
metadataClb: (meta: string) => void
|
||||
): ResourceViewerContext | null {
|
||||
): ResourceViewerContext {
|
||||
if (ImageView.shouldShowImageInline(descriptor)) {
|
||||
return InlineImageView.create(container, descriptor, fileService, scrollbar, metadataClb);
|
||||
}
|
||||
|
||||
LargeImageView.create(container, descriptor, openExternalClb);
|
||||
|
||||
return null;
|
||||
return LargeImageView.create(container, descriptor, openExternalClb, metadataClb);
|
||||
}
|
||||
|
||||
private static shouldShowImageInline(descriptor: IResourceDescriptor): boolean {
|
||||
@@ -156,25 +152,29 @@ class LargeImageView {
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
openExternalClb: (uri: URI) => void
|
||||
openExternalClb: (uri: URI) => void,
|
||||
metadataClb: (meta: string) => void
|
||||
) {
|
||||
const size = BinarySize.formatSize(descriptor.size);
|
||||
metadataClb(size);
|
||||
|
||||
const imageContainer = $(container)
|
||||
.empty()
|
||||
.p({
|
||||
text: nls.localize('largeImageError', "The image is not displayed in the editor because it is too large ({0}).", size)
|
||||
});
|
||||
DOM.clearNode(container);
|
||||
|
||||
const disposables: IDisposable[] = [];
|
||||
|
||||
const label = document.createElement('p');
|
||||
label.textContent = nls.localize('largeImageError', "The image is not displayed in the editor because it is too large ({0}).", size);
|
||||
container.appendChild(label);
|
||||
|
||||
if (descriptor.resource.scheme !== Schemas.data) {
|
||||
imageContainer.append($('a', {
|
||||
role: 'button',
|
||||
class: 'embedded-link',
|
||||
text: nls.localize('resourceOpenExternalButton', "Open image using external program?")
|
||||
}).on(DOM.EventType.CLICK, (e) => {
|
||||
openExternalClb(descriptor.resource);
|
||||
}));
|
||||
const link = DOM.append(label, DOM.$('a.embedded-link'));
|
||||
link.setAttribute('role', 'button');
|
||||
link.textContent = nls.localize('resourceOpenExternalButton', "Open image using external program?");
|
||||
|
||||
disposables.push(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => openExternalClb(descriptor.resource)));
|
||||
}
|
||||
|
||||
return combinedDisposable(disposables);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,18 +186,17 @@ class FileTooLargeFileView {
|
||||
metadataClb: (meta: string) => void
|
||||
) {
|
||||
const size = BinarySize.formatSize(descriptor.size);
|
||||
metadataClb(size);
|
||||
|
||||
$(container)
|
||||
.empty()
|
||||
.span({
|
||||
text: nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size)
|
||||
});
|
||||
DOM.clearNode(container);
|
||||
|
||||
if (metadataClb) {
|
||||
metadataClb(size);
|
||||
}
|
||||
const label = document.createElement('span');
|
||||
label.textContent = nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size);
|
||||
container.appendChild(label);
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
|
||||
return Disposable.None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,27 +208,27 @@ class FileSeemsBinaryFileView {
|
||||
openInternalClb: (uri: URI) => void,
|
||||
metadataClb: (meta: string) => void
|
||||
) {
|
||||
const binaryContainer = $(container)
|
||||
.empty()
|
||||
.p({
|
||||
text: nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding.")
|
||||
});
|
||||
metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : '');
|
||||
|
||||
DOM.clearNode(container);
|
||||
|
||||
const disposables: IDisposable[] = [];
|
||||
|
||||
const label = document.createElement('p');
|
||||
label.textContent = nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding.");
|
||||
container.appendChild(label);
|
||||
|
||||
if (descriptor.resource.scheme !== Schemas.data) {
|
||||
binaryContainer.append($('a', {
|
||||
role: 'button',
|
||||
class: 'embedded-link',
|
||||
text: nls.localize('openAsText', "Do you want to open it anyway?")
|
||||
}).on(DOM.EventType.CLICK, (e) => {
|
||||
openInternalClb(descriptor.resource);
|
||||
}));
|
||||
}
|
||||
const link = DOM.append(label, DOM.$('a.embedded-link'));
|
||||
link.setAttribute('role', 'button');
|
||||
link.textContent = nls.localize('openAsText', "Do you want to open it anyway?");
|
||||
|
||||
if (metadataClb) {
|
||||
metadataClb(BinarySize.formatSize(descriptor.size));
|
||||
disposables.push(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => openInternalClb(descriptor.resource)));
|
||||
}
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
|
||||
return combinedDisposable(disposables);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +238,7 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
|
||||
|
||||
static instance: ZoomStatusbarItem;
|
||||
|
||||
showTimeout: number;
|
||||
showTimeout: any;
|
||||
|
||||
private statusBarItem: HTMLElement;
|
||||
private onSelectScale?: (scale: Scale) => void;
|
||||
@@ -276,16 +275,16 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
|
||||
|
||||
render(container: HTMLElement): IDisposable {
|
||||
if (!this.statusBarItem && container) {
|
||||
this.statusBarItem = $(container).a()
|
||||
.addClass('.zoom-statusbar-item')
|
||||
.on('click', () => {
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => container,
|
||||
getActions: () => TPromise.as(this.zoomActions)
|
||||
});
|
||||
})
|
||||
.getHTMLElement();
|
||||
this.statusBarItem = DOM.append(container, DOM.$('a.zoom-statusbar-item'));
|
||||
this.statusBarItem.setAttribute('role', 'button');
|
||||
this.statusBarItem.style.display = 'none';
|
||||
|
||||
DOM.addDisposableListener(this.statusBarItem, DOM.EventType.CLICK, () => {
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => container,
|
||||
getActions: () => this.zoomActions
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -304,7 +303,7 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
|
||||
this.onSelectScale(scale);
|
||||
}
|
||||
|
||||
return null;
|
||||
return void 0;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -368,8 +367,11 @@ class InlineImageView {
|
||||
scrollbar: DomScrollableElement,
|
||||
metadataClb: (meta: string) => void
|
||||
) {
|
||||
const context = {
|
||||
layout(dimension: DOM.Dimension) { }
|
||||
const disposables: IDisposable[] = [];
|
||||
|
||||
const context: ResourceViewerContext = {
|
||||
layout(dimension: DOM.Dimension) { },
|
||||
dispose: () => combinedDisposable(disposables).dispose()
|
||||
};
|
||||
|
||||
const cacheKey = descriptor.resource.toString();
|
||||
@@ -379,41 +381,40 @@ class InlineImageView {
|
||||
|
||||
const initialState: ImageState = InlineImageView.imageStateCache.get(cacheKey) || { scale: 'fit', offsetX: 0, offsetY: 0 };
|
||||
let scale = initialState.scale;
|
||||
let img: Builder | null = null;
|
||||
let imgElement: HTMLImageElement | null = null;
|
||||
let image: HTMLImageElement | null = null;
|
||||
|
||||
function updateScale(newScale: Scale) {
|
||||
if (!img || !imgElement.parentElement) {
|
||||
if (!image || !image.parentElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newScale === 'fit') {
|
||||
scale = 'fit';
|
||||
img.addClass('scale-to-fit');
|
||||
img.removeClass('pixelated');
|
||||
img.style('min-width', 'auto');
|
||||
img.style('width', 'auto');
|
||||
DOM.addClass(image, 'scale-to-fit');
|
||||
DOM.removeClass(image, 'pixelated');
|
||||
image.style.minWidth = 'auto';
|
||||
image.style.width = 'auto';
|
||||
InlineImageView.imageStateCache.set(cacheKey, null);
|
||||
} else {
|
||||
const oldWidth = imgElement.width;
|
||||
const oldHeight = imgElement.height;
|
||||
const oldWidth = image.width;
|
||||
const oldHeight = image.height;
|
||||
|
||||
scale = clamp(newScale, InlineImageView.MIN_SCALE, InlineImageView.MAX_SCALE);
|
||||
if (scale >= InlineImageView.PIXELATION_THRESHOLD) {
|
||||
img.addClass('pixelated');
|
||||
DOM.addClass(image, 'pixelated');
|
||||
} else {
|
||||
img.removeClass('pixelated');
|
||||
DOM.removeClass(image, 'pixelated');
|
||||
}
|
||||
|
||||
const { scrollTop, scrollLeft } = imgElement.parentElement;
|
||||
const dx = (scrollLeft + imgElement.parentElement.clientWidth / 2) / imgElement.parentElement.scrollWidth;
|
||||
const dy = (scrollTop + imgElement.parentElement.clientHeight / 2) / imgElement.parentElement.scrollHeight;
|
||||
const { scrollTop, scrollLeft } = image.parentElement;
|
||||
const dx = (scrollLeft + image.parentElement.clientWidth / 2) / image.parentElement.scrollWidth;
|
||||
const dy = (scrollTop + image.parentElement.clientHeight / 2) / image.parentElement.scrollHeight;
|
||||
|
||||
img.removeClass('scale-to-fit');
|
||||
img.style('min-width', `${(imgElement.naturalWidth * scale)}px`);
|
||||
img.style('width', `${(imgElement.naturalWidth * scale)}px`);
|
||||
DOM.removeClass(image, 'scale-to-fit');
|
||||
image.style.minWidth = `${(image.naturalWidth * scale)}px`;
|
||||
image.style.width = `${(image.naturalWidth * scale)}px`;
|
||||
|
||||
const newWidth = imgElement.width;
|
||||
const newWidth = image.width;
|
||||
const scaleFactor = (newWidth - oldWidth) / oldWidth;
|
||||
|
||||
const newScrollLeft = ((oldWidth * scaleFactor * dx) + scrollLeft);
|
||||
@@ -431,123 +432,131 @@ class InlineImageView {
|
||||
}
|
||||
|
||||
function firstZoom() {
|
||||
scale = imgElement.clientWidth / imgElement.naturalWidth;
|
||||
scale = image.clientWidth / image.naturalWidth;
|
||||
updateScale(scale);
|
||||
}
|
||||
|
||||
$(container)
|
||||
.on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent, c) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
ctrlPressed = e.ctrlKey;
|
||||
altPressed = e.altKey;
|
||||
disposables.push(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
ctrlPressed = e.ctrlKey;
|
||||
altPressed = e.altKey;
|
||||
|
||||
if (platform.isMacintosh ? altPressed : ctrlPressed) {
|
||||
c.removeClass('zoom-in').addClass('zoom-out');
|
||||
}
|
||||
})
|
||||
.on(DOM.EventType.KEY_UP, (e: KeyboardEvent, c) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
if (platform.isMacintosh ? altPressed : ctrlPressed) {
|
||||
DOM.removeClass(container, 'zoom-in');
|
||||
DOM.addClass(container, 'zoom-out');
|
||||
}
|
||||
}));
|
||||
|
||||
ctrlPressed = e.ctrlKey;
|
||||
altPressed = e.altKey;
|
||||
disposables.push(DOM.addDisposableListener(window, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(platform.isMacintosh ? altPressed : ctrlPressed)) {
|
||||
c.removeClass('zoom-out').addClass('zoom-in');
|
||||
}
|
||||
})
|
||||
.on(DOM.EventType.CLICK, (e: MouseEvent) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
ctrlPressed = e.ctrlKey;
|
||||
altPressed = e.altKey;
|
||||
|
||||
if (e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
if (!(platform.isMacintosh ? altPressed : ctrlPressed)) {
|
||||
DOM.removeClass(container, 'zoom-out');
|
||||
DOM.addClass(container, 'zoom-in');
|
||||
}
|
||||
}));
|
||||
|
||||
// left click
|
||||
if (scale === 'fit') {
|
||||
firstZoom();
|
||||
}
|
||||
disposables.push(DOM.addDisposableListener(container, DOM.EventType.CLICK, (e: MouseEvent) => {
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(platform.isMacintosh ? altPressed : ctrlPressed)) { // zoom in
|
||||
let i = 0;
|
||||
for (; i < InlineImageView.zoomLevels.length; ++i) {
|
||||
if (InlineImageView.zoomLevels[i] > scale) {
|
||||
break;
|
||||
}
|
||||
if (e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// left click
|
||||
if (scale === 'fit') {
|
||||
firstZoom();
|
||||
}
|
||||
|
||||
if (!(platform.isMacintosh ? altPressed : ctrlPressed)) { // zoom in
|
||||
let i = 0;
|
||||
for (; i < InlineImageView.zoomLevels.length; ++i) {
|
||||
if (InlineImageView.zoomLevels[i] > scale) {
|
||||
break;
|
||||
}
|
||||
updateScale(InlineImageView.zoomLevels[i] || InlineImageView.MAX_SCALE);
|
||||
} else {
|
||||
let i = InlineImageView.zoomLevels.length - 1;
|
||||
for (; i >= 0; --i) {
|
||||
if (InlineImageView.zoomLevels[i] < scale) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateScale(InlineImageView.zoomLevels[i] || InlineImageView.MAX_SCALE);
|
||||
} else {
|
||||
let i = InlineImageView.zoomLevels.length - 1;
|
||||
for (; i >= 0; --i) {
|
||||
if (InlineImageView.zoomLevels[i] < scale) {
|
||||
break;
|
||||
}
|
||||
updateScale(InlineImageView.zoomLevels[i] || InlineImageView.MIN_SCALE);
|
||||
}
|
||||
})
|
||||
.on(DOM.EventType.WHEEL, (e: WheelEvent) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
updateScale(InlineImageView.zoomLevels[i] || InlineImageView.MIN_SCALE);
|
||||
}
|
||||
}));
|
||||
|
||||
const isScrollWhellKeyPressed = platform.isMacintosh ? altPressed : ctrlPressed;
|
||||
if (!isScrollWhellKeyPressed && !e.ctrlKey) { // pinching is reported as scroll wheel + ctrl
|
||||
return;
|
||||
}
|
||||
disposables.push(DOM.addDisposableListener(container, DOM.EventType.WHEEL, (e: WheelEvent) => {
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const isScrollWhellKeyPressed = platform.isMacintosh ? altPressed : ctrlPressed;
|
||||
if (!isScrollWhellKeyPressed && !e.ctrlKey) { // pinching is reported as scroll wheel + ctrl
|
||||
return;
|
||||
}
|
||||
|
||||
if (scale === 'fit') {
|
||||
firstZoom();
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
let delta = e.deltaY < 0 ? 1 : -1;
|
||||
if (scale === 'fit') {
|
||||
firstZoom();
|
||||
}
|
||||
|
||||
// Pinching should increase the scale
|
||||
if (e.ctrlKey && !isScrollWhellKeyPressed) {
|
||||
delta *= -1;
|
||||
}
|
||||
updateScale(scale as number * (1 - delta * InlineImageView.SCALE_PINCH_FACTOR));
|
||||
})
|
||||
.on(DOM.EventType.SCROLL, () => {
|
||||
if (!imgElement || !imgElement.parentElement || scale === 'fit') {
|
||||
return;
|
||||
}
|
||||
let delta = e.deltaY < 0 ? 1 : -1;
|
||||
|
||||
const entry = InlineImageView.imageStateCache.get(cacheKey);
|
||||
if (entry) {
|
||||
const { scrollTop, scrollLeft } = imgElement.parentElement;
|
||||
InlineImageView.imageStateCache.set(cacheKey, { scale: entry.scale, offsetX: scrollLeft, offsetY: scrollTop });
|
||||
}
|
||||
});
|
||||
// Pinching should increase the scale
|
||||
if (e.ctrlKey && !isScrollWhellKeyPressed) {
|
||||
delta *= -1;
|
||||
}
|
||||
updateScale(scale as number * (1 - delta * InlineImageView.SCALE_PINCH_FACTOR));
|
||||
}));
|
||||
|
||||
$(container)
|
||||
.empty()
|
||||
.addClass('image', 'zoom-in')
|
||||
.img({})
|
||||
.style('visibility', 'hidden')
|
||||
.addClass('scale-to-fit')
|
||||
.on(DOM.EventType.LOAD, (e, i) => {
|
||||
img = i;
|
||||
imgElement = img.getHTMLElement() as HTMLImageElement;
|
||||
metadataClb(nls.localize('imgMeta', '{0}x{1} {2}', imgElement.naturalWidth, imgElement.naturalHeight, BinarySize.formatSize(descriptor.size)));
|
||||
scrollbar.scanDomNode();
|
||||
img.style('visibility', 'visible');
|
||||
updateScale(scale);
|
||||
if (initialState.scale !== 'fit') {
|
||||
scrollbar.setScrollPosition({
|
||||
scrollLeft: initialState.offsetX,
|
||||
scrollTop: initialState.offsetY,
|
||||
});
|
||||
}
|
||||
});
|
||||
disposables.push(DOM.addDisposableListener(container, DOM.EventType.SCROLL, () => {
|
||||
if (!image || !image.parentElement || scale === 'fit') {
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = InlineImageView.imageStateCache.get(cacheKey);
|
||||
if (entry) {
|
||||
const { scrollTop, scrollLeft } = image.parentElement;
|
||||
InlineImageView.imageStateCache.set(cacheKey, { scale: entry.scale, offsetX: scrollLeft, offsetY: scrollTop });
|
||||
}
|
||||
}));
|
||||
|
||||
DOM.clearNode(container);
|
||||
DOM.addClasses(container, 'image', 'zoom-in');
|
||||
|
||||
image = DOM.append(container, DOM.$('img.scale-to-fit'));
|
||||
image.style.visibility = 'hidden';
|
||||
|
||||
disposables.push(DOM.addDisposableListener(image, DOM.EventType.LOAD, e => {
|
||||
if (typeof descriptor.size === 'number') {
|
||||
metadataClb(nls.localize('imgMeta', '{0}x{1} {2}', image.naturalWidth, image.naturalHeight, BinarySize.formatSize(descriptor.size)));
|
||||
} else {
|
||||
metadataClb(nls.localize('imgMetaNoSize', '{0}x{1}', image.naturalWidth, image.naturalHeight));
|
||||
}
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
image.style.visibility = 'visible';
|
||||
updateScale(scale);
|
||||
if (initialState.scale !== 'fit') {
|
||||
scrollbar.setScrollPosition({
|
||||
scrollLeft: initialState.offsetX,
|
||||
scrollTop: initialState.offsetY,
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
InlineImageView.imageSrc(descriptor, fileService).then(dataUri => {
|
||||
const imgs = container.getElementsByTagName('img');
|
||||
@@ -559,9 +568,9 @@ class InlineImageView {
|
||||
return context;
|
||||
}
|
||||
|
||||
private static imageSrc(descriptor: IResourceDescriptor, fileService: IFileService): TPromise<string> {
|
||||
private static imageSrc(descriptor: IResourceDescriptor, fileService: IFileService): Thenable<string> {
|
||||
if (descriptor.resource.scheme === Schemas.data) {
|
||||
return TPromise.as(descriptor.resource.toString(true /* skip encoding */));
|
||||
return Promise.resolve(descriptor.resource.toString(true /* skip encoding */));
|
||||
}
|
||||
|
||||
return fileService.resolveContent(descriptor.resource, { encoding: 'base64' }).then(data => {
|
||||
@@ -575,7 +584,8 @@ class InlineImageView {
|
||||
function getMime(descriptor: IResourceDescriptor) {
|
||||
let mime = descriptor.mime;
|
||||
if (!mime && descriptor.resource.scheme !== Schemas.data) {
|
||||
mime = mimes.getMediaMime(descriptor.resource.toString());
|
||||
mime = mimes.getMediaMime(descriptor.resource.path);
|
||||
}
|
||||
|
||||
return mime || mimes.MIME_BINARY;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { EditorInput, EditorOptions, SideBySideEditorInput, IEditorControl, IEditor } from 'vs/workbench/common/editor';
|
||||
@@ -17,6 +16,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { SplitView, Sizing, Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Event, Relay, anyEvent, mapEvent, Emitter } from 'vs/base/common/event';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
@@ -59,9 +59,10 @@ export class SideBySideEditor extends BaseEditor {
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(SideBySideEditor.ID, telemetryService, themeService);
|
||||
super(SideBySideEditor.ID, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
protected createEditor(parent: HTMLElement): void {
|
||||
@@ -107,9 +108,11 @@ export class SideBySideEditor extends BaseEditor {
|
||||
if (this.masterEditor) {
|
||||
this.masterEditor.setVisible(visible, group);
|
||||
}
|
||||
|
||||
if (this.detailsEditor) {
|
||||
this.detailsEditor.setVisible(visible, group);
|
||||
}
|
||||
|
||||
super.setEditorVisible(visible, group);
|
||||
}
|
||||
|
||||
@@ -117,10 +120,13 @@ export class SideBySideEditor extends BaseEditor {
|
||||
if (this.masterEditor) {
|
||||
this.masterEditor.clearInput();
|
||||
}
|
||||
|
||||
if (this.detailsEditor) {
|
||||
this.detailsEditor.clearInput();
|
||||
}
|
||||
|
||||
this.disposeEditors();
|
||||
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
@@ -139,6 +145,7 @@ export class SideBySideEditor extends BaseEditor {
|
||||
if (this.masterEditor) {
|
||||
return this.masterEditor.getControl();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -159,7 +166,10 @@ export class SideBySideEditor extends BaseEditor {
|
||||
return this.setNewInput(newInput, options, token);
|
||||
}
|
||||
|
||||
return TPromise.join([this.detailsEditor.setInput(newInput.details, null, token), this.masterEditor.setInput(newInput.master, options, token)]).then(() => void 0);
|
||||
return Promise.all([
|
||||
this.detailsEditor.setInput(newInput.details, null, token),
|
||||
this.masterEditor.setInput(newInput.master, options, token)]
|
||||
).then(() => void 0);
|
||||
}
|
||||
|
||||
private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
@@ -179,7 +189,7 @@ export class SideBySideEditor extends BaseEditor {
|
||||
return editor;
|
||||
}
|
||||
|
||||
private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<void> {
|
||||
private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise<void> {
|
||||
this.detailsEditor = details;
|
||||
this.masterEditor = master;
|
||||
|
||||
@@ -190,7 +200,7 @@ export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
this.onDidCreateEditors.fire();
|
||||
|
||||
return TPromise.join([this.detailsEditor.setInput(detailsInput, null, token), this.masterEditor.setInput(masterInput, options, token)]).then(() => this.focus());
|
||||
return Promise.all([this.detailsEditor.setInput(detailsInput, null, token), this.masterEditor.setInput(masterInput, options, token)]).then(() => this.focus());
|
||||
}
|
||||
|
||||
updateStyles(): void {
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/tabstitlecontrol';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { shorten } from 'vs/base/common/labels';
|
||||
@@ -26,11 +24,10 @@ import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecyc
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { getOrSet } from 'vs/base/common/map';
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
|
||||
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, WORKBENCH_BACKGROUND, TAB_ACTIVE_BORDER_TOP, TAB_UNFOCUSED_ACTIVE_BORDER_TOP } from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder, contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, WORKBENCH_BACKGROUND, TAB_ACTIVE_BORDER_TOP, TAB_UNFOCUSED_ACTIVE_BORDER_TOP, TAB_ACTIVE_MODIFIED_BORDER, TAB_INACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER } from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder, contrastBorder, editorBackground, breadcrumbsBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ResourcesDropHandler, fillResourceDataTransfers, DraggedEditorIdentifier, DraggedEditorGroupIdentifier, DragAndDropObserver } from 'vs/workbench/browser/dnd';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -43,9 +40,7 @@ import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView } from 'vs/
|
||||
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 { 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 { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
@@ -53,6 +48,10 @@ import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/q
|
||||
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';
|
||||
// {{SQL CARBON EDIT}} -- End
|
||||
|
||||
interface IEditorInputLabel {
|
||||
name: string;
|
||||
@@ -67,7 +66,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
private titleContainer: HTMLElement;
|
||||
private tabsContainer: HTMLElement;
|
||||
private editorToolbarContainer: HTMLElement;
|
||||
private scrollbar: ScrollableElement;
|
||||
private tabsScrollbar: ScrollableElement;
|
||||
private closeOneEditorAction: CloseOneEditorAction;
|
||||
|
||||
private tabLabelWidgets: ResourceLabel[] = [];
|
||||
@@ -94,19 +93,26 @@ export class TabsTitleControl extends TitleControl {
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@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
|
||||
@IObjectExplorerService private objectExplorerService: IObjectExplorerService,
|
||||
// {{SQL CARBON EDIT}} -- End
|
||||
) {
|
||||
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService);
|
||||
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService, fileService);
|
||||
}
|
||||
|
||||
protected create(parent: HTMLElement): void {
|
||||
this.titleContainer = parent;
|
||||
|
||||
// Tabs and Actions Container (are on a single row with flex side-by-side)
|
||||
const tabsAndActionsContainer = document.createElement('div');
|
||||
addClass(tabsAndActionsContainer, 'tabs-and-actions-container');
|
||||
this.titleContainer.appendChild(tabsAndActionsContainer);
|
||||
|
||||
// Tabs Container
|
||||
this.tabsContainer = document.createElement('div');
|
||||
this.tabsContainer.setAttribute('role', 'tablist');
|
||||
@@ -114,15 +120,16 @@ export class TabsTitleControl extends TitleControl {
|
||||
addClass(this.tabsContainer, 'tabs-container');
|
||||
|
||||
// Tabs Container listeners
|
||||
this.registerContainerListeners();
|
||||
this.registerTabsContainerListeners();
|
||||
|
||||
// Scrollbar
|
||||
this.createScrollbar();
|
||||
// Tabs Scrollbar
|
||||
this.tabsScrollbar = this.createTabsScrollbar(this.tabsContainer);
|
||||
tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode());
|
||||
|
||||
// Editor Toolbar Container
|
||||
this.editorToolbarContainer = document.createElement('div');
|
||||
addClass(this.editorToolbarContainer, 'editor-actions');
|
||||
this.titleContainer.appendChild(this.editorToolbarContainer);
|
||||
tabsAndActionsContainer.appendChild(this.editorToolbarContainer);
|
||||
|
||||
// Editor Actions Toolbar
|
||||
this.createEditorActionsToolBar(this.editorToolbarContainer);
|
||||
@@ -130,17 +137,15 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Close Action
|
||||
this.closeOneEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL));
|
||||
|
||||
// Breadcrumbs
|
||||
// Breadcrumbs (are on a separate row below tabs and actions)
|
||||
const breadcrumbsContainer = document.createElement('div');
|
||||
addClass(breadcrumbsContainer, 'tabs-breadcrumbs');
|
||||
this.titleContainer.appendChild(breadcrumbsContainer);
|
||||
this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, extraClasses: [] });
|
||||
this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: breadcrumbsBackground });
|
||||
}
|
||||
|
||||
private createScrollbar(): void {
|
||||
|
||||
// Custom Scrollbar
|
||||
this.scrollbar = new ScrollableElement(this.tabsContainer, {
|
||||
private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement {
|
||||
const tabsScrollbar = new ScrollableElement(scrollable, {
|
||||
horizontal: ScrollbarVisibility.Auto,
|
||||
vertical: ScrollbarVisibility.Hidden,
|
||||
scrollYToX: true,
|
||||
@@ -148,11 +153,11 @@ export class TabsTitleControl extends TitleControl {
|
||||
horizontalScrollbarSize: 3
|
||||
});
|
||||
|
||||
this.scrollbar.onScroll(e => {
|
||||
this.tabsContainer.scrollLeft = e.scrollLeft;
|
||||
tabsScrollbar.onScroll(e => {
|
||||
scrollable.scrollLeft = e.scrollLeft;
|
||||
});
|
||||
|
||||
this.titleContainer.appendChild(this.scrollbar.getDomNode());
|
||||
return tabsScrollbar;
|
||||
}
|
||||
|
||||
private updateBreadcrumbsControl(): void {
|
||||
@@ -163,7 +168,12 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
}
|
||||
|
||||
private registerContainerListeners(): void {
|
||||
protected handleBreadcrumbsEnablementChange(): void {
|
||||
// relayout when breadcrumbs are enable/disabled
|
||||
this.group.relayout();
|
||||
}
|
||||
|
||||
private registerTabsContainerListeners(): void {
|
||||
|
||||
// Group dragging
|
||||
this.enableGroupDragging(this.tabsContainer);
|
||||
@@ -171,7 +181,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Forward scrolling inside the container to our custom scrollbar
|
||||
this._register(addDisposableListener(this.tabsContainer, EventType.SCROLL, () => {
|
||||
if (hasClass(this.tabsContainer, 'scroll')) {
|
||||
this.scrollbar.setScrollPosition({
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: this.tabsContainer.scrollLeft // during DND the container gets scrolled so we need to update the custom scrollbar
|
||||
});
|
||||
}
|
||||
@@ -182,7 +192,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
if (e.target === this.tabsContainer) {
|
||||
EventHelper.stop(e);
|
||||
// {{SQL CARBON EDIT}}
|
||||
this.commandService.executeCommand(GlobalNewUntitledFileAction.ID).done(undefined, err => this.notificationService.warn(err));
|
||||
this.commandService.executeCommand(GlobalNewUntitledFileAction.ID).then(undefined, err => this.notificationService.warn(err));
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -354,7 +364,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Activity has an impact on each tab
|
||||
this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel) => {
|
||||
this.redrawEditorActive(isGroupActive, editor, tabContainer, tabLabelWidget);
|
||||
this.redrawEditorActiveAndDirty(isGroupActive, editor, tabContainer, tabLabelWidget);
|
||||
});
|
||||
|
||||
// Activity has an impact on the toolbar, so we need to update and layout
|
||||
@@ -377,7 +387,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
updateEditorDirty(editor: IEditorInput): void {
|
||||
this.withTab(editor, tabContainer => this.redrawEditorDirty(editor, tabContainer));
|
||||
this.withTab(editor, (tabContainer, tabLabelWidget) => this.redrawEditorActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget));
|
||||
}
|
||||
|
||||
updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void {
|
||||
@@ -393,7 +403,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
oldOptions.tabCloseButton !== newOptions.tabCloseButton ||
|
||||
oldOptions.tabSizing !== newOptions.tabSizing ||
|
||||
oldOptions.showIcons !== newOptions.showIcons ||
|
||||
oldOptions.iconTheme !== newOptions.iconTheme
|
||||
oldOptions.iconTheme !== newOptions.iconTheme ||
|
||||
oldOptions.highlightModifiedTabs !== newOptions.highlightModifiedTabs
|
||||
) {
|
||||
this.redraw();
|
||||
}
|
||||
@@ -493,7 +504,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Touch Scroll Support
|
||||
disposables.push(addDisposableListener(tab, TouchEventType.Change, (e: GestureEvent) => {
|
||||
this.scrollbar.setScrollPosition({ scrollLeft: this.scrollbar.getScrollPosition().scrollLeft - e.translationX });
|
||||
this.tabsScrollbar.setScrollPosition({ scrollLeft: this.tabsScrollbar.getScrollPosition().scrollLeft - e.translationX });
|
||||
}));
|
||||
|
||||
// Close on mouse middle click
|
||||
@@ -502,7 +513,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
tab.blur();
|
||||
|
||||
if (e.button === 1 /* Middle Button*/ && !this.originatesFromTabActionBar(e)) {
|
||||
if (e.button === 1 /* Middle Button*/) {
|
||||
e.stopPropagation(); // for https://github.com/Microsoft/vscode/issues/56715
|
||||
|
||||
this.blockRevealActiveTabOnce();
|
||||
this.closeOneEditorAction.run({ groupId: this.group.id, editorIndex: index });
|
||||
}
|
||||
@@ -558,7 +571,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
// moving in the tabs container can have an impact on scrolling position, so we need to update the custom scrollbar
|
||||
this.scrollbar.setScrollPosition({
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: this.tabsContainer.scrollLeft
|
||||
});
|
||||
}));
|
||||
@@ -838,9 +851,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
this.redrawLabel(editor, tabContainer, tabLabelWidget, tabLabel);
|
||||
|
||||
// Borders / Outline
|
||||
const borderLeftColor = (index !== 0) ? (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)) : null;
|
||||
const borderRightColor = (index === this.group.count - 1) ? (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)) : null;
|
||||
tabContainer.style.borderLeft = borderLeftColor ? `1px solid ${borderLeftColor}` : null;
|
||||
const borderRightColor = (this.getColor(TAB_BORDER) || this.getColor(contrastBorder));
|
||||
tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : null;
|
||||
tabContainer.style.outlineColor = this.getColor(activeContrastBorder);
|
||||
|
||||
@@ -863,12 +874,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
removeClass(tabContainer, 'has-icon-theme');
|
||||
}
|
||||
|
||||
// Active state
|
||||
this.redrawEditorActive(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget);
|
||||
|
||||
// Dirty State
|
||||
this.redrawEditorDirty(editor, tabContainer);
|
||||
|
||||
// Active / dirty state
|
||||
this.redrawEditorActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget);
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
this.setEditorTabColor(editor, tabContainer, this.group.isActive(editor));
|
||||
}
|
||||
@@ -883,15 +890,22 @@ export class TabsTitleControl extends TitleControl {
|
||||
tabContainer.title = title;
|
||||
|
||||
// Label
|
||||
// {{SQL CARBON EDIT}} -- add title in options passed
|
||||
tabLabelWidget.setLabel({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { extraClasses: ['tab-label'], italic: !this.group.isPinned(editor), title });
|
||||
tabLabelWidget.setLabel({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
const isTabActive = this.group.isActive(editor);
|
||||
this.setEditorTabColor(editor, tabContainer, isTabActive);
|
||||
}
|
||||
|
||||
private redrawEditorActive(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel): void {
|
||||
private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel): void {
|
||||
const isTabActive = this.group.isActive(editor);
|
||||
|
||||
const hasModifiedBorderTop = this.doRedrawEditorDirty(isGroupActive, isTabActive, editor, tabContainer);
|
||||
|
||||
this.doRedrawEditorActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabLabelWidget);
|
||||
}
|
||||
|
||||
private doRedrawEditorActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel): void {
|
||||
|
||||
// Tab is active
|
||||
if (this.group.isActive(editor)) {
|
||||
@@ -910,7 +924,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
tabContainer.style.removeProperty('--tab-border-bottom-color');
|
||||
}
|
||||
|
||||
const activeTabBorderColorTop = this.getColor(isGroupActive ? TAB_ACTIVE_BORDER_TOP : TAB_UNFOCUSED_ACTIVE_BORDER_TOP);
|
||||
const activeTabBorderColorTop = allowBorderTop ? this.getColor(isGroupActive ? TAB_ACTIVE_BORDER_TOP : TAB_UNFOCUSED_ACTIVE_BORDER_TOP) : void 0;
|
||||
if (activeTabBorderColorTop) {
|
||||
addClass(tabContainer, 'tab-border-top');
|
||||
tabContainer.style.setProperty('--tab-border-top-color', activeTabBorderColorTop.toString());
|
||||
@@ -926,7 +940,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Tab is inactive
|
||||
else {
|
||||
|
||||
// Containr
|
||||
// Container
|
||||
removeClass(tabContainer, 'active');
|
||||
tabContainer.setAttribute('aria-selected', 'false');
|
||||
tabContainer.style.backgroundColor = this.getColor(TAB_INACTIVE_BACKGROUND);
|
||||
@@ -937,22 +951,57 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
}
|
||||
|
||||
private redrawEditorDirty(editor: IEditorInput, tabContainer: HTMLElement): void {
|
||||
private doRedrawEditorDirty(isGroupActive: boolean, isTabActive: boolean, editor: IEditorInput, tabContainer: HTMLElement): boolean {
|
||||
let hasModifiedBorderColor = false;
|
||||
|
||||
// Tab: dirty
|
||||
if (editor.isDirty()) {
|
||||
addClass(tabContainer, 'dirty');
|
||||
} else {
|
||||
removeClass(tabContainer, 'dirty');
|
||||
|
||||
// Highlight modified tabs with a border if configured
|
||||
if (this.accessor.partOptions.highlightModifiedTabs) {
|
||||
let modifiedBorderColor: string;
|
||||
if (isGroupActive && isTabActive) {
|
||||
modifiedBorderColor = this.getColor(TAB_ACTIVE_MODIFIED_BORDER);
|
||||
} else if (isGroupActive && !isTabActive) {
|
||||
modifiedBorderColor = this.getColor(TAB_INACTIVE_MODIFIED_BORDER);
|
||||
} else if (!isGroupActive && isTabActive) {
|
||||
modifiedBorderColor = this.getColor(TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER);
|
||||
} else {
|
||||
modifiedBorderColor = this.getColor(TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER);
|
||||
}
|
||||
|
||||
if (modifiedBorderColor) {
|
||||
hasModifiedBorderColor = true;
|
||||
|
||||
addClass(tabContainer, 'dirty-border-top');
|
||||
tabContainer.style.setProperty('--tab-dirty-border-top-color', modifiedBorderColor);
|
||||
}
|
||||
} else {
|
||||
removeClass(tabContainer, 'dirty-border-top');
|
||||
tabContainer.style.removeProperty('--tab-dirty-border-top-color');
|
||||
}
|
||||
}
|
||||
|
||||
// Tab: not dirty
|
||||
else {
|
||||
removeClass(tabContainer, 'dirty');
|
||||
|
||||
removeClass(tabContainer, 'dirty-border-top');
|
||||
tabContainer.style.removeProperty('--tab-dirty-border-top-color');
|
||||
}
|
||||
|
||||
return hasModifiedBorderColor;
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
|
||||
const activeTab = this.getTab(this.group.activeEditor);
|
||||
if (!activeTab || !dimension) {
|
||||
if (!activeTab || !this.dimension) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dimension = dimension;
|
||||
|
||||
// The layout of tabs can be an expensive operation because we access DOM properties
|
||||
// that can result in the browser doing a full page layout to validate them. To buffer
|
||||
// this a little bit we try at least to schedule this work on the next animation frame.
|
||||
@@ -972,7 +1021,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
if (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden()) {
|
||||
this.breadcrumbsControl.layout({ width: dimension.width, height: BreadcrumbsControl.HEIGHT });
|
||||
this.scrollbar.getDomNode().style.height = `${dimension.height - BreadcrumbsControl.HEIGHT}px`;
|
||||
this.tabsScrollbar.getDomNode().style.height = `${dimension.height - BreadcrumbsControl.HEIGHT}px`;
|
||||
}
|
||||
|
||||
const visibleContainerWidth = this.tabsContainer.offsetWidth;
|
||||
@@ -987,7 +1036,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
// Update scrollbar
|
||||
this.scrollbar.setScrollDimensions({
|
||||
this.tabsScrollbar.setScrollDimensions({
|
||||
width: visibleContainerWidth,
|
||||
scrollWidth: totalContainerWidth
|
||||
});
|
||||
@@ -999,20 +1048,20 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
// Reveal the active one
|
||||
const containerScrollPosX = this.scrollbar.getScrollPosition().scrollLeft;
|
||||
const containerScrollPosX = this.tabsScrollbar.getScrollPosition().scrollLeft;
|
||||
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) {
|
||||
this.scrollbar.setScrollPosition({
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
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) {
|
||||
this.scrollbar.setScrollPosition({
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: activeTabPosX
|
||||
});
|
||||
}
|
||||
@@ -1247,7 +1296,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
const adjustedColorDrag = editorDragAndDropBackground.flatten(adjustedTabDragBackground);
|
||||
collector.addRule(`
|
||||
.monaco-workbench > .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.active):not(.dragged) > .tab-label::after,
|
||||
.monaco-workbench > .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.dragged) > .tab-label::after {
|
||||
.monaco-workbench > .part.editor > .content.dragged-over .editor-group-container:not(.active) > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.dragged) > .tab-label::after {
|
||||
background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important;
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -3,13 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/textdiffeditor';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -28,15 +23,15 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/common/editorCommon';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/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';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
|
||||
/**
|
||||
* The text editor that leverages the diff text editor for the editing experience.
|
||||
@@ -47,31 +42,22 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
|
||||
private diffNavigator: DiffNavigator;
|
||||
private diffNavigatorDisposables: IDisposable[] = [];
|
||||
private nextDiffAction: NavigateAction;
|
||||
private previousDiffAction: NavigateAction;
|
||||
private toggleIgnoreTrimWhitespaceAction: ToggleIgnoreTrimWhitespaceAction;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
|
||||
@IConfigurationService private readonly _actualConfigurationService: IConfigurationService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IWindowService windowService: IWindowService
|
||||
) {
|
||||
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService);
|
||||
|
||||
this._register(this._actualConfigurationService.onDidChangeConfiguration((e) => {
|
||||
if (e.affectsConfiguration('diffEditor.ignoreTrimWhitespace')) {
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
}
|
||||
}));
|
||||
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, windowService);
|
||||
}
|
||||
|
||||
protected getEditorMemento<T>(storageService: IStorageService, editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
protected getEditorMemento<T>(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted
|
||||
}
|
||||
|
||||
@@ -84,13 +70,6 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
}
|
||||
|
||||
createEditorControl(parent: HTMLElement, configuration: ICodeEditorOptions): IDiffEditor {
|
||||
|
||||
// Actions
|
||||
this.nextDiffAction = new NavigateAction(this, true);
|
||||
this.previousDiffAction = new NavigateAction(this, false);
|
||||
this.toggleIgnoreTrimWhitespaceAction = new ToggleIgnoreTrimWhitespaceAction(this._actualConfigurationService);
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
|
||||
return this.instantiationService.createInstance(DiffEditorWidget, parent, configuration);
|
||||
}
|
||||
|
||||
@@ -139,14 +118,6 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
});
|
||||
this.diffNavigatorDisposables.push(this.diffNavigator);
|
||||
|
||||
this.diffNavigatorDisposables.push(this.diffNavigator.onDidUpdate(() => {
|
||||
this.nextDiffAction.updateEnablement();
|
||||
this.previousDiffAction.updateEnablement();
|
||||
}));
|
||||
|
||||
// Enablement of actions
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
|
||||
// Readonly flag
|
||||
diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() });
|
||||
}, error => {
|
||||
@@ -157,7 +128,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
}
|
||||
|
||||
// Otherwise make sure the error bubbles up
|
||||
return TPromise.wrapError(error);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -185,13 +156,6 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private updateIgnoreTrimWhitespaceAction(): void {
|
||||
const ignoreTrimWhitespace = this.configurationService.getValue<boolean>(this.getResource(), 'diffEditor.ignoreTrimWhitespace');
|
||||
if (this.toggleIgnoreTrimWhitespaceAction) {
|
||||
this.toggleIgnoreTrimWhitespaceAction.updateClassName(ignoreTrimWhitespace);
|
||||
}
|
||||
}
|
||||
|
||||
private openAsBinary(input: EditorInput, options: EditorOptions): boolean {
|
||||
if (input instanceof DiffEditorInput) {
|
||||
const originalInput = input.originalInput;
|
||||
@@ -232,6 +196,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
const options: IDiffEditorOptions = super.getConfigurationOverrides();
|
||||
|
||||
options.readOnly = this.isReadOnly();
|
||||
options.lineDecorationsWidth = '2ch';
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -289,14 +254,6 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
return this.diffNavigator;
|
||||
}
|
||||
|
||||
getActions(): IAction[] {
|
||||
return [
|
||||
this.toggleIgnoreTrimWhitespaceAction,
|
||||
this.previousDiffAction,
|
||||
this.nextDiffAction
|
||||
];
|
||||
}
|
||||
|
||||
getControl(): IDiffEditor {
|
||||
return super.getControl() as IDiffEditor;
|
||||
}
|
||||
@@ -380,59 +337,3 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class NavigateAction extends Action {
|
||||
static ID_NEXT = 'workbench.action.compareEditor.nextChange';
|
||||
static ID_PREV = 'workbench.action.compareEditor.previousChange';
|
||||
|
||||
private editor: TextDiffEditor;
|
||||
private next: boolean;
|
||||
|
||||
constructor(editor: TextDiffEditor, next: boolean) {
|
||||
super(next ? NavigateAction.ID_NEXT : NavigateAction.ID_PREV);
|
||||
|
||||
this.editor = editor;
|
||||
this.next = next;
|
||||
|
||||
this.label = this.next ? nls.localize('navigate.next.label', "Next Change") : nls.localize('navigate.prev.label', "Previous Change");
|
||||
this.class = this.next ? 'textdiff-editor-action next' : 'textdiff-editor-action previous';
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
if (this.next) {
|
||||
this.editor.getDiffNavigator().next();
|
||||
} else {
|
||||
this.editor.getDiffNavigator().previous();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
updateEnablement(): void {
|
||||
this.enabled = this.editor.getDiffNavigator().canNavigate();
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleIgnoreTrimWhitespaceAction extends Action {
|
||||
static ID = 'workbench.action.compareEditor.toggleIgnoreTrimWhitespace';
|
||||
|
||||
private _isChecked: boolean;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
super(ToggleIgnoreTrimWhitespaceAction.ID);
|
||||
this.label = nls.localize('toggleIgnoreTrimWhitespace.label', "Ignore Trim Whitespace");
|
||||
}
|
||||
|
||||
updateClassName(ignoreTrimWhitespace: boolean): void {
|
||||
this._isChecked = ignoreTrimWhitespace;
|
||||
this.class = `textdiff-editor-action toggleIgnoreTrimWhitespace${this._isChecked ? ' is-checked' : ''}`;
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
this._configurationService.updateValue(`diffEditor.ignoreTrimWhitespace`, !this._isChecked);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { EditorInput, EditorOptions, IEditorMemento, ITextEditor } from 'vs/workbench/common/editor';
|
||||
@@ -26,6 +23,7 @@ import { isDiffEditor, isCodeEditor, ICodeEditor, getCodeEditor } from 'vs/edito
|
||||
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/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';
|
||||
|
||||
const TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'textEditorViewState';
|
||||
|
||||
@@ -55,10 +53,11 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
@ITextFileService private readonly _textFileService: ITextFileService,
|
||||
@IEditorService protected editorService: IEditorService,
|
||||
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
|
||||
@IWindowService private windowService: IWindowService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
|
||||
this.editorMemento = this.getEditorMemento<IEditorViewState>(storageService, editorGroupService, TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
|
||||
this.editorMemento = this.getEditorMemento<IEditorViewState>(editorGroupService, TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
|
||||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
|
||||
}
|
||||
@@ -75,7 +74,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
return this._textFileService;
|
||||
}
|
||||
|
||||
private handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void {
|
||||
protected handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void {
|
||||
if (this.isVisible()) {
|
||||
this.updateEditorConfiguration(configuration);
|
||||
} else {
|
||||
@@ -148,15 +147,17 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
}
|
||||
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.onEditorFocusLost()));
|
||||
this._register(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => this.onWindowFocusLost()));
|
||||
this._register(this.windowService.onDidChangeFocus(focused => this.onWindowFocusChange(focused)));
|
||||
}
|
||||
|
||||
private onEditorFocusLost(): void {
|
||||
this.maybeTriggerSaveAll(SaveReason.FOCUS_CHANGE);
|
||||
}
|
||||
|
||||
private onWindowFocusLost(): void {
|
||||
this.maybeTriggerSaveAll(SaveReason.WINDOW_CHANGE);
|
||||
private onWindowFocusChange(focused: boolean): void {
|
||||
if (!focused) {
|
||||
this.maybeTriggerSaveAll(SaveReason.WINDOW_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
private maybeTriggerSaveAll(reason: SaveReason): void {
|
||||
@@ -169,7 +170,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
(reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE)
|
||||
) {
|
||||
if (this.textFileService.isDirty()) {
|
||||
this.textFileService.saveAll(void 0, { reason }).done(null, errors.onUnexpectedError);
|
||||
this.textFileService.saveAll(void 0, { reason });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +233,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editorMemento.saveState(this.group, resource, editorViewState);
|
||||
this.editorMemento.saveEditorState(this.group, resource, editorViewState);
|
||||
}
|
||||
|
||||
protected retrieveTextEditorViewState(resource: URI): IEditorViewState {
|
||||
@@ -257,9 +258,9 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
/**
|
||||
* Clears the text editor view state for the given resources.
|
||||
*/
|
||||
protected clearTextEditorViewState(resources: URI[]): void {
|
||||
protected clearTextEditorViewState(resources: URI[], group?: IEditorGroup): void {
|
||||
resources.forEach(resource => {
|
||||
this.editorMemento.clearState(resource);
|
||||
this.editorMemento.clearEditorState(resource, group);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -267,7 +268,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
* Loads the text editor view state for the given resource and returns it.
|
||||
*/
|
||||
protected loadTextEditorViewState(resource: URI): IEditorViewState {
|
||||
return this.editorMemento.loadState(this.group, resource);
|
||||
return this.editorMemento.loadEditorState(this.group, resource);
|
||||
}
|
||||
|
||||
private updateEditorConfiguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -25,6 +23,7 @@ import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/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';
|
||||
|
||||
/**
|
||||
* An editor implementation that is capable of showing the contents of resource inputs. Uses
|
||||
@@ -41,9 +40,10 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IEditorService editorService: IEditorService
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IWindowService windowService: IWindowService
|
||||
) {
|
||||
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService);
|
||||
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, windowService);
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
@@ -70,7 +70,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
|
||||
// Assert Model instance
|
||||
if (!(resolvedModel instanceof BaseTextEditorModel)) {
|
||||
return TPromise.wrapError<void>(new Error('Unable to open file as text'));
|
||||
return Promise.reject(new Error('Unable to open file as text'));
|
||||
}
|
||||
|
||||
// Set Editor Model
|
||||
@@ -163,15 +163,14 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
|
||||
// Save View State (only for untitled)
|
||||
if (this.input instanceof UntitledEditorInput) {
|
||||
this.saveTextResourceEditorViewState(this.input);
|
||||
}
|
||||
|
||||
// Call Super
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
private saveTextResourceEditorViewState(input: EditorInput): void {
|
||||
@@ -210,8 +209,9 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IWindowService windowService: IWindowService
|
||||
) {
|
||||
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, textFileService, editorService);
|
||||
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, textFileService, editorService, windowService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { applyDragImage } from 'vs/base/browser/dnd';
|
||||
import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
@@ -14,7 +12,6 @@ import { Action, IAction, IRunEvent } from 'vs/base/common/actions';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import 'vs/css!./media/titlecontrol';
|
||||
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -40,6 +37,8 @@ import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput
|
||||
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';
|
||||
|
||||
export interface IToolbarActions {
|
||||
primary: IAction[];
|
||||
@@ -57,8 +56,6 @@ export abstract class TitleControl extends Themable {
|
||||
private currentSecondaryEditorActionIds: string[] = [];
|
||||
protected editorActionsToolbar: ToolBar;
|
||||
|
||||
private mapEditorToActions: Map<string, IToolbarActions> = new Map();
|
||||
|
||||
private resourceContext: ResourceContextKey;
|
||||
private editorToolBarMenuDisposables: IDisposable[] = [];
|
||||
|
||||
@@ -79,11 +76,12 @@ export abstract class TitleControl extends Themable {
|
||||
@IQuickOpenService protected quickOpenService: IQuickOpenService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IExtensionService private extensionService: IExtensionService,
|
||||
@IConfigurationService protected configurationService: IConfigurationService
|
||||
@IConfigurationService protected configurationService: IConfigurationService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.resourceContext = instantiationService.createInstance(ResourceContextKey);
|
||||
this.resourceContext = this._register(instantiationService.createInstance(ResourceContextKey));
|
||||
this.contextMenu = this._register(this.menuService.createMenu(MenuId.EditorTitleContext, this.contextKeyService));
|
||||
|
||||
this.create(parent);
|
||||
@@ -98,22 +96,31 @@ export abstract class TitleControl extends Themable {
|
||||
|
||||
protected createBreadcrumbsControl(container: HTMLElement, options: IBreadcrumbsControlOptions): void {
|
||||
const config = this._register(BreadcrumbsConfig.IsEnabled.bindTo(this.configurationService));
|
||||
config.onDidChange(value => {
|
||||
this._register(config.onDidChange(() => {
|
||||
const value = config.getValue();
|
||||
if (!value && this.breadcrumbsControl) {
|
||||
this.breadcrumbsControl.dispose();
|
||||
this.breadcrumbsControl = undefined;
|
||||
this.group.relayout();
|
||||
this.handleBreadcrumbsEnablementChange();
|
||||
} else if (value && !this.breadcrumbsControl) {
|
||||
this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group);
|
||||
this.breadcrumbsControl.update();
|
||||
this.group.relayout();
|
||||
this.handleBreadcrumbsEnablementChange();
|
||||
}
|
||||
});
|
||||
if (config.value) {
|
||||
}));
|
||||
if (config.getValue()) {
|
||||
this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group);
|
||||
}
|
||||
|
||||
this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(() => {
|
||||
if (this.breadcrumbsControl && this.breadcrumbsControl.update()) {
|
||||
this.handleBreadcrumbsEnablementChange();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected abstract handleBreadcrumbsEnablementChange(): void;
|
||||
|
||||
protected createEditorActionsToolBar(container: HTMLElement): void {
|
||||
const context = { groupId: this.group.id } as IEditorCommandsContext;
|
||||
|
||||
@@ -122,7 +129,8 @@ export abstract class TitleControl extends Themable {
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
ariaLabel: localize('araLabelEditorActions', "Editor actions"),
|
||||
getKeyBinding: action => this.getKeybinding(action),
|
||||
actionRunner: this._register(new EditorCommandsContextActionRunner(context))
|
||||
actionRunner: this._register(new EditorCommandsContextActionRunner(context)),
|
||||
anchorAlignmentProvider: () => AnchorAlignment.RIGHT
|
||||
}));
|
||||
|
||||
// Context
|
||||
@@ -215,17 +223,6 @@ export abstract class TitleControl extends Themable {
|
||||
// Editor actions require the editor control to be there, so we retrieve it via service
|
||||
const activeControl = this.group.activeControl;
|
||||
if (activeControl instanceof BaseEditor) {
|
||||
|
||||
// Editor Control Actions
|
||||
let editorActions = this.mapEditorToActions.get(activeControl.getId());
|
||||
if (!editorActions) {
|
||||
editorActions = { primary: activeControl.getActions(), secondary: activeControl.getSecondaryActions() };
|
||||
this.mapEditorToActions.set(activeControl.getId(), editorActions);
|
||||
}
|
||||
primary.push(...editorActions.primary);
|
||||
secondary.push(...editorActions.secondary);
|
||||
|
||||
// Contributed Actions
|
||||
const codeEditor = getCodeEditor(activeControl.getControl());
|
||||
const scopedContextKeyService = codeEditor && codeEditor.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
|
||||
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
|
||||
@@ -302,7 +299,7 @@ export abstract class TitleControl extends Themable {
|
||||
// Show it
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => TPromise.as(actions),
|
||||
getActions: () => actions,
|
||||
getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) } as IEditorCommandsContext),
|
||||
getKeyBinding: (action) => this.getKeybinding(action),
|
||||
onHide: () => {
|
||||
@@ -316,7 +313,7 @@ export abstract class TitleControl extends Themable {
|
||||
});
|
||||
}
|
||||
|
||||
protected getKeybinding(action: IAction): ResolvedKeybinding {
|
||||
private getKeybinding(action: IAction): ResolvedKeybinding {
|
||||
return this.keybindingService.lookupKeybinding(action.id);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user