Merge VS Code 1.26.1 (#2394)

* Squash merge commits for 1.26 (#1) (#2323)

* Polish tag search as per feedback (#55269)

* Polish tag search as per feedback

* Updated regex

* Allow users to opt-out of features that send online requests in the background (#55097)

* settings sweep #54690

* Minor css tweaks to enable eoverflow elipsis in more places (#55277)

* fix an issue with titlebarheight when not scaling with zoom

* Settings descriptions update #54690

* fixes #55209

* Settings editor - many padding fixes

* More space above level 2 label

* Fixing Cannot debug npm script using Yarn #55103

* Settings editor - show ellipsis when description overflows

* Settings editor - ... fix measuring around links, relayout

* Setting descriptions

* Settings editor - fix ... for some short lines, fix select container width

* Settings editor - overlay trees so scrollable shadow is full width

* Fix #54133 - missing extension settings after reload

* Settings color token description tweak

* Settings editor - disable overflow indicator temporarily, needs to be faster

* Added command to Run the selected npm script

* fixes #54452

* fixes #54929

* fixes #55248

* prefix command with extension name

* Contribute run selected to the context menu

* node-debug@1.26.6

* Allow terminal rendererType to be swapped out at runtime

Part of #53274
Fixes #55344

* Settings editor - fix not focusing search when restoring editor
setInput must be actually async. Will be fixed naturally when we aren't using winJS promises...

* Settings editor - TOC should only expand the section with a selected item

* Bump node-debug2

* Settings editor - Tree focus outlines

* Settings editor - don't blink the scrollbar when toc selection changes
And hide TOC correctly when the editor is narrow

* Settings editor - header rows should not be selectable

* fixes #54877

* change debug assignee to isi

* Settings sweep (#54690)

* workaround for #55051

* Settings sweep (#54690)

* settings sweep

#54690

* Don't try closing tags when you type > after another >

* Describe what implementation code lens does

Fixes #55370

* fix javadoc formatter setting description

* fixes #55325

* update to officical TS version

* Settings editor - Even more padding, use semibold instead of bold

* Fix #55357 - fix TOC twistie

* fixes #55288

* explorer: refresh on di change file system provider registration

fixes #53256

* Disable push to Linux repo to test standalone publisher

* New env var to notify log level to extensions #54001

* Disable snippets in extension search (when not in suggest dropdown) (#55281)

* Disable snippits in extension search (when not in suggest dropdown)

* Add monaco input contributions

* Fix bug preventing snippetSuggestions from taking effect in sub-editors

* Latest emmet helper to fix #52366

* Fix comment updates for threads within same file

* Allow extensions to log telemetry to log files #54001

* Pull latest css grammar

* files.exclude control - use same style for "add" vs "edit"

* files.exclude control - focus/keyboard behavior

* don't show menubar too early

* files.exclude - better styling

* Place cursor at end of extensions search box on autofill (#55254)

* Place cursor at end of extensions search box on autofill

* Use position instead of selection

* fix linux build issue (empty if block)

* Settings editor - fix extension category prefixes

* Settings editor - add simple ellipsis for first line that overflows, doesn't cover case when first line does not overflow but there is more text, TODO

* File/Text search provider docs

* Fixes #52655

* Include epoch (#55008)

* Fixes #53385

* Fixes #49480

*  VS Code Insiders (Users) not opening Fixes #55353

* Better handling of the case when the extension host fails to start

* Fixes #53966

*  Remove confusing Start from wordPartLeft commands ID

* vscode-xterm@3.6.0-beta12

Fixes #55488

* Initial size is set to infinity!! Fixes #55461

* Polish embeddedEditorBackground

* configuration service misses event

* Fix #55224 - fix duplicate results in multiroot workspace from splitting the diskseach query

* Select all not working in issue reporter on mac, fixes #55424

* Disable fuzzy matching for extensions autosuggest (#55498)

* Fix clipping of extensions search border in some third party themes (#55504)

* fixes #55538

* Fix bug causing an aria alert to not be shown the third time
 (and odd numbers thereafter)

* Settings editor - work around rendering glitch with webkit-line-clamp

* Settings editor - revert earlier '...' changes

* Settings editor - move enumDescription to its own div, because it disturbs -webkit-line-clamp for some reason

* Settings editor - better overflow indicator

* Don't show existing filters in autocomplete (#55495)

* Dont show existing filters in autocomplete

* Simplify

* Settings Editor: Add aria labels for input elements Fixes: #54836 (#55543)

* fixes #55223

* Update vscode-css-languageservice to 3.0.10-next.1

* Fix #55509 - settings navigation

* Fix #55519

* Fix #55520

* FIx #55524

* Fix #55556 - include wordSeparators in all search queries, so findTextInFiles can respect isWordMatch correctly

* oss updates for endgame

* Fix unit tests

* fixes #55522

* Avoid missing manifest error from bubbling up #54757

* Settings format crawl

* Search provider - Fix FileSearchProvider to return array, not progress

* Fix #55598

* Settings editor - fix NPE rendering settings with no description

* dont render inden guides in search box (#55600)

* fixes #55454

* More settings crawl

* Another change for #55598 - maxResults applies to FileSearch and TextSearch but not FileIndex

* Fix FileSearchProvider unit tests for progress change

* fixes #55561

* Settings description update for #54690

* Update setting descriptions for online services

* Minor edits

* fixes #55513

* fixes #55451

* Fix #55612 - fix findTextInFiles cancellation

* fixes #55539

* More setting description tweaks

* Setting to disable online experiments #54354

* fixes #55507

* fixes #55515

* Show online services action only in Insiders for now

* Settings editor - change toc behavior default to 'filter'

* Settings editor - nicer filter count style during search

* Fix #55617 - search viewlet icons

* Settings editor - better styling for element count indicator

* SearchProvider - fix NPE when searching extraFileResources

* Allow extends to work without json suffix

Fixes #16905

* Remove accessability options logic entirely

Follow up on #55451

* use latest version of DAP

* fixes #55490

* fixes #55122

* fixes #52332

* Avoid assumptions about git: URIs (fixes #36236)

* relative path for descriptions

* resourece: get rid of isFile context key

fixes #48275

* Register previous ids for compatibility (#53497)

* more tuning for #48275

* no need to always re-read "files explorer"

fixes #52003

* read out active composites properly

fixes #51967

* Update link colors for hc theme to meet color contrast ratio, fixes #55651

Also updated link color for `textLinkActiveForeground` to be the same as `textLinkForeground` as it wasn't properly updated

* detect 'winpty-agent.exe'; fixes #55672

* node-debug@1.26.7

* reset counter on new label

* Settings editor - fix multiple setting links in one description

* Settings editor - color code blocks in setting descriptions, fix #55532

* Settings editor - hover color in TOC

* Settings editor - fix navigation NPE

* Settings editor - fix text control width

* Settings editor - maybe fix #55684

* Fix bug causing cursor to not move on paste

* fixes #53582

* Use ctrlCmd instead of ctrl for go down from search box

* fixes #55264

* fixes #55456

* filter for spcaes before triggering search (#55611)

* Fix #55698 - don't lose filtered TOC counts when refreshing TOC

* fixes #55421

* fixes #28979

* fixes #55576

* only add check for updates to windows/linux help

* readonly files: append decoration to label

fixes #53022

* debug: do not show toolbar while initialising

fixes #55026

* Opening launch.json should not activate debug extensions

fixes #55029

* fixes #55435

* fixes #55434

* fixes #55439

* trigger menu only on altkey up

* Fix #50555 - fix settings editor memory leak

* Fix #55712 - no need to focus 'a' anymore when restoring control focus after tree render

* fixes #55335

* proper fix for readonly model

fixes #53022

* improve FoldingRangeKind spec (for #55686)

* Use class with static fields (fixes #55494)

* Fixes #53671

* fixes #54630

* [html] should disable ionic suggestions by default. Currently forces deprecated Ionic v1 suggestions in .html files while typing. Fixes #53324

* cleanup deps

* debug issues back to andre

* update electron for smoketest

* Fix #55757 - prevent settings tabs from overflowing

* Fix #53897 - revert setting menu defaults to old editor

* Add enum descriptions to `typescript.preferences.importModuleSpecifier`

* Fix #55767 - leaking style elements from settings editor

* Fix #55521 - prevent flashing when clicking in exclude control

* Update Git modified color for contrast ratio, fixes #53140

* Revert "Merge branch 'master' of github.com:Microsoft/vscode"

This reverts commit bf46b6bfbae0cab99c2863e1244a916181fa9fbc, reversing
changes made to e275a424483dfb4ed33b428c97d5e2c441d6b917.

* Revert "Revert "Merge branch 'master' of github.com:Microsoft/vscode""

This reverts commit 53949d963f39e40757557c6526332354a31d9154.

* don't ask to install an incomplete menu

* Fix NPE in terminal AccessibilityManager

Fixes #55744

* don't display fallback menu unless we've closed the last window

* fixes #55547

* Fix smoke tests for extension search box

* Update OSSREADME.json for Electron 2.0.5

* Update distro

Includes Chromium license changes

* fix #55455

* fix #55865

* fixes #55893

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation (#55805)

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation

* ONly show on @recommended or @recommended:workspace

* Make more consistant

* Fix #55911

* Understand json activity (#55926)

* Understand json file activity

* Refactoring

* adding composer.json

* Distro update for experiments

* use terminal.processId for auto-attach; fixes #55918

* Reject invalid URI with vscode.openFolder (for #55891)

* improve win32 setup system vs user detection

fixes #55840

fixes #55840

delay winreg import

related to #55840

show notification earlier

related to #55840

fix #55840

update inno setup message

related to #55840

* Fix #55593 - this code only operates on local paths, so use fsPath and Uri.file instead

* Bring back the old menu due to electron 2.0 issues (#55913)

* add the old menu back for native menus

* make menu labels match

* `vscode.openFolder`: treat missing URI schema gracefully (for #55891)

* delay EH reattach; fixes #55955

* Mark all json files under appSettingsHome as settings

* Use localized strings for telemetry opt-out

* Exception when saving file editor opened from remote file provider (fixes #55051)

* Remove terminal menu from stable

Fixes 56003

* VSCode Insiders crashes on open with TypeError: Cannot read property 'lastIndexOf' of undefined. Fixes #54933

* improve fix for #55891

* fix #55916

* Improve #55891

* increase EH debugging restart delay; fixes #55955

* Revert "Don't include non-resource entries in history quick pick"

This reverts commit 37209a838e9f7e9abe6dc53ed73cdf1e03b72060.

* Diff editor: horizontal scrollbar height is smaller (fixes #56062)

* improve openFolder uri fix (correctly treat backslashes)

* fixes #56116
repair ipc for native menubar keybindings

* Fix #56240 - Open the JSON settings editor instead of the UI editor

* Fix #55536

* uriDisplay: if no formatter is registered fall back to getPathlabel

fixes #56104

* VSCode hangs when opening python file. Fixes #56377

* VS Code Hangs When Opening Specific PowerShell File. Fixes #56430

* Fix #56433 - search extraFileResources even when no folders open

* Workaround #55649

* Fix in master #56371

* Fix tests #56371

* Fix in master #56317

* increase version to 1.26.1

* Fixes #56387: Handle SIGPIPE in extension host

* fixes #56185

* Fix merge issues (part 1)

* Fix build breaks (part 1)

* Build breaks (part 2)

* Build breaks (part 3)

* More build breaks (part 4)

* Fix build breaks (part 5)

* WIP

* Fix menus

* Render query result and message panels (#2363)

* Put back query editor hot exit changes

* Fix grid changes that broke profiler (#2365)

* Update APIs for saving query editor state

* Fix restore view state for profiler and edit data

* Updating custom default themes to support 4.5:1 contrast ratio

* Test updates

* Fix Extension Manager and Windows Setup

* Update license headers

* Add appveyor and travis files back

* Fix hidden modal dropdown issue
This commit is contained in:
Karl Burtram
2018-09-04 14:55:00 -07:00
committed by GitHub
parent 3763278366
commit 81329fa7fa
2638 changed files with 118456 additions and 64012 deletions

View File

@@ -6,16 +6,24 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Panel } from 'vs/workbench/browser/panel';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { IEditor, Position } from 'vs/platform/editor/common/editor';
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 { LRUCache } from 'vs/base/common/map';
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';
/**
* The base class of editors in the workbench. Editors register themselves for specific editor inputs.
* Editors are layed out in the editor part of the workbench. Only one editor can be open at a time.
* Each editor has a minimized representation that is good enough to provide some information about the
* state of the editor data.
* Editors are layed out in the editor part of the workbench in editor groups. Multiple editors can be
* open at the same time. Each editor has a minimized representation that is good enough to provide some
* information about the state of the editor data.
*
* The workbench will keep an editor alive after it has been created and show/hide it based on
* user interaction. The lifecycle of a editor goes in the order create(), setVisible(true|false),
* layout(), setInput(), focus(), dispose(). During use of the workbench, a editor will often receive a
@@ -24,30 +32,53 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
* This class is only intended to be subclassed and not instantiated.
*/
export abstract class BaseEditor extends Panel implements IEditor {
protected _input: EditorInput;
private _options: EditorOptions;
private _position: Position;
constructor(id: string, telemetryService: ITelemetryService, themeService: IThemeService) {
private static readonly EDITOR_MEMENTOS: Map<string, EditorMemento<any>> = new Map<string, EditorMemento<any>>();
readonly minimumWidth = DEFAULT_EDITOR_MIN_DIMENSIONS.width;
readonly maximumWidth = DEFAULT_EDITOR_MAX_DIMENSIONS.width;
readonly minimumHeight = DEFAULT_EDITOR_MIN_DIMENSIONS.height;
readonly maximumHeight = DEFAULT_EDITOR_MAX_DIMENSIONS.height;
readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = Event.None;
protected _input: EditorInput;
private _options: EditorOptions;
private _group: IEditorGroup;
constructor(
id: string,
telemetryService: ITelemetryService,
themeService: IThemeService
) {
super(id, telemetryService, themeService);
}
public get input(): EditorInput {
get input(): EditorInput {
return this._input;
}
public get options(): EditorOptions {
get options(): EditorOptions {
return this._options;
}
get group(): IEditorGroup {
return this._group;
}
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
*
* Sets the given input with the options to the part. An editor has to deal with the
* situation that the same input is being set with different options.
* Sets the given input with the options to the editor. The input is guaranteed
* to be different from the previous input that was set using the input.matches()
* method.
*
* The provided cancellation token should be used to test if the operation
* was cancelled.
*/
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
this._input = input;
this._options = options;
@@ -55,17 +86,28 @@ export abstract class BaseEditor extends Panel implements IEditor {
}
/**
* Called to indicate to the editor that the input should be cleared and resources associated with the
* input should be freed.
* Called to indicate to the editor that the input should be cleared and
* resources associated with the input should be freed.
*/
public clearInput(): void {
clearInput(): void {
this._input = null;
this._options = null;
}
public create(parent: HTMLElement): void; // create is sync for editors
public create(parent: HTMLElement): TPromise<void>;
public create(parent: HTMLElement): TPromise<void> {
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
*
* Sets the given options to the editor. Clients should apply the options
* to the current input.
*/
setOptions(options: EditorOptions): void {
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 Editor
@@ -79,50 +121,183 @@ export abstract class BaseEditor extends Panel implements IEditor {
*/
protected abstract createEditor(parent: HTMLElement): void;
/**
* Subclasses can set this to false if it does not make sense to center editor input.
*/
public supportsCenteredLayout(): boolean {
return true;
}
/**
* Overload this function to allow for passing in a position argument.
*/
public setVisible(visible: boolean, position?: Position): void; // setVisible is sync for editors
public setVisible(visible: boolean, position?: Position): TPromise<void>;
public setVisible(visible: boolean, position: Position = null): TPromise<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);
// Propagate to Editor
this.setEditorVisible(visible, position);
this.setEditorVisible(visible, group);
return promise;
}
protected setEditorVisible(visible: boolean, position: Position = null): void {
this._position = position;
}
/**
* Called when the position of the editor changes while it is visible.
* Indicates that the editor control got visible or hidden in a specific group. A
* editor instance will only ever be visible in one editor group.
*
* @param visible the state of visibility of this editor
* @param group the editor group this editor is in.
*/
public changePosition(position: Position): void {
this._position = position;
protected setEditorVisible(visible: boolean, group: IEditorGroup): void {
this._group = group;
}
/**
* The position this editor is showing in or null if none.
*/
public get position(): Position {
return this._position;
protected getEditorMemento<T>(storageService: IStorageService, 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);
BaseEditor.EDITOR_MEMENTOS.set(mementoKey, editorMemento);
}
return editorMemento;
}
public dispose(): void {
shutdown(): void {
// Shutdown all editor memento for this editor type
BaseEditor.EDITOR_MEMENTOS.forEach(editorMemento => {
if (editorMemento.id === this.getId()) {
editorMemento.shutdown();
}
});
super.shutdown();
}
dispose(): void {
this._input = null;
this._options = null;
// Super Dispose
super.dispose();
}
}
interface MapGroupToMemento<T> {
[group: number]: T;
}
export class EditorMemento<T> implements IEditorMemento<T> {
private cache: LRUCache<string, MapGroupToMemento<T>>;
private cleanedUp = false;
constructor(
private _id: string,
private key: string,
private memento: object,
private limit: number,
private editorGroupService: IEditorGroupsService
) { }
get id(): string {
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 {
const resource = this.doGetResource(resourceOrEditor);
if (!resource || !group) {
return; // we are not in a good state to save any state for a resource
}
const cache = this.doLoad();
let mementoForResource = cache.get(resource.toString());
if (!mementoForResource) {
mementoForResource = Object.create(null) as MapGroupToMemento<T>;
cache.set(resource.toString(), mementoForResource);
}
mementoForResource[group.id] = state;
// Automatically clear when editor input gets disposed if any
if (resourceOrEditor instanceof EditorInput) {
once(resourceOrEditor.onDispose)(() => {
this.clearState(resource);
});
}
}
loadState(group: IEditorGroup, resource: URI): T;
loadState(group: IEditorGroup, editor: EditorInput): T;
loadState(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
}
const cache = this.doLoad();
const mementoForResource = cache.get(resource.toString());
if (mementoForResource) {
return mementoForResource[group.id];
}
return void 0;
}
clearState(resource: URI): void;
clearState(editor: EditorInput): void;
clearState(resourceOrEditor: URI | EditorInput): void {
const resource = this.doGetResource(resourceOrEditor);
if (resource) {
const cache = this.doLoad();
cache.delete(resource.toString());
}
}
private doGetResource(resourceOrEditor: URI | EditorInput): URI {
if (resourceOrEditor instanceof EditorInput) {
return resourceOrEditor.getResource();
}
return resourceOrEditor;
}
private doLoad(): LRUCache<string, MapGroupToMemento<T>> {
if (!this.cache) {
this.cache = new LRUCache<string, MapGroupToMemento<T>>(this.limit);
// Restore from serialized map state
const rawEditorMemento = this.memento[this.key];
if (Array.isArray(rawEditorMemento)) {
this.cache.fromJSON(rawEditorMemento);
}
}
return this.cache;
}
shutdown(): void {
const cache = this.doLoad();
// Cleanup once during shutdown
if (!this.cleanedUp) {
this.cleanUp();
this.cleanedUp = true;
}
this.memento[this.key] = cache.toJSON();
}
private cleanUp(): void {
const cache = this.doLoad();
// Remove groups from states that no longer exist
cache.forEach((mapGroupToMemento, resource) => {
Object.keys(mapGroupToMemento).forEach(group => {
const groupId: GroupIdentifier = Number(group);
if (!this.editorGroupService.getGroup(groupId)) {
delete mapGroupToMemento[groupId];
if (isEmptyObject(mapGroupToMemento)) {
cache.delete(resource);
}
}
});
});
}
}

View File

@@ -18,7 +18,7 @@ import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/bina
*/
export class BinaryResourceDiffEditor extends SideBySideEditor {
public static readonly ID = BINARY_DIFF_EDITOR_ID;
static readonly ID = BINARY_DIFF_EDITOR_ID;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -28,7 +28,7 @@ export class BinaryResourceDiffEditor extends SideBySideEditor {
super(telemetryService, instantiationService, themeService);
}
public getMetadata(): string {
getMetadata(): string {
const master = this.masterEditor;
const details = this.detailsEditor;

View File

@@ -19,6 +19,8 @@ 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 { IFileService } from 'vs/platform/files/common/files';
import { CancellationToken } from 'vs/base/common/cancellation';
export interface IOpenCallbacks {
openInternal: (input: EditorInput, options: EditorOptions) => void;
@@ -30,7 +32,8 @@ export interface IOpenCallbacks {
*/
export abstract class BaseBinaryResourceEditor extends BaseEditor {
private readonly _onMetadataChanged: Emitter<void>;
private readonly _onMetadataChanged: Emitter<void> = this._register(new Emitter<void>());
get onMetadataChanged(): Event<void> { return this._onMetadataChanged.event; }
private callbacks: IOpenCallbacks;
private metadata: string;
@@ -42,21 +45,15 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
id: string,
callbacks: IOpenCallbacks,
telemetryService: ITelemetryService,
themeService: IThemeService
themeService: IThemeService,
@IFileService private readonly _fileService: IFileService,
) {
super(id, telemetryService, themeService);
this._onMetadataChanged = new Emitter<void>();
this.toUnbind.push(this._onMetadataChanged);
this.callbacks = callbacks;
}
public get onMetadataChanged(): Event<void> {
return this._onMetadataChanged.event;
}
public getTitle(): string {
getTitle(): string {
return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer");
}
@@ -70,35 +67,28 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
this.binaryContainer.tabindex(0); // enable focus support from the editor part (do not remove)
// Custom Scrollbars
this.scrollbar = new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto });
this.scrollbar = this._register(new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto }));
parent.appendChild(this.scrollbar.getDomNode());
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
return super.setInput(input, options, token).then(() => {
return input.resolve().then(model => {
// Return early for same input unless we force to open
const forceOpen = options && options.forceOpen;
if (!forceOpen && input.matches(this.input)) {
return TPromise.wrap<void>(null);
}
// Otherwise set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then(model => {
// Check for cancellation
if (token.isCancellationRequested) {
return void 0;
}
// Assert Model instance
if (!(model instanceof BinaryEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// 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.scrollbar,
resource => this.callbacks.openInternal(input, options),
@@ -106,25 +96,22 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
meta => this.handleMetadataChanged(meta)
);
return TPromise.as<void>(null);
return void 0;
});
});
}
private handleMetadataChanged(meta: string): void {
this.metadata = meta;
this._onMetadataChanged.fire();
}
public getMetadata(): string {
getMetadata(): string {
return this.metadata;
}
public supportsCenteredLayout(): boolean {
return false;
}
public clearInput(): void {
clearInput(): void {
// Clear Meta
this.handleMetadataChanged(null);
@@ -135,7 +122,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
super.clearInput();
}
public layout(dimension: Dimension): void {
layout(dimension: Dimension): void {
// Pass on to Binary Container
this.binaryContainer.size(dimension.width, dimension.height);
@@ -145,15 +132,14 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
}
}
public focus(): void {
focus(): void {
this.binaryContainer.domFocus();
}
public dispose(): void {
dispose(): void {
// Destroy Container
this.binaryContainer.destroy();
this.scrollbar.dispose();
super.dispose();
}

View File

@@ -0,0 +1,147 @@
/*---------------------------------------------------------------------------------------------
* 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 { 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 { localize } from 'vs/nls';
export const IBreadcrumbsService = createDecorator<IBreadcrumbsService>('IEditorBreadcrumbsService');
export interface IBreadcrumbsService {
_serviceBrand: any;
register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable;
getWidget(group: GroupIdentifier): BreadcrumbsWidget;
}
export class BreadcrumbsService implements IBreadcrumbsService {
_serviceBrand: any;
private readonly _map = new Map<number, BreadcrumbsWidget>();
register(group: number, widget: BreadcrumbsWidget): IDisposable {
if (this._map.has(group)) {
throw new Error(`group (${group}) has already a widget`);
}
this._map.set(group, widget);
return {
dispose: () => this._map.delete(group)
};
}
getWidget(group: number): BreadcrumbsWidget {
return this._map.get(group);
}
}
registerSingleton(IBreadcrumbsService, BreadcrumbsService);
//#region config
export abstract class BreadcrumbsConfig<T> {
name: string;
value: T;
onDidChange: Event<T>;
abstract dispose(): void;
private constructor() {
// internal
}
static IsEnabled = BreadcrumbsConfig._stub<boolean>('breadcrumbs.enabled');
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');
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 listener = service.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(name)) {
value = service.getValue(name);
onDidChange.fire(value);
}
});
return {
name,
get value() {
return value;
},
set value(newValue: T) {
service.updateValue(name, newValue);
value = newValue;
},
onDidChange: onDidChange.event,
dispose(): void {
listener.dispose();
onDidChange.dispose();
}
};
}
};
}
}
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
id: 'breadcrumbs',
title: localize('title', "Breadcrumb Navigation"),
order: 101,
type: 'object',
properties: {
'breadcrumbs.enabled': {
description: localize('enabled', "Enable/disable navigation breadcrumbs"),
type: 'boolean',
default: false
},
// 'breadcrumbs.useQuickPick': {
// description: localize('useQuickPick', "Use quick pick instead of breadcrumb-pickers."),
// type: 'boolean',
// default: false
// },
'breadcrumbs.filePath': {
description: localize('filepath', "Controls whether and how file paths are shown in the breadcrumbs view."),
type: 'string',
default: 'on',
enum: ['on', 'off', 'last'],
enumDescriptions: [
localize('filepath.on', "Show the file path in the breadcrumbs view."),
localize('filepath.off', "Do not show the file path in the breadcrumbs view."),
localize('filepath.last', "Only show the last element of the file path in the breadcrumbs view."),
]
},
'breadcrumbs.symbolPath': {
description: localize('symbolpath', "Controls whether and how symbols are shown in the breadcrumbs view."),
type: 'string',
default: 'on',
enum: ['on', 'off', 'last'],
enumDescriptions: [
localize('symbolpath.on', "Show all symbols in the breadcrumbs view."),
localize('symbolpath.off', "Do not show symbols in the breadcrumbs view."),
localize('symbolpath.last', "Only show the current symbol in the breadcrumbs view."),
]
},
}
});
//#endregion

View File

@@ -0,0 +1,501 @@
/*---------------------------------------------------------------------------------------------
* 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 * 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 { 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 'vs/css!./media/breadcrumbscontrol';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { symbolKindToCssClass } from 'vs/editor/common/modes';
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
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 { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
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 { 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';
class Item extends BreadcrumbsItem {
private readonly _disposables: IDisposable[] = [];
constructor(
readonly element: BreadcrumbElement,
readonly options: IBreadcrumbsControlOptions,
@IInstantiationService private readonly _instantiationService: IInstantiationService
) {
super();
}
dispose(): void {
dispose(this._disposables);
}
equals(other: BreadcrumbsItem): boolean {
if (!(other instanceof Item)) {
return false;
}
if (this.element instanceof FileElement && other.element instanceof FileElement) {
return isEqual(this.element.uri, other.element.uri);
}
if (this.element instanceof TreeElement && other.element instanceof TreeElement) {
return this.element.id === other.element.id;
}
return false;
}
render(container: HTMLElement): void {
if (this.element instanceof FileElement) {
// file/folder
let label = this._instantiationService.createInstance(FileLabel, container, {});
label.setFile(this.element.uri, {
hidePath: true,
hideIcon: this.element.kind === FileKind.FOLDER || !this.options.showFileIcons,
fileKind: this.element.kind,
fileDecorations: { colors: this.options.showDecorationColors, badges: false },
});
dom.addClass(container, FileKind[this.element.kind].toLowerCase());
this._disposables.push(label);
} else if (this.element instanceof OutlineModel) {
// has outline element but not in one
let label = document.createElement('div');
label.innerHTML = '&hellip;';
label.className = 'hint-more';
container.appendChild(label);
} else if (this.element instanceof OutlineGroup) {
// provider
let label = new IconLabel(container);
label.setValue(this.element.provider.displayName);
this._disposables.push(label);
} else if (this.element instanceof OutlineElement) {
// symbol
if (this.options.showSymbolIcons) {
let icon = document.createElement('div');
icon.className = symbolKindToCssClass(this.element.symbol.kind);
container.appendChild(icon);
dom.addClass(container, 'shows-symbol-icon');
}
let label = new IconLabel(container);
let title = this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE');
label.setValue(title);
this._disposables.push(label);
}
}
}
export interface IBreadcrumbsControlOptions {
showFileIcons: boolean;
showSymbolIcons: boolean;
showDecorationColors: boolean;
extraClasses: string[];
}
export class BreadcrumbsControl {
static HEIGHT = 25;
static readonly Payload_Reveal = {};
static readonly Payload_RevealAside = {};
static readonly Payload_Pick = {};
static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false);
static CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false);
private readonly _ckBreadcrumbsVisible: IContextKey<boolean>;
private readonly _ckBreadcrumbsActive: IContextKey<boolean>;
private readonly _cfUseQuickPick: BreadcrumbsConfig<boolean>;
readonly domNode: HTMLDivElement;
private readonly _widget: BreadcrumbsWidget;
private _disposables = new Array<IDisposable>();
private _breadcrumbsDisposables = new Array<IDisposable>();
private _breadcrumbsPickerShowing = false;
constructor(
container: HTMLElement,
private readonly _options: IBreadcrumbsControlOptions,
private readonly _editorGroup: EditorGroupView,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IContextViewService private readonly _contextViewService: IContextViewService,
@IEditorService private readonly _editorService: IEditorService,
@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,
@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._ckBreadcrumbsVisible = BreadcrumbsControl.CK_BreadcrumbsVisible.bindTo(this._contextKeyService);
this._ckBreadcrumbsActive = BreadcrumbsControl.CK_BreadcrumbsActive.bindTo(this._contextKeyService);
this._cfUseQuickPick = BreadcrumbsConfig.UseQuickPick.bindTo(_configurationService);
this._disposables.push(breadcrumbsService.register(this._editorGroup.id, this._widget));
}
dispose(): void {
this._disposables = dispose(this._disposables);
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
this._ckBreadcrumbsVisible.reset();
this._ckBreadcrumbsActive.reset();
this._cfUseQuickPick.dispose();
this._widget.dispose();
this.domNode.remove();
}
layout(dim: dom.Dimension): void {
this._widget.layout(dim);
}
isHidden(): boolean {
return dom.hasClass(this.domNode, 'hidden');
}
hide(): void {
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
this._ckBreadcrumbsVisible.set(false);
dom.toggleClass(this.domNode, 'hidden', true);
}
update(): boolean {
const input = this._editorGroup.activeEditor;
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
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
if (!this.isHidden()) {
this.hide();
return true;
} else {
return false;
}
}
dom.toggleClass(this.domNode, 'hidden', false);
this._ckBreadcrumbsVisible.set(true);
let control = this._editorGroup.activeControl.getControl() as ICodeEditor;
let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService, this._configurationService);
dom.toggleClass(this.domNode, 'relative-path', model.isRelative());
let updateBreadcrumbs = () => {
let items = model.getElements().map(element => new Item(element, this._options, this._instantiationService));
this._widget.setItems(items);
this._widget.reveal(items[items.length - 1]);
};
let listener = model.onDidUpdate(updateBreadcrumbs);
updateBreadcrumbs();
this._breadcrumbsDisposables = [model, listener];
// close picker on hide/update
this._breadcrumbsDisposables.push({
dispose: () => {
if (this._breadcrumbsPickerShowing) {
this._contextViewService.hideContextView(this);
}
}
});
return true;
}
private _onFocusEvent(event: IBreadcrumbsItemEvent): void {
if (event.item && this._breadcrumbsPickerShowing) {
return this._widget.setSelection(event.item);
}
}
private _onSelectEvent(event: IBreadcrumbsItemEvent): void {
if (!event.item) {
return;
}
this._editorGroup.focus();
const { element } = event.item as Item;
const group = this._getEditorGroup(event.payload);
if (group !== undefined) {
// reveal the item
this._widget.setFocused(undefined);
this._widget.setSelection(undefined);
this._revealInEditor(event, element, group);
return;
}
if (this._cfUseQuickPick.value) {
// using quick pick
this._widget.setFocused(undefined);
this._widget.setSelection(undefined);
this._quickOpenService.show(element instanceof TreeElement ? '@' : '');
return;
}
// show picker
let picker: BreadcrumbsPicker;
this._contextViewService.showContextView({
render: (parent: HTMLElement) => {
picker = createBreadcrumbsPicker(this._instantiationService, parent, element);
let listener = picker.onDidPickElement(data => {
this._contextViewService.hideContextView(this);
this._revealInEditor(event, data.target, this._getEditorGroup(data.payload && data.payload.originalEvent));
});
this._breadcrumbsPickerShowing = true;
this._updateCkBreadcrumbsActive();
return combinedDisposable([listener, picker]);
},
getAnchor() {
let pickerHeight = 330;
let pickerWidth = Math.max(220, dom.getTotalWidth(event.node));
let pickerArrowSize = 8;
let pickerArrowOffset: number;
let data = dom.getDomNodePagePosition(event.node.firstChild as HTMLElement);
let y = data.top + data.height - pickerArrowSize;
let x = data.left;
if (x + pickerWidth >= window.innerWidth) {
x = window.innerWidth - pickerWidth;
}
if (event.payload instanceof StandardMouseEvent) {
pickerArrowOffset = event.payload.posx - x - pickerArrowSize;
} else {
pickerArrowOffset = (data.left + (data.width * .3)) - x;
}
picker.layout(pickerHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
picker.setInput(element);
return { x, y };
},
onHide: (data) => {
this._breadcrumbsPickerShowing = false;
this._updateCkBreadcrumbsActive();
if (data === this) {
this._widget.setFocused(undefined);
this._widget.setSelection(undefined);
}
}
});
}
private _updateCkBreadcrumbsActive(): void {
const value = this._widget.isDOMFocused() || this._breadcrumbsPickerShowing;
this._ckBreadcrumbsActive.set(value);
}
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): void {
if (element instanceof FileElement) {
if (element.kind === FileKind.FILE) {
// open file in editor
this._editorService.openEditor({ resource: element.uri }, group);
} else {
// show next picker
let items = this._widget.getItems();
let idx = items.indexOf(event.item);
this._widget.setFocused(items[idx + 1]);
this._widget.setSelection(items[idx + 1], BreadcrumbsControl.Payload_Pick);
}
} else if (element instanceof OutlineElement) {
// open symbol in editor
let model = OutlineModel.get(element);
this._editorService.openEditor({
resource: model.textModel.uri,
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
}, group);
}
}
private _getEditorGroup(data: StandardMouseEvent | object): SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | undefined {
if (data === BreadcrumbsControl.Payload_RevealAside || (data instanceof StandardMouseEvent && data.altKey)) {
return SIDE_GROUP;
} else if (data === BreadcrumbsControl.Payload_Reveal || (data instanceof StandardMouseEvent && data.metaKey)) {
return ACTIVE_GROUP;
} else {
return undefined;
}
}
}
//#region commands
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: 'breadcrumbs.focusAndSelect',
title: localize('cmd.focus', "Focus Breadcrumbs")
}
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: 'breadcrumbs.toggle',
title: localize('cmd.toggle', "Toggle Breadcrumbs")
}
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '5_editor',
order: 99,
command: {
id: 'breadcrumbs.toggle',
title: localize('cmd.toggle', "Toggle Breadcrumbs")
}
});
CommandsRegistry.registerCommand('breadcrumbs.toggle', accessor => {
let config = accessor.get(IConfigurationService);
let value = BreadcrumbsConfig.IsEnabled.bindTo(config).value;
BreadcrumbsConfig.IsEnabled.bindTo(config).value = !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);
const item = tail(widget.getItems());
widget.setFocused(item);
}
});
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);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.focusNext',
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.RightArrow,
secondary: [KeyMod.CtrlCmd | KeyCode.RightArrow],
mac: {
primary: KeyCode.RightArrow,
secondary: [KeyMod.Alt | KeyCode.RightArrow],
},
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).focusNext();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.focusPrevious',
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.LeftArrow,
secondary: [KeyMod.CtrlCmd | KeyCode.LeftArrow],
mac: {
primary: KeyCode.LeftArrow,
secondary: [KeyMod.Alt | KeyCode.LeftArrow],
},
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).focusPrev();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.selectFocused',
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.Enter,
secondary: [KeyCode.DownArrow],
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
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_Pick);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.revealFocused',
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.Space,
secondary: [KeyMod.CtrlCmd | KeyCode.Enter],
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
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_Reveal);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.selectEditor',
weight: KeybindingWeight.WorkbenchContrib + 1,
primary: KeyCode.Escape,
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).setFocused(undefined);
breadcrumbs.getWidget(groups.activeGroup.id).setSelection(undefined);
groups.activeGroup.activeControl.focus();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.revealFocusedFromTreeAside',
weight: KeybindingWeight.WorkbenchContrib,
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);
}
});
//#endregion

View File

@@ -0,0 +1,231 @@
/*---------------------------------------------------------------------------------------------
* 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 { equals } from 'vs/base/common/arrays';
import { TimeoutTimer } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
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 { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IPosition } from 'vs/editor/common/core/position';
import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { FileKind } from 'vs/platform/files/common/files';
export class FileElement {
constructor(
readonly uri: URI,
readonly kind: FileKind
) { }
}
export type BreadcrumbElement = FileElement | OutlineModel | OutlineGroup | OutlineElement;
type FileInfo = { path: FileElement[], folder: IWorkspaceFolder };
export class EditorBreadcrumbsModel {
private readonly _disposables: IDisposable[] = [];
private readonly _fileInfo: FileInfo;
private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
private _outlineElements: (OutlineModel | OutlineGroup | OutlineElement)[] = [];
private _outlineDisposables: IDisposable[] = [];
private _onDidUpdate = new Emitter<this>();
readonly onDidUpdate: Event<this> = this._onDidUpdate.event;
constructor(
private readonly _uri: URI,
private readonly _editor: ICodeEditor | undefined,
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
@IConfigurationService configurationService: IConfigurationService,
) {
this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(configurationService);
this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(configurationService);
this._disposables.push(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this)));
this._disposables.push(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this)));
this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService);
this._bindToEditor();
this._onDidUpdate.fire(this);
}
dispose(): void {
this._cfgFilePath.dispose();
this._cfgSymbolPath.dispose();
dispose(this._disposables);
}
isRelative(): boolean {
return Boolean(this._fileInfo.folder);
}
getElements(): ReadonlyArray<BreadcrumbElement> {
let result: BreadcrumbElement[] = [];
// file path elements
if (this._cfgFilePath.value === 'on') {
result = result.concat(this._fileInfo.path);
} else if (this._cfgFilePath.value === 'last' && this._fileInfo.path.length > 0) {
result = result.concat(this._fileInfo.path.slice(-1));
}
// symbol path elements
if (this._cfgSymbolPath.value === 'on') {
result = result.concat(this._outlineElements);
} else if (this._cfgSymbolPath.value === 'last' && this._outlineElements.length > 0) {
result = result.concat(this._outlineElements.slice(-1));
}
return result;
}
private static _initFilePathInfo(uri: URI, workspaceService: IWorkspaceContextService): FileInfo {
if (uri.scheme === Schemas.untitled) {
return {
folder: undefined,
path: []
};
}
let info: FileInfo = {
folder: workspaceService.getWorkspaceFolder(uri),
path: []
};
while (uri.path !== '/') {
if (info.folder && isEqual(info.folder.uri, uri)) {
break;
}
info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER));
uri = uri.with({ path: paths.dirname(uri.path) });
}
if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
info.path.unshift(new FileElement(info.folder.uri, FileKind.ROOT_FOLDER));
}
return info;
}
private _bindToEditor(): void {
if (!this._editor) {
return;
}
// update as model changes
this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline()));
this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline()));
this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline()));
this._disposables.push(debounceEvent(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline(true)));
this._updateOutline();
// stop when editor dies
this._disposables.push(this._editor.onDidDispose(() => this._outlineDisposables = dispose(this._outlineDisposables)));
}
private _updateOutline(didChangeContent?: boolean): void {
this._outlineDisposables = dispose(this._outlineDisposables);
if (!didChangeContent) {
this._updateOutlineElements([]);
}
const buffer = this._editor.getModel();
if (!buffer || !DocumentSymbolProviderRegistry.has(buffer) || !isEqual(buffer.uri, this._uri)) {
return;
}
const source = new CancellationTokenSource();
const versionIdThen = buffer.getVersionId();
const timeout = new TimeoutTimer();
this._outlineDisposables.push({
dispose: () => {
source.cancel();
source.dispose();
timeout.dispose();
}
});
OutlineModel.create(buffer, source.token).then(model => {
if (TreeElement.empty(model)) {
// empty -> no outline elements
this._updateOutlineElements([]);
} else {
// copy the model
model = model.adopt();
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => {
timeout.cancelAndSet(() => {
if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId()) {
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
}
}, 150);
}));
}
}).catch(err => {
this._updateOutlineElements([]);
onUnexpectedError(err);
});
}
private _getOutlineElements(model: OutlineModel, position: IPosition): (OutlineModel | OutlineGroup | OutlineElement)[] {
if (!model) {
return [];
}
let item: OutlineGroup | OutlineElement = model.getItemEnclosingPosition(position);
if (!item) {
return [model];
}
let chain: (OutlineGroup | OutlineElement)[] = [];
while (item) {
chain.push(item);
let parent = item.parent;
if (parent instanceof OutlineModel) {
break;
}
if (parent instanceof OutlineGroup && size(parent.parent.children) === 1) {
break;
}
item = parent;
}
return chain.reverse();
}
private _updateOutlineElements(elements: (OutlineModel | OutlineGroup | OutlineElement)[]): void {
if (!equals(elements, this._outlineElements, EditorBreadcrumbsModel._outlineElementEquals)) {
this._outlineElements = elements;
this._onDidUpdate.fire(this);
}
}
private static _outlineElementEquals(a: OutlineModel | OutlineGroup | OutlineElement, b: OutlineModel | OutlineGroup | OutlineElement): boolean {
if (a === b) {
return true;
} else if (!a || !b) {
return false;
} else {
return a.id === b.id;
}
}
}

View File

@@ -0,0 +1,367 @@
/*---------------------------------------------------------------------------------------------
* 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 * as dom from 'vs/base/browser/dom';
import { compareFileNames } from 'vs/base/common/comparers';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { 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 '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 { 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';
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);
}
export abstract class BreadcrumbsPicker {
protected readonly _disposables = new Array<IDisposable>();
protected readonly _domNode: HTMLDivElement;
protected readonly _arrow: HTMLDivElement;
protected readonly _treeContainer: HTMLDivElement;
protected readonly _tree: HighlightingWorkbenchTree;
protected readonly _focus: dom.IFocusTracker;
private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>();
readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event;
constructor(
parent: HTMLElement,
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
@IThemeService protected readonly _themeService: IThemeService,
) {
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons';
parent.appendChild(this._domNode);
this._focus = dom.trackFocus(this._domNode);
this._focus.onDidBlur(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined }), undefined, this._disposables);
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.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._domNode.appendChild(this._treeContainer);
const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined });
this._tree = this._instantiationService.createInstance(
HighlightingWorkbenchTree,
this._treeContainer,
treeConifg,
{ useShadows: false },
{ placeholder: localize('placeholder', "Find") }
);
this._disposables.push(this._tree.onDidChangeSelection(e => {
if (e.payload !== this._tree) {
const target = this._getTargetFromSelectionEvent(e);
if (!target) {
return;
}
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._domNode.focus();
}
dispose(): void {
dispose(this._disposables);
this._onDidPickElement.dispose();
this._tree.dispose();
this._focus.dispose();
}
setInput(input: any): void {
let actualInput = this._getInput(input);
this._tree.setInput(actualInput).then(() => {
let selection = this._getInitialSelection(this._tree, input);
if (selection) {
this._tree.reveal(selection, .5).then(() => {
this._tree.setSelection([selection], this._tree);
this._tree.setFocus(selection);
this._tree.domFocus();
});
} else {
this._tree.focusFirst();
this._tree.setSelection([this._tree.getFocus()], this._tree);
this._tree.domFocus();
}
}, 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`;
this._treeContainer.style.height = `${height - 2 * arrowSize}px`;
this._treeContainer.style.width = `${width}px`;
this._tree.layout();
}
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;
}
//#region - Files
export class FileDataSource implements IDataSource {
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
constructor(
@IFileService private readonly _fileService: IFileService,
) { }
getId(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): string {
if (URI.isUri(element)) {
return element.toString();
} else if (IWorkspace.isIWorkspace(element)) {
return element.id;
} else if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
return element.uri.toString();
} else {
return element.resource.toString();
}
}
hasChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): boolean {
return URI.isUri(element) || IWorkspace.isIWorkspace(element) || IWorkspaceFolder.isIWorkspaceFolder(element) || element.isDirectory;
}
getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): TPromise<IWorkspaceFolder[] | IFileStat[]> {
if (IWorkspace.isIWorkspace(element)) {
return TPromise.as(element.folders).then(folders => {
for (let child of folders) {
this._parents.set(element, child);
}
return folders;
});
}
let uri: URI;
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
uri = element.uri;
} else if (URI.isUri(element)) {
uri = element;
} else {
uri = element.resource;
}
return this._fileService.resolveFile(uri).then(stat => {
for (let child of stat.children) {
this._parents.set(stat, child);
}
return stat.children;
});
}
getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): TPromise<IWorkspaceFolder | IFileStat> {
return TPromise.as(this._parents.get(element));
}
}
export class FileRenderer implements IRenderer, IHighlightingRenderer {
private readonly _scores = new Map<string, FuzzyScore>();
constructor(
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IConfigurationService private readonly _configService: IConfigurationService,
) { }
getHeight(tree: ITree, element: any): number {
return 22;
}
getTemplateId(tree: ITree, element: any): string {
return 'FileStat';
}
renderTemplate(tree: ITree, templateId: string, container: HTMLElement) {
return this._instantiationService.createInstance(FileLabel, container, { supportHighlights: true });
}
renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: FileLabel): void {
let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
let resource: URI;
let fileKind: FileKind;
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
resource = element.uri;
fileKind = FileKind.ROOT_FOLDER;
} else {
resource = element.resource;
fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE;
}
templateData.setFile(resource, {
fileKind,
hidePath: true,
fileDecorations: fileDecorations,
matches: createMatches((this._scores.get(resource.toString()) || [, []])[1])
});
}
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 {
compare(tree: ITree, a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
if (IWorkspaceFolder.isIWorkspaceFolder(a) && IWorkspaceFolder.isIWorkspaceFolder(b)) {
return a.index - b.index;
} else {
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
// same type -> compare on names
return compareFileNames(a.name, b.name);
} else if ((a as IFileStat).isDirectory) {
return -1;
} else {
return 1;
}
}
}
}
export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
constructor(
parent: HTMLElement,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
) {
super(parent, instantiationService, themeService);
}
protected _getInput(input: BreadcrumbElement): any {
let { uri, kind } = (input as FileElement);
if (kind === FileKind.ROOT_FOLDER) {
return this._workspaceService.getWorkspace();
} else {
return dirname(uri);
}
}
protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any {
let { uri } = (input as FileElement);
let nav = tree.getNavigator();
while (nav.next()) {
let cur = nav.current();
let candidate = IWorkspaceFolder.isIWorkspaceFolder(cur) ? cur.uri : (cur as IFileStat).resource;
if (isEqual(uri, candidate)) {
return cur;
}
}
return undefined;
}
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
// todo@joh reuse explorer implementations?
config.dataSource = this._instantiationService.createInstance(FileDataSource);
config.renderer = this._instantiationService.createInstance(FileRenderer);
config.sorter = new FileSorter();
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);
}
}
}
//#endregion
//#region - Symbols
class HighlightingOutlineRenderer extends OutlineRenderer implements IHighlightingRenderer {
updateHighlights(tree: ITree, pattern: string): any {
let model = OutlineModel.get(tree.getInput());
return model.updateMatches(pattern);
}
}
export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
protected _getInput(input: BreadcrumbElement): any {
let element = input as TreeElement;
let model = OutlineModel.get(element);
model.updateMatches('');
return model;
}
protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any {
return input instanceof OutlineModel ? undefined : input;
}
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
config.dataSource = this._instantiationService.createInstance(OutlineDataSource);
config.renderer = this._instantiationService.createInstance(HighlightingOutlineRenderer);
config.sorter = new OutlineItemComparator();
return config;
}
protected _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined {
if (e.payload && e.payload.didClickOnTwistie) {
return;
}
let [first] = e.selection;
if (first instanceof OutlineElement) {
return first;
}
}
}
//#endregion

View File

@@ -28,19 +28,25 @@ import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import {
CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, OpenToSideAction, RevertAndCloseEditorAction,
NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInGroupOneAction,
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, NavigateLastAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction,
OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup, OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction,
ShowEditorsInGroupThreeAction, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorToFirstGroupAction, MoveEditorToSecondGroupAction, MoveEditorToThirdGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction, OpenLastEditorInGroup
CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, OpenToSideFromQuickOpenAction, RevertAndCloseEditorAction,
NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, ResetGroupSizesAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup,
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, NavigateLastAction, ReopenClosedEditorAction,
OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, ClearEditorHistoryAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup,
OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction,
MoveEditorToNextGroupAction, MoveEditorToFirstGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction, OpenLastEditorInGroup,
ShowEditorsInActiveGroupAction, MoveEditorToLastGroupAction, OpenFirstEditorInGroup, MoveGroupUpAction, MoveGroupDownAction, FocusLastGroupAction, SplitEditorLeftAction, SplitEditorRightAction,
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
} from 'vs/workbench/browser/parts/editor/editorActions';
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { getQuickNavigateHandler, inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { isMacintosh } from 'vs/base/common/platform';
import { GroupOnePicker, GroupTwoPicker, GroupThreePicker, AllEditorsPicker } from 'vs/workbench/browser/parts/editor/editorPicker';
import { AllEditorsPicker, ActiveEditorGroupPicker } from 'vs/workbench/browser/parts/editor/editorPicker';
import { Schemas } from 'vs/base/common/network';
// Register String Editor
@@ -103,10 +109,9 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
constructor(
@ITextFileService private textFileService: ITextFileService
) {
}
) { }
public serialize(editorInput: EditorInput): string {
serialize(editorInput: EditorInput): string {
if (!this.textFileService.isHotExitEnabled) {
return null; // never restore untitled unless hot exit is enabled
}
@@ -120,7 +125,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
let resource = untitledEditorInput.getResource();
if (untitledEditorInput.hasAssociatedFilePath) {
resource = URI.file(resource.fsPath); // untitled with associated file path use the file schema
resource = resource.with({ scheme: Schemas.file }); // untitled with associated file path use the file schema
}
const serialized: ISerializedUntitledEditorInput = {
@@ -133,7 +138,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
return JSON.stringify(serialized);
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledEditorInput {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledEditorInput {
return instantiationService.invokeFunction<UntitledEditorInput>(accessor => {
const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput);
const resource = !!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource);
@@ -141,7 +146,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
const language = deserialized.modeId;
const encoding = deserialized.encoding;
return accessor.get(IWorkbenchEditorService).createInput({ resource, filePath, language, encoding }) as UntitledEditorInput;
return accessor.get(IEditorService).createInput({ resource, filePath, language, encoding }) as UntitledEditorInput;
});
}
}
@@ -162,7 +167,7 @@ interface ISerializedSideBySideEditorInput {
// Register Side by Side Editor Input Factory
class SideBySideEditorInputFactory implements IEditorInputFactory {
public serialize(editorInput: EditorInput): string {
serialize(editorInput: EditorInput): string {
const input = <SideBySideEditorInput>editorInput;
if (input.details && input.master) {
@@ -190,7 +195,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
return null;
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
@@ -223,25 +228,25 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEOLAction, Chang
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding');
export class QuickOpenActionContributor extends ActionBarContributor {
private openToSideActionInstance: OpenToSideAction;
private openToSideActionInstance: OpenToSideFromQuickOpenAction;
constructor(@IInstantiationService private instantiationService: IInstantiationService) {
super();
}
public hasActions(context: any): boolean {
hasActions(context: any): boolean {
const entry = this.getEntry(context);
return !!entry;
}
public getActions(context: any): IAction[] {
getActions(context: any): IAction[] {
const actions: Action[] = [];
const entry = this.getEntry(context);
if (entry) {
if (!this.openToSideActionInstance) {
this.openToSideActionInstance = this.instantiationService.createInstance(OpenToSideAction);
this.openToSideActionInstance = this.instantiationService.createInstance(OpenToSideFromQuickOpenAction);
} else {
this.openToSideActionInstance.updateClass();
}
@@ -269,50 +274,20 @@ const editorPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExp
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
GroupOnePicker,
GroupOnePicker.ID,
editorCommands.NAVIGATE_IN_GROUP_ONE_PREFIX,
ActiveEditorGroupPicker,
ActiveEditorGroupPicker.ID,
editorCommands.NAVIGATE_IN_ACTIVE_GROUP_PREFIX,
editorPickerContextKey,
[
{
prefix: editorCommands.NAVIGATE_IN_GROUP_ONE_PREFIX,
prefix: editorCommands.NAVIGATE_IN_ACTIVE_GROUP_PREFIX,
needsEditor: false,
description: nls.localize('groupOnePicker', "Show Editors in First Group")
},
{
prefix: editorCommands.NAVIGATE_IN_GROUP_TWO_PREFIX,
needsEditor: false,
description: nls.localize('groupTwoPicker', "Show Editors in Second Group")
},
{
prefix: editorCommands.NAVIGATE_IN_GROUP_THREE_PREFIX,
needsEditor: false,
description: nls.localize('groupThreePicker', "Show Editors in Third Group")
description: nls.localize('groupOnePicker', "Show Editors in Active Group")
}
]
)
);
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
GroupTwoPicker,
GroupTwoPicker.ID,
editorCommands.NAVIGATE_IN_GROUP_TWO_PREFIX,
editorPickerContextKey,
[]
)
);
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
GroupThreePicker,
GroupThreePicker.ID,
editorCommands.NAVIGATE_IN_GROUP_THREE_PREFIX,
editorPickerContextKey,
[]
)
);
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
AllEditorsPicker,
@@ -333,48 +308,73 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
const category = nls.localize('view', "View");
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditorInGroup, OpenNextEditorInGroup.ID, OpenNextEditorInGroup.LABEL), 'View: Open Next Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorInGroup, OpenPreviousEditorInGroup.ID, OpenPreviousEditorInGroup.LABEL), 'View: Open Previous Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLastEditorInGroup, OpenLastEditorInGroup.ID, OpenLastEditorInGroup.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_9 }), 'View: Open Last Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLastEditorInGroup, OpenLastEditorInGroup.ID, OpenLastEditorInGroup.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9], mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9] } }), 'View: Open Last Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFirstEditorInGroup, OpenFirstEditorInGroup.ID, OpenFirstEditorInGroup.LABEL), 'View: Open First Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorAction.ID, OpenNextRecentlyUsedEditorAction.LABEL), 'View: Open Next Recently Used Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction.ID, OpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Open Previous Recently Used Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupOneAction, ShowEditorsInGroupOneAction.ID, ShowEditorsInGroupOneAction.LABEL), 'View: Show Editors in First Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupTwoAction, ShowEditorsInGroupTwoAction.ID, ShowEditorsInGroupTwoAction.LABEL), 'View: Show Editors in Second Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupThreeAction, ShowEditorsInGroupThreeAction.ID, ShowEditorsInGroupThreeAction.LABEL), 'View: Show Editors in Third Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInActiveGroupAction, ShowEditorsInActiveGroupAction.ID, ShowEditorsInActiveGroupAction.LABEL), 'View: Show Editors in Active Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET] } }), 'View: Open Next Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'File: Clear Recently Opened', nls.localize('file', "File"));
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(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(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);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorRightAction, SplitEditorRightAction.ID, SplitEditorRightAction.LABEL), 'View: Split Editor Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorUpAction, SplitEditorUpAction.ID, SplitEditorUpAction.LABEL), 'Split Editor Up', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorDownAction, SplitEditorDownAction.ID, SplitEditorDownAction.LABEL), 'View: Split Editor Down', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, JoinTwoGroupsAction.ID, JoinTwoGroupsAction.LABEL), 'View: Join Editors of Two Groups', category);
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(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSecondGroupAction, FocusSecondGroupAction.ID, FocusSecondGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_2 }), 'View: Focus Second Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusThirdGroupAction, FocusThirdGroupAction.ID, FocusThirdGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_3 }), 'View: Focus Third Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLastEditorInStackAction, FocusLastEditorInStackAction.ID, FocusLastEditorInStackAction.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0 } }), 'View: Open Last Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EvenGroupWidthsAction, EvenGroupWidthsAction.ID, EvenGroupWidthsAction.LABEL), 'View: Even Editor Group Widths', 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(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Minimize Other Editor Groups', 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);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupLeftAction, MoveGroupLeftAction.ID, MoveGroupLeftAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupUpAction, MoveGroupUpAction.ID, MoveGroupUpAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.UpArrow) }), 'View: Move Editor Group Up', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupDownAction, MoveGroupDownAction.ID, MoveGroupDownAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.DownArrow) }), 'View: Move Editor Group Down', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToFirstGroupAction, MoveEditorToFirstGroupAction.ID, MoveEditorToFirstGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_1 } }), 'View: Move Editor into First Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToSecondGroupAction, MoveEditorToSecondGroupAction.ID, MoveEditorToSecondGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_2, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_2 } }), 'View: Move Editor into Second Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToThirdGroupAction, MoveEditorToThirdGroupAction.ID, MoveEditorToThirdGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_3, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_3 } }), 'View: Move Editor into Third Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Previous Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Next Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLastGroupAction, MoveEditorToLastGroupAction.ID, MoveEditorToLastGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_9, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_9 } }), 'View: Move Editor into Last Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLeftGroupAction, MoveEditorToLeftGroupAction.ID, MoveEditorToLeftGroupAction.LABEL), 'View: Move Editor into Left Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToRightGroupAction, MoveEditorToRightGroupAction.ID, MoveEditorToRightGroupAction.LABEL), 'View: Move Editor into Right Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToAboveGroupAction, MoveEditorToAboveGroupAction.ID, MoveEditorToAboveGroupAction.LABEL), 'View: Move Editor into Above Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToBelowGroupAction, MoveEditorToBelowGroupAction.ID, MoveEditorToBelowGroupAction.LABEL), 'View: Move Editor into Below Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLastGroupAction, FocusLastGroupAction.ID, FocusLastGroupAction.LABEL), 'View: Focus Last Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL), 'View: Focus Previous Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL), 'View: Focus Next Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLeftGroup, FocusLeftGroup.ID, FocusLeftGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Left Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusRightGroup, FocusRightGroup.ID, FocusRightGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Right Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusAboveGroup, FocusAboveGroup.ID, FocusAboveGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.UpArrow) }), 'View: Focus Above Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusBelowGroup, FocusBelowGroup.ID, FocusBelowGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.DownArrow) }), 'View: Focus Below Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupLeftAction, NewEditorGroupLeftAction.ID, NewEditorGroupLeftAction.LABEL), 'View: New Editor Group to the Left', category);
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(NavigateLastAction, NavigateLastAction.ID, NavigateLastAction.LABEL), 'Go Last');
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History');
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History');
registry.registerWorkbenchAction(new SyncActionDescriptor(RevertAndCloseEditorAction, RevertAndCloseEditorAction.ID, RevertAndCloseEditorAction.LABEL), 'View: Revert and Close Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutSingleAction, EditorLayoutSingleAction.ID, EditorLayoutSingleAction.LABEL), 'View: Single Column Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsAction, EditorLayoutTwoColumnsAction.ID, EditorLayoutTwoColumnsAction.LABEL), 'View: Two Columns Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeColumnsAction, EditorLayoutThreeColumnsAction.ID, EditorLayoutThreeColumnsAction.LABEL), 'View: Three Columns Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoRowsAction, EditorLayoutTwoRowsAction.ID, EditorLayoutTwoRowsAction.LABEL), 'View: Two Rows Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeRowsAction, EditorLayoutThreeRowsAction.ID, EditorLayoutThreeRowsAction.LABEL), 'View: Three Rows Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoByTwoGridAction, EditorLayoutTwoByTwoGridAction.ID, EditorLayoutTwoByTwoGridAction.LABEL), 'View: Grid Editor Layout (2x2)', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoRowsRightAction, EditorLayoutTwoRowsRightAction.ID, EditorLayoutTwoRowsRightAction.LABEL), 'View: Two Rows Right Editor Layout', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category);
// Register Editor Picker Actions including quick navigate support
const openNextEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } };
@@ -385,7 +385,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUs
const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: quickOpenNavigateNextInEditorPickerId,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
weight: KeybindingWeight.WorkbenchContrib + 50,
handler: getQuickNavigateHandler(quickOpenNavigateNextInEditorPickerId, true),
when: editorPickerContext,
primary: openNextEditorKeybinding.primary,
@@ -395,30 +395,36 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const quickOpenNavigatePreviousInEditorPickerId = 'workbench.action.quickOpenNavigatePreviousInEditorPicker';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: quickOpenNavigatePreviousInEditorPickerId,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
weight: KeybindingWeight.WorkbenchContrib + 50,
handler: getQuickNavigateHandler(quickOpenNavigatePreviousInEditorPickerId, false),
when: editorPickerContext,
primary: openPreviousEditorKeybinding.primary,
mac: openPreviousEditorKeybinding.mac
});
// Editor Commands
editorCommands.setup();
// Touch Bar
if (isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconPath: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')).fsPath } },
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')) } },
group: 'navigation'
});
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconPath: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')).fsPath } },
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')) } },
group: 'navigation'
});
}
// Empty Editor Group Context Menu
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_UP, title: nls.localize('splitUp', "Split Up") }, group: '2_split', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '2_split', order: 20 });
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '2_split', order: 30 });
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '2_split', order: 40 });
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.CLOSE_EDITOR_GROUP_COMMAND_ID, title: nls.localize('close', "Close") }, group: '3_close', order: 10, when: ContextKeyExpr.has('multipleEditorGroups') });
// Editor Title Context Menu
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close") }, group: '1_close', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOthers', "Close Others") }, group: '1_close', order: 20 });
@@ -426,6 +432,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '1_close', order: 40 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '1_close', order: 50 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepOpen', "Keep Open") }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_UP, title: nls.localize('splitUp', "Split Up") }, group: '5_split', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '5_split', order: 20 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '5_split', order: 30 });
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') });
@@ -433,9 +443,236 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.
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, {
command: {
id: primary.id,
title: primary.title,
iconLocation: {
dark: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconDark}`)),
light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconLight}`))
}
},
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
});
}
// Editor Title Menu: Split Editor
appendEditorToolItem(
{
id: SplitEditorAction.ID,
title: nls.localize('splitEditorRight', "Split Editor Right"),
iconDark: 'split-editor-horizontal-inverse.svg',
iconLight: 'split-editor-horizontal.svg'
}, {
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(
{
id: SplitEditorAction.ID,
title: nls.localize('splitEditorDown', "Split Editor Down"),
iconDark: 'split-editor-vertical-inverse.svg',
iconLight: 'split-editor-vertical.svg'
}, {
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)
appendEditorToolItem(
{
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
title: nls.localize('close', "Close"),
iconDark: 'close-big-inverse-alt.svg',
iconLight: 'close-big-alt.svg'
}, {
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(
{
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
title: nls.localize('close', "Close"),
iconDark: 'close-dirty-inverse-alt.svg',
iconLight: 'close-dirty-alt.svg'
}, {
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.has('groupActiveEditorDirty')),
1000000 // towards the end
);
// Editor Commands for Command Palette
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepEditor', "Keep Editor"), category }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeEditorsInGroup', "Close All Editors in Group"), category } });
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), category } });
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOtherEditors', "Close Other Editors"), category } });
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRightEditors', "Close Editors to the Right"), category } });
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 } });
// File menu
MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, {
group: '1_editor',
command: {
id: ReopenClosedEditorAction.ID,
title: nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, {
group: 'z_clear',
command: {
id: ClearRecentFilesAction.ID,
title: nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")
},
order: 1
});
// Layout menu
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_appearance',
title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"),
submenu: MenuId.MenubarLayoutMenu,
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '1_split',
command: {
id: editorCommands.SPLIT_EDITOR_UP,
title: nls.localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '1_split',
command: {
id: editorCommands.SPLIT_EDITOR_DOWN,
title: nls.localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '1_split',
command: {
id: editorCommands.SPLIT_EDITOR_LEFT,
title: nls.localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left")
},
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '1_split',
command: {
id: editorCommands.SPLIT_EDITOR_RIGHT,
title: nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right")
},
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '2_layouts',
command: {
id: EditorLayoutSingleAction.ID,
title: nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '2_layouts',
command: {
id: EditorLayoutTwoColumnsAction.ID,
title: nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns")
},
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '2_layouts',
command: {
id: EditorLayoutThreeColumnsAction.ID,
title: nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns")
},
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '2_layouts',
command: {
id: EditorLayoutTwoRowsAction.ID,
title: nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows")
},
order: 5
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '2_layouts',
command: {
id: EditorLayoutThreeRowsAction.ID,
title: nls.localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows")
},
order: 6
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '2_layouts',
command: {
id: EditorLayoutTwoByTwoGridAction.ID,
title: nls.localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)")
},
order: 7
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '2_layouts',
command: {
id: EditorLayoutTwoRowsRightAction.ID,
title: nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right")
},
order: 8
});
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
group: '2_layouts',
command: {
id: EditorLayoutTwoColumnsBottomAction.ID,
title: nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom")
},
order: 9
});

View File

@@ -0,0 +1,163 @@
/*---------------------------------------------------------------------------------------------
* 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 { 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';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Dimension } from 'vs/base/browser/dom';
import { Event } from 'vs/base/common/event';
import { assign } from 'vs/base/common/objects';
import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
export const EDITOR_TITLE_HEIGHT = 35;
export const DEFAULT_EDITOR_MIN_DIMENSIONS = new Dimension(220, 70);
export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
export interface IEditorPartOptions extends IWorkbenchEditorPartConfiguration {
iconTheme?: string;
}
export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
showTabs: true,
tabCloseButton: 'right',
tabSizing: 'fit',
showIcons: true,
enablePreview: true,
openPositioning: 'right',
openSideBySideDirection: 'right',
closeEmptyGroups: true,
labelFormat: 'default',
iconTheme: 'vs-seti'
};
export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean {
return event.affectsConfiguration('workbench.editor') || event.affectsConfiguration('workbench.iconTheme');
}
export function getEditorPartOptions(config: IWorkbenchEditorConfiguration): IEditorPartOptions {
const options: IEditorPartOptions = assign(Object.create(null), DEFAULT_EDITOR_PART_OPTIONS);
if (!config || !config.workbench) {
return options;
}
if (typeof config.workbench.iconTheme === 'string') {
options.iconTheme = config.workbench.iconTheme;
}
if (config.workbench.editor) {
assign(options, config.workbench.editor);
}
return options;
}
export interface IEditorPartOptionsChangeEvent {
oldPartOptions: IEditorPartOptions;
newPartOptions: IEditorPartOptions;
}
export interface IEditorOpeningEvent extends IEditorIdentifier {
options?: IEditorOptions;
/**
* Allows to prevent the opening of an editor by providing a callback
* that will be executed instead. By returning another editor promise
* it is possible to override the opening with another editor. It is ok
* to return a promise that resolves to NULL to prevent the opening
* altogether.
*/
prevent(callback: () => Thenable<any>): void;
}
export interface IEditorGroupsAccessor {
readonly groups: IEditorGroupView[];
readonly activeGroup: IEditorGroupView;
readonly partOptions: IEditorPartOptions;
readonly onDidEditorPartOptionsChange: Event<IEditorPartOptionsChangeEvent>;
getGroup(identifier: GroupIdentifier): IEditorGroupView;
getGroups(order: GroupsOrder): IEditorGroupView[];
activateGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView;
addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): IEditorGroupView;
mergeGroup(group: IEditorGroupView | GroupIdentifier, target: IEditorGroupView | GroupIdentifier, options?: IMergeGroupOptions): IEditorGroupView;
moveGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView;
copyGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView;
removeGroup(group: IEditorGroupView | GroupIdentifier): void;
}
export interface IEditorGroupView extends IDisposable, ISerializableView, IEditorGroup {
readonly group: EditorGroup;
readonly whenRestored: TPromise<void>;
readonly disposed: boolean;
readonly onDidFocus: Event<void>;
readonly onWillDispose: Event<void>;
readonly onWillOpenEditor: Event<IEditorOpeningEvent>;
readonly onDidOpenEditorFail: Event<IEditorInput>;
readonly onWillCloseEditor: Event<IEditorCloseEvent>;
readonly onDidCloseEditor: Event<IEditorCloseEvent>;
isEmpty(): boolean;
setActive(isActive: boolean): void;
setLabel(label: string): void;
relayout(): void;
shutdown(): void;
}
export function getActiveTextEditorOptions(group: IEditorGroup, expectedActiveEditor?: IEditorInput, presetOptions?: EditorOptions): EditorOptions {
const activeGroupCodeEditor = group.activeControl ? getCodeEditor(group.activeControl.getControl()) : void 0;
if (activeGroupCodeEditor) {
if (!expectedActiveEditor || expectedActiveEditor.matches(group.activeEditor)) {
return TextEditorOptions.fromEditor(activeGroupCodeEditor, presetOptions);
}
}
return presetOptions || new EditorOptions();
}
/**
* A sub-interface of IEditorService to hide some workbench-core specific
* events from clients.
*/
export interface EditorServiceImpl extends IEditorService {
/**
* Emitted when an editor is closed.
*/
readonly onDidCloseEditor: Event<IEditorCloseEvent>;
/**
* Emitted when an editor failed to open.
*/
readonly onDidOpenEditorFail: Event<IEditorIdentifier>;
}
/**
* A sub-interface of IEditorGroupsService to hide some workbench-core specific
* methods from clients.
*/
export interface EditorGroupsServiceImpl extends IEditorGroupsService {
/**
* A promise that resolves when groups have been restored.
*/
readonly whenRestored: TPromise<void>;
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,14 +6,11 @@
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ActiveEditorMoveArguments, ActiveEditorMovePositioning, ActiveEditorMovePositioningBy, EditorCommands, TextCompareEditorVisible, EditorInput, IEditorIdentifier, IEditorCommandsContext } from 'vs/workbench/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditor, Position, POSITIONS, Direction, IEditorInput } from 'vs/platform/editor/common/editor';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditor, IEditorInput } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
import { EditorStacksModel, EditorGroup } from 'vs/workbench/common/editor/editorStacksModel';
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
@@ -22,26 +19,37 @@ 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';
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
export const CLOSE_EDITORS_AND_GROUP_COMMAND_ID = 'workbench.action.closeEditorsAndGroup';
export const CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID = 'workbench.action.closeEditorsToTheRight';
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.action.closeActiveEditor';
export const CLOSE_EDITOR_GROUP_COMMAND_ID = 'workbench.action.closeGroup';
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
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 NAVIGATE_IN_GROUP_ONE_PREFIX = 'edt one ';
export const NAVIGATE_IN_GROUP_TWO_PREFIX = 'edt two ';
export const NAVIGATE_IN_GROUP_THREE_PREFIX = 'edt three ';
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp';
export const SPLIT_EDITOR_DOWN = 'workbench.action.splitEditorDown';
export const SPLIT_EDITOR_LEFT = 'workbench.action.splitEditorLeft';
export const SPLIT_EDITOR_RIGHT = 'workbench.action.splitEditorRight';
export function setup(): void {
registerActiveEditorMoveCommand();
registerDiffEditorCommands();
registerOpenEditorAtIndexCommands();
registerEditorCommands();
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active ';
export interface ActiveEditorMoveArguments {
to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
by?: 'tab' | 'group';
value?: number;
}
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
@@ -49,17 +57,15 @@ const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean
return false;
}
const activeEditorMoveArg: ActiveEditorMoveArguments = arg;
if (!types.isString(activeEditorMoveArg.to)) {
if (!types.isString(arg.to)) {
return false;
}
if (!types.isUndefined(activeEditorMoveArg.by) && !types.isString(activeEditorMoveArg.by)) {
if (!types.isUndefined(arg.by) && !types.isString(arg.by)) {
return false;
}
if (!types.isUndefined(activeEditorMoveArg.value) && !types.isNumber(activeEditorMoveArg.value)) {
if (!types.isUndefined(arg.value) && !types.isNumber(arg.value)) {
return false;
}
@@ -68,8 +74,8 @@ const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean
function registerActiveEditorMoveCommand(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: EditorCommands.MoveActiveEditor,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
id: MOVE_ACTIVE_EDITOR_COMMAND_ID,
weight: KeybindingWeight.WorkbenchContrib,
when: EditorContextKeys.editorTextFocus,
primary: null,
handler: (accessor, args: any) => moveActiveEditor(args, accessor),
@@ -78,7 +84,7 @@ function registerActiveEditorMoveCommand(): void {
args: [
{
name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move. By tab or by group.\n\t* 'value': Number value providing how many positions or an absolute position to move."),
description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."),
constraint: isActiveEditorMoveArg
}
]
@@ -86,98 +92,153 @@ function registerActiveEditorMoveCommand(): void {
});
}
function moveActiveEditor(args: ActiveEditorMoveArguments = {}, accessor: ServicesAccessor): void {
args.to = args.to || ActiveEditorMovePositioning.RIGHT;
args.by = args.by || ActiveEditorMovePositioningBy.TAB;
args.value = types.isUndefined(args.value) ? 1 : args.value;
function moveActiveEditor(args: ActiveEditorMoveArguments = Object.create(null), accessor: ServicesAccessor): void {
args.to = args.to || 'right';
args.by = args.by || 'tab';
args.value = typeof args.value === 'number' ? args.value : 1;
const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
if (activeEditor) {
const activeControl = accessor.get(IEditorService).activeControl;
if (activeControl) {
switch (args.by) {
case ActiveEditorMovePositioningBy.TAB:
return moveActiveTab(args, activeEditor, accessor);
case ActiveEditorMovePositioningBy.GROUP:
return moveActiveEditorToGroup(args, activeEditor, accessor);
case 'tab':
return moveActiveTab(args, activeControl, accessor);
case 'group':
return moveActiveEditorToGroup(args, activeControl, accessor);
}
}
}
function moveActiveTab(args: ActiveEditorMoveArguments, activeEditor: IEditor, accessor: ServicesAccessor): void {
const editorGroupsService: IEditorGroupService = accessor.get(IEditorGroupService);
const editorGroup = editorGroupsService.getStacksModel().groupAt(activeEditor.position);
let index = editorGroup.indexOf(activeEditor.input);
function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
const group = control.group;
let index = group.getIndexOfEditor(control.input);
switch (args.to) {
case ActiveEditorMovePositioning.FIRST:
case 'first':
index = 0;
break;
case ActiveEditorMovePositioning.LAST:
index = editorGroup.count - 1;
case 'last':
index = group.count - 1;
break;
case ActiveEditorMovePositioning.LEFT:
case 'left':
index = index - args.value;
break;
case ActiveEditorMovePositioning.RIGHT:
case 'right':
index = index + args.value;
break;
case ActiveEditorMovePositioning.CENTER:
index = Math.round(editorGroup.count / 2) - 1;
case 'center':
index = Math.round(group.count / 2) - 1;
break;
case ActiveEditorMovePositioning.POSITION:
case 'position':
index = args.value - 1;
break;
}
index = index < 0 ? 0 : index >= editorGroup.count ? editorGroup.count - 1 : index;
editorGroupsService.moveEditor(activeEditor.input, editorGroup, editorGroup, { index });
index = index < 0 ? 0 : index >= group.count ? group.count - 1 : index;
group.moveEditor(control.input, group, { index });
}
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, activeEditor: IEditor, accessor: ServicesAccessor): void {
let newPosition = activeEditor.position;
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
const editorGroupService = accessor.get(IEditorGroupsService);
const configurationService = accessor.get(IConfigurationService);
const sourceGroup = control.group;
let targetGroup: IEditorGroup;
switch (args.to) {
case ActiveEditorMovePositioning.LEFT:
newPosition = newPosition - 1;
case 'left':
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.LEFT }, sourceGroup);
if (!targetGroup) {
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.LEFT);
}
break;
case ActiveEditorMovePositioning.RIGHT:
newPosition = newPosition + 1;
case 'right':
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.RIGHT }, sourceGroup);
if (!targetGroup) {
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.RIGHT);
}
break;
case ActiveEditorMovePositioning.FIRST:
newPosition = Position.ONE;
case 'up':
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.UP }, sourceGroup);
if (!targetGroup) {
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.UP);
}
break;
case ActiveEditorMovePositioning.LAST:
newPosition = Position.THREE;
case 'down':
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.DOWN }, sourceGroup);
if (!targetGroup) {
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.DOWN);
}
break;
case ActiveEditorMovePositioning.CENTER:
newPosition = Position.TWO;
case 'first':
targetGroup = editorGroupService.findGroup({ location: GroupLocation.FIRST }, sourceGroup);
break;
case ActiveEditorMovePositioning.POSITION:
newPosition = args.value - 1;
case 'last':
targetGroup = editorGroupService.findGroup({ location: GroupLocation.LAST }, sourceGroup);
break;
case 'previous':
targetGroup = editorGroupService.findGroup({ location: GroupLocation.PREVIOUS }, sourceGroup);
break;
case 'next':
targetGroup = editorGroupService.findGroup({ location: GroupLocation.NEXT }, sourceGroup);
if (!targetGroup) {
targetGroup = editorGroupService.addGroup(sourceGroup, preferredSideBySideGroupDirection(configurationService));
}
break;
case 'center':
targetGroup = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)[(editorGroupService.count / 2) - 1];
break;
case 'position':
targetGroup = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)[args.value - 1];
break;
}
newPosition = POSITIONS.indexOf(newPosition) !== -1 ? newPosition : activeEditor.position;
accessor.get(IEditorGroupService).moveEditor(activeEditor.input, activeEditor.position, newPosition);
if (targetGroup) {
sourceGroup.moveEditor(control.input, targetGroup);
targetGroup.focus();
}
}
function registerEditorGroupsLayoutCommand(): void {
CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => {
if (!args || typeof args !== 'object') {
return;
}
const editorGroupService = accessor.get(IEditorGroupsService);
editorGroupService.applyLayout(args);
});
}
export function mergeAllGroups(editorGroupService: IEditorGroupsService): void {
const target = editorGroupService.activeGroup;
editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).forEach(group => {
if (group === target) {
return; // keep target
}
editorGroupService.mergeGroup(group, target);
});
}
function registerDiffEditorCommands(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.action.compareEditor.nextChange',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: TextCompareEditorVisible,
weight: KeybindingWeight.WorkbenchContrib,
when: TextCompareEditorVisibleContext,
primary: null,
handler: accessor => navigateInDiffEditor(accessor, true)
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.action.compareEditor.previousChange',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: TextCompareEditorVisible,
weight: KeybindingWeight.WorkbenchContrib,
when: TextCompareEditorVisibleContext,
primary: null,
handler: accessor => navigateInDiffEditor(accessor, false)
});
function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
let editorService = accessor.get(IWorkbenchEditorService);
const candidates = [editorService.getActiveEditor(), ...editorService.getVisibleEditors()].filter(e => e instanceof TextDiffEditor);
const editorService = accessor.get(IEditorService);
const candidates = [editorService.activeControl, ...editorService.visibleControls].filter(e => e instanceof TextDiffEditor);
if (candidates.length > 0) {
next ? (<TextDiffEditor>candidates[0]).getDiffNavigator().next() : (<TextDiffEditor>candidates[0]).getDiffNavigator().previous();
@@ -186,25 +247,17 @@ function registerDiffEditorCommands(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: TOGGLE_DIFF_INLINE_MODE,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: void 0,
handler: (accessor, resource, context: IEditorCommandsContext) => {
const editorService = accessor.get(IWorkbenchEditorService);
const editorGroupService = accessor.get(IEditorGroupService);
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
let editor: IEditor;
if (context) {
const position = positionAndInput(editorGroupService, editorService, context).position;
editor = editorService.getVisibleEditors()[position];
} else {
editor = editorService.getActiveEditor();
}
if (editor instanceof TextDiffEditor) {
const control = editor.getControl();
const isInlineMode = !control.renderSideBySide;
control.updateOptions(<IDiffEditorOptions>{
const { control } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
if (control instanceof TextDiffEditor) {
const widget = control.getControl();
const isInlineMode = !widget.renderSideBySide;
widget.updateOptions(<IDiffEditorOptions>{
renderSideBySide: isInlineMode
});
}
@@ -221,19 +274,16 @@ function registerOpenEditorAtIndexCommands(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.action.openEditorAtIndex' + visibleIndex,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: KeyMod.Alt | toKeyCode(visibleIndex),
mac: { primary: KeyMod.WinCtrl | toKeyCode(visibleIndex) },
handler: accessor => {
const editorService = accessor.get(IWorkbenchEditorService);
const editorGroupService = accessor.get(IEditorGroupService);
const active = editorService.getActiveEditor();
if (active) {
const group = editorGroupService.getStacksModel().groupAt(active.position);
const editor = group.getEditor(editorIndex);
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);
}
@@ -262,169 +312,243 @@ function registerOpenEditorAtIndexCommands(): void {
}
}
function registerEditorCommands() {
function registerFocusEditorGroupAtIndexCommands(): void {
// Keybindings to focus a specific group (2-8) in the editor area
for (let groupIndex = 1; groupIndex < 8; groupIndex++) {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: toCommandId(groupIndex),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: KeyMod.CtrlCmd | toKeyCode(groupIndex),
handler: accessor => {
const editorGroupService = accessor.get(IEditorGroupsService);
const configurationService = accessor.get(IConfigurationService);
// To keep backwards compatibility (pre-grid), allow to focus a group
// that does not exist as long as it is the next group after the last
// opened group. Otherwise we return.
if (groupIndex > editorGroupService.count) {
return;
}
// Group exists: just focus
const groups = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE);
if (groups[groupIndex]) {
return groups[groupIndex].focus();
}
// Group does not exist: create new by splitting the active one of the last group
const direction = preferredSideBySideGroupDirection(configurationService);
const lastGroup = editorGroupService.findGroup({ location: GroupLocation.LAST });
const newGroup = editorGroupService.addGroup(lastGroup, direction);
// Focus
newGroup.focus();
}
});
}
function toCommandId(index: number): string {
switch (index) {
case 1: return 'workbench.action.focusSecondEditorGroup';
case 2: return 'workbench.action.focusThirdEditorGroup';
case 3: return 'workbench.action.focusFourthEditorGroup';
case 4: return 'workbench.action.focusFifthEditorGroup';
case 5: return 'workbench.action.focusSixthEditorGroup';
case 6: return 'workbench.action.focusSeventhEditorGroup';
case 7: return 'workbench.action.focusEighthEditorGroup';
}
return void 0;
}
function toKeyCode(index: number): KeyCode {
switch (index) {
case 1: return KeyCode.KEY_2;
case 2: return KeyCode.KEY_3;
case 3: return KeyCode.KEY_4;
case 4: return KeyCode.KEY_5;
case 5: return KeyCode.KEY_6;
case 6: return KeyCode.KEY_7;
case 7: return KeyCode.KEY_8;
}
return void 0;
}
}
export function splitEditor(editorGroupService: IEditorGroupsService, direction: GroupDirection, context?: IEditorCommandsContext): void {
let sourceGroup: IEditorGroup;
if (context && typeof context.groupId === 'number') {
sourceGroup = editorGroupService.getGroup(context.groupId);
} else {
sourceGroup = editorGroupService.activeGroup;
}
// Add group
const newGroup = editorGroupService.addGroup(sourceGroup, direction);
// Split editor (if it can be split)
let editorToCopy: IEditorInput;
if (context && typeof context.editorIndex === 'number') {
editorToCopy = sourceGroup.getEditor(context.editorIndex);
} else {
editorToCopy = sourceGroup.activeEditor;
}
if (editorToCopy && (editorToCopy as EditorInput).supportsSplitEditor()) {
sourceGroup.copyEditor(editorToCopy, newGroup);
}
// Focus
newGroup.focus();
}
function registerSplitEditorCommands() {
[
{ id: SPLIT_EDITOR_UP, direction: GroupDirection.UP },
{ id: SPLIT_EDITOR_DOWN, direction: GroupDirection.DOWN },
{ id: SPLIT_EDITOR_LEFT, direction: GroupDirection.LEFT },
{ id: SPLIT_EDITOR_RIGHT, direction: GroupDirection.RIGHT }
].forEach(({ id, direction }) => {
CommandsRegistry.registerCommand(id, function (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) {
splitEditor(accessor.get(IEditorGroupsService), direction, getCommandsContext(resourceOrContext, context));
});
});
}
function registerCloseEditorCommands() {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: CLOSE_SAVED_EDITORS_COMMAND_ID,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const model = editorGroupService.getStacksModel();
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
if (contexts.length === 0 && model.activeGroup) {
// If command is triggered from the command palette use the active group
contexts.push({ groupId: model.activeGroup.id });
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
const activeGroup = editorGroupService.activeGroup;
if (contexts.length === 0) {
contexts.push({ groupId: activeGroup.id }); // active group as fallback
}
let positionOne: { savedOnly: boolean } = void 0;
let positionTwo: { savedOnly: boolean } = void 0;
let positionThree: { savedOnly: boolean } = void 0;
contexts.forEach(c => {
switch (model.positionOfGroup(model.getGroup(c.groupId))) {
case Position.ONE: positionOne = { savedOnly: true }; break;
case Position.TWO: positionTwo = { savedOnly: true }; break;
case Position.THREE: positionThree = { savedOnly: true }; break;
}
});
return editorService.closeEditors({ positionOne, positionTwo, positionThree });
return TPromise.join(distinct(contexts.map(c => c.groupId)).map(groupId =>
editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
));
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
const distinctGroupIds = distinct(contexts.map(c => c.groupId));
const model = editorGroupService.getStacksModel();
if (distinctGroupIds.length) {
return editorService.closeEditors(distinctGroupIds.map(gid => model.positionOfGroup(model.getGroup(gid))));
}
const activeEditor = editorService.getActiveEditor();
if (activeEditor) {
return editorService.closeEditors(activeEditor.position);
if (distinctGroupIds.length === 0) {
distinctGroupIds.push(editorGroupService.activeGroup.id);
}
return TPromise.as(false);
return TPromise.join(distinctGroupIds.map(groupId =>
editorGroupService.getGroup(groupId).closeAllEditors()
));
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: CLOSE_EDITOR_COMMAND_ID,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
const groupIds = distinct(contexts.map(context => context.groupId));
const model = editorGroupService.getStacksModel();
const editorsToClose = new Map<Position, IEditorInput[]>();
groupIds.forEach(groupId => {
const group = model.getGroup(groupId);
const position = model.positionOfGroup(group);
if (position >= 0) {
const inputs = contexts.map(c => {
if (c && groupId === c.groupId && types.isNumber(c.editorIndex)) {
return group.getEditor(c.editorIndex);
}
return group.activeEditor;
}).filter(input => !!input);
if (inputs.length) {
editorsToClose.set(position, inputs);
}
}
});
if (editorsToClose.size === 0) {
const activeEditor = editorService.getActiveEditor();
if (activeEditor) {
return editorService.closeEditor(activeEditor.position, activeEditor.input);
}
const activeGroup = editorGroupService.activeGroup;
if (contexts.length === 0 && activeGroup.activeEditor) {
contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) }); // active editor as fallback
}
return editorService.closeEditors({
positionOne: editorsToClose.get(Position.ONE),
positionTwo: editorsToClose.get(Position.TWO),
positionThree: editorsToClose.get(Position.THREE)
});
const groupIds = distinct(contexts.map(context => context.groupId));
return TPromise.join(groupIds.map(groupId => {
const group = editorGroupService.getGroup(groupId);
const editors = contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
return group.closeEditors(editors);
}));
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: CLOSE_EDITOR_GROUP_COMMAND_ID,
weight: KeybindingWeight.WorkbenchContrib,
when: ContextKeyExpr.and(ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext),
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
const commandsContext = getCommandsContext(resourceOrContext, context);
let group: IEditorGroup;
if (commandsContext && typeof commandsContext.groupId === 'number') {
group = editorGroupService.getGroup(commandsContext.groupId);
} else {
group = editorGroupService.activeGroup;
}
editorGroupService.removeGroup(group);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: void 0,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
const model = editorGroupService.getStacksModel();
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
if (contexts.length === 0) {
// Cover the case when run from command palette
const activeGroup = model.activeGroup;
const activeEditor = editorService.getActiveEditorInput();
if (activeGroup && activeEditor) {
contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.indexOf(activeEditor) });
}
const activeGroup = editorGroupService.activeGroup;
if (contexts.length === 0 && activeGroup.activeEditor) {
contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) }); // active editor as fallback
}
const groupIds = distinct(contexts.map(context => context.groupId));
const editorsToClose = new Map<Position, IEditorInput[]>();
groupIds.forEach(groupId => {
const group = model.getGroup(groupId);
const inputsToSkip = contexts.map(c => {
if (c.groupId === groupId && types.isNumber(c.editorIndex)) {
return group.getEditor(c.editorIndex);
}
return void 0;
}).filter(input => !!input);
return TPromise.join(groupIds.map(groupId => {
const group = editorGroupService.getGroup(groupId);
const editors = contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
const toClose = group.getEditors().filter(input => inputsToSkip.indexOf(input) === -1);
editorsToClose.set(model.positionOfGroup(group), toClose);
});
return editorService.closeEditors({
positionOne: editorsToClose.get(Position.ONE),
positionTwo: editorsToClose.get(Position.TWO),
positionThree: editorsToClose.get(Position.THREE)
});
return group.closeEditors(editorsToClose);
}));
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: void 0,
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
const { position, input } = positionAndInput(editorGroupService, editorService, context);
if (typeof position === 'number' && input) {
return editorService.closeEditors(position, { except: input, direction: Direction.RIGHT });
const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
if (group && editor) {
return group.closeEditors({ direction: CloseDirection.RIGHT, except: editor });
}
return TPromise.as(false);
@@ -433,17 +557,15 @@ function registerEditorCommands() {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEEP_EDITOR_COMMAND_ID,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter),
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
const { position, input } = positionAndInput(editorGroupService, editorService, context);
if (typeof position === 'number' && input) {
return editorGroupService.pinEditor(position, input);
const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
if (group && editor) {
return group.pinEditor(editor);
}
return TPromise.as(false);
@@ -452,88 +574,98 @@ function registerEditorCommands() {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: SHOW_EDITORS_IN_GROUP,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
weight: KeybindingWeight.WorkbenchContrib,
when: void 0,
primary: void 0,
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
const quickOpenService = accessor.get(IQuickOpenService);
const stacks = editorGroupService.getStacksModel();
const groupCount = stacks.groups.length;
if (groupCount <= 1) {
if (editorGroupService.count <= 1) {
return quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
}
const { position } = positionAndInput(editorGroupService, editorService, context);
switch (position) {
case Position.TWO:
return quickOpenService.show(NAVIGATE_IN_GROUP_TWO_PREFIX);
case Position.THREE:
return quickOpenService.show(NAVIGATE_IN_GROUP_THREE_PREFIX);
const commandsContext = getCommandsContext(resourceOrContext, context);
if (commandsContext && typeof commandsContext.groupId === 'number') {
editorGroupService.activateGroup(editorGroupService.getGroup(commandsContext.groupId)); // we need the group to be active
}
return quickOpenService.show(NAVIGATE_IN_GROUP_ONE_PREFIX);
return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: '_workbench.printStacksModel',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
handler(accessor: ServicesAccessor) {
console.log(`${accessor.get(IEditorGroupService).getStacksModel().toString()}\n\n`);
},
when: void 0,
primary: void 0
});
CommandsRegistry.registerCommand(CLOSE_EDITORS_AND_GROUP_COMMAND_ID, (accessor: ServicesAccessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupsService);
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: '_workbench.validateStacksModel',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
handler(accessor: ServicesAccessor) {
(<EditorStacksModel>accessor.get(IEditorGroupService).getStacksModel()).validate();
},
when: void 0,
primary: void 0
const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
if (group) {
return group.closeAllEditors().then(() => {
if (group.count === 0 && editorGroupService.getGroup(group.id) /* could be gone by now */) {
editorGroupService.removeGroup(group); // only remove group if it is now empty
}
});
}
return void 0;
});
}
function positionAndInput(editorGroupService: IEditorGroupService, editorService: IWorkbenchEditorService, context?: IEditorCommandsContext): { position: Position, input: IEditorInput } {
// Resolve from context
const model = editorGroupService.getStacksModel();
const group = context ? model.getGroup(context.groupId) : undefined;
let position = group ? model.positionOfGroup(group) : undefined;
let input = group && types.isNumber(context.editorIndex) ? group.getEditor(context.editorIndex) : undefined;
// If position or input are not passed in take the position and input of the active editor.
const active = editorService.getActiveEditor();
if (active) {
position = typeof position === 'number' ? position : active.position;
input = input ? input : <EditorInput>active.input;
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext {
if (URI.isUri(resourceOrContext)) {
return context;
}
return { position, input };
if (resourceOrContext && typeof resourceOrContext.groupId === 'number') {
return resourceOrContext;
}
if (context && typeof context.groupId === 'number') {
return context;
}
return void 0;
}
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService): IEditorCommandsContext[] {
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
// Resolve from context
let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
let editor = group && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : undefined;
let control = group ? group.activeControl : undefined;
// Fallback to active group as needed
if (!group) {
group = editorGroupService.activeGroup;
editor = <EditorInput>group.activeEditor;
control = group.activeControl;
}
return { group, editor, control };
}
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
// First check for a focused list to return the selected items from
const list = listService.lastFocusedList;
if (list instanceof List && list.isDOMFocused()) {
const elementToContext = (element: IEditorIdentifier | EditorGroup) =>
element instanceof EditorGroup ? { groupId: element.id, editorIndex: undefined } : { groupId: element.group.id, editorIndex: element.group.indexOf(element.editor) };
const onlyEditorGroupAndEditor = (e: IEditorIdentifier | EditorGroup) => e instanceof EditorGroup || ('editor' in e && 'group' in e);
const elementToContext = (element: IEditorIdentifier | IEditorGroup) => {
if (isEditorGroup(element)) {
return { groupId: element.id, editorIndex: void 0 };
}
const focusedElements: (IEditorIdentifier | EditorGroup)[] = list.getFocusedElements().filter(onlyEditorGroupAndEditor);
// need to take into account when editor context is { group: group }
const focus = editorContext ? editorContext : focusedElements.length ? focusedElements.map(elementToContext)[0] : undefined;
return { groupId: element.groupId, editorIndex: editorGroupService.getGroup(element.groupId).getIndexOfEditor(element.editor) };
};
const onlyEditorGroupAndEditor = (e: IEditorIdentifier | IEditorGroup) => isEditorGroup(e) || isEditorIdentifier(e);
const focusedElements: (IEditorIdentifier | IEditorGroup)[] = list.getFocusedElements().filter(onlyEditorGroupAndEditor);
const focus = editorContext ? editorContext : focusedElements.length ? focusedElements.map(elementToContext)[0] : void 0; // need to take into account when editor context is { group: group }
if (focus) {
const selection: (IEditorIdentifier | EditorGroup)[] = list.getSelectedElements().filter(onlyEditorGroupAndEditor);
const selection: (IEditorIdentifier | IEditorGroup)[] = list.getSelectedElements().filter(onlyEditorGroupAndEditor);
// Only respect selection if it contains focused element
if (selection && selection.some(s => s instanceof EditorGroup ? s.id === focus.groupId : s.group.id === focus.groupId && s.group.indexOf(s.editor) === focus.editorIndex)) {
if (selection && selection.some(s => isEditorGroup(s) ? s.id === focus.groupId : s.groupId === focus.groupId && editorGroupService.getGroup(s.groupId).getIndexOfEditor(s.editor) === focus.editorIndex)) {
return selection.map(elementToContext);
}
@@ -544,3 +676,25 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
// Otherwise go with passed in context
return !!editorContext ? [editorContext] : [];
}
function isEditorGroup(thing: any): thing is IEditorGroup {
const group = thing as IEditorGroup;
return group && typeof group.id === 'number' && Array.isArray(group.editors);
}
function isEditorIdentifier(thing: any): thing is IEditorIdentifier {
const identifier = thing as IEditorIdentifier;
return identifier && typeof identifier.groupId === 'number';
}
export function setup(): void {
registerActiveEditorMoveCommand();
registerEditorGroupsLayoutCommand();
registerDiffEditorCommands();
registerOpenEditorAtIndexCommands();
registerCloseEditorCommands();
registerFocusEditorGroupAtIndexCommands();
registerSplitEditorCommands();
}

View File

@@ -0,0 +1,247 @@
/*---------------------------------------------------------------------------------------------
* 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 { 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';
import { Registry } from 'vs/platform/registry/common/platform';
import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor';
import { TPromise } from 'vs/base/common/winjs.base';
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';
export interface IOpenEditorResult {
readonly control: BaseEditor;
readonly editorChanged: boolean;
}
export class EditorControl extends Disposable {
get minimumWidth() { return this._activeControl ? this._activeControl.minimumWidth : DEFAULT_EDITOR_MIN_DIMENSIONS.width; }
get minimumHeight() { return this._activeControl ? this._activeControl.minimumHeight : DEFAULT_EDITOR_MIN_DIMENSIONS.height; }
get maximumWidth() { return this._activeControl ? this._activeControl.maximumWidth : DEFAULT_EDITOR_MAX_DIMENSIONS.width; }
get maximumHeight() { return this._activeControl ? this._activeControl.maximumHeight : DEFAULT_EDITOR_MAX_DIMENSIONS.height; }
private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
get onDidFocus(): Event<void> { return this._onDidFocus.event; }
private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; }>());
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; }> { return this._onDidSizeConstraintsChange.event; }
private _activeControl: BaseEditor;
private controls: BaseEditor[] = [];
private activeControlDisposeables: IDisposable[] = [];
private dimension: Dimension;
private editorOperation: LongRunningOperation;
constructor(
private parent: HTMLElement,
private groupView: IEditorGroupView,
@IPartService private partService: IPartService,
@IInstantiationService private instantiationService: IInstantiationService,
@IProgressService progressService: IProgressService
) {
super();
this.editorOperation = this._register(new LongRunningOperation(progressService));
}
get activeControl(): BaseEditor {
return this._activeControl;
}
openEditor(editor: EditorInput, options?: EditorOptions): TPromise<IOpenEditorResult> {
// Editor control
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editor);
const control = this.doShowEditorControl(descriptor, options);
// Set input
return this.doSetInput(control, editor, options).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
}
private doShowEditorControl(descriptor: IEditorDescriptor, options: EditorOptions): BaseEditor {
// Return early if the currently active editor control can handle the input
if (this._activeControl && descriptor.describes(this._activeControl)) {
return this._activeControl;
}
// Hide active one first
this.doHideActiveEditorControl();
// Create editor
const control = this.doCreateEditorControl(descriptor);
// Set editor as active
this.doSetActiveControl(control);
// Show editor
this.parent.appendChild(control.getContainer());
show(control.getContainer());
// Indicate to editor that it is now visible
control.setVisible(true, this.groupView);
// Layout
if (this.dimension) {
control.layout(this.dimension);
}
return control;
}
private doCreateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
// Instantiate editor
const control = this.doInstantiateEditorControl(descriptor);
// Create editor container as needed
if (!control.getContainer()) {
const controlInstanceContainer = document.createElement('div');
addClass(controlInstanceContainer, 'editor-instance');
controlInstanceContainer.id = descriptor.getId();
control.create(controlInstanceContainer);
}
return control;
}
private doInstantiateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
// Return early if already instantiated
const existingControl = this.controls.filter(control => descriptor.describes(control))[0];
if (existingControl) {
return existingControl;
}
// Otherwise instantiate new
const control = this._register(descriptor.instantiate(this.instantiationService));
this.controls.push(control);
return control;
}
private doSetActiveControl(control: BaseEditor) {
this._activeControl = control;
// Clear out previous active control listeners
this.activeControlDisposeables = dispose(this.activeControlDisposeables);
// Listen to control changes
if (control) {
this.activeControlDisposeables.push(control.onDidSizeConstraintsChange(e => this._onDidSizeConstraintsChange.fire(e)));
this.activeControlDisposeables.push(control.onDidFocus(() => this._onDidFocus.fire()));
}
// Indicate that size constraints could have changed due to new editor
this._onDidSizeConstraintsChange.fire();
}
private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions): TPromise<boolean> {
// If the input did not change, return early and only apply the options
// unless the options instruct us to force open it even if it is the same
const forceReload = options && options.forceReload;
const inputMatches = control.input && control.input.matches(editor);
if (inputMatches && !forceReload) {
// Forward options
control.setOptions(options);
// Still focus as needed
const focus = !options || !options.preserveFocus;
if (focus) {
control.focus();
}
return TPromise.as(false);
}
// 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);
// Call into editor control
const editorWillChange = !inputMatches;
return toWinJsPromise(control.setInput(editor, options, operation.token)).then(() => {
// Focus (unless prevented or another operation is running)
if (operation.isCurrent()) {
const focus = !options || !options.preserveFocus;
if (focus) {
control.focus();
}
}
// Operation done
operation.stop();
return editorWillChange;
}, e => {
// Operation done
operation.stop();
return TPromise.wrapError(e);
});
}
private doHideActiveEditorControl(): void {
if (!this._activeControl) {
return;
}
// Stop any running operation
this.editorOperation.stop();
// Remove control from parent and hide
const controlInstanceContainer = this._activeControl.getContainer();
this.parent.removeChild(controlInstanceContainer);
hide(controlInstanceContainer);
// Indicate to editor control
this._activeControl.clearInput();
this._activeControl.setVisible(false, this.groupView);
// Clear active control
this.doSetActiveControl(null);
}
closeEditor(editor: EditorInput): void {
if (this._activeControl && editor.matches(this._activeControl.input)) {
this.doHideActiveEditorControl();
}
}
layout(dimension: Dimension): void {
this.dimension = dimension;
if (this._activeControl && this.dimension) {
this._activeControl.layout(this.dimension);
}
}
shutdown(): void {
// Forward to all editor controls
this.controls.forEach(editor => editor.shutdown());
}
dispose(): void {
this.activeControlDisposeables = dispose(this.activeControlDisposeables);
super.dispose();
}
}

View File

@@ -0,0 +1,534 @@
/*---------------------------------------------------------------------------------------------
* 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/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';
import { IEditorGroupsAccessor, EDITOR_TITLE_HEIGHT, IEditorGroupView, getActiveTextEditorOptions } from 'vs/workbench/browser/parts/editor/editor';
import { EDITOR_DRAG_AND_DROP_BACKGROUND, Themable } from 'vs/workbench/common/theme';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { isMacintosh } from 'vs/base/common/platform';
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/group/common/editorGroupsService';
import { toDisposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
interface IDropOperation {
splitDirection?: GroupDirection;
}
class DropOverlay extends Themable {
private static OVERLAY_ID = 'monaco-workbench-editor-drop-overlay';
private container: HTMLElement;
private overlay: HTMLElement;
private currentDropOperation: IDropOperation;
private _disposed: boolean;
private readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
private readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
constructor(
private accessor: IEditorGroupsAccessor,
private groupView: IEditorGroupView,
themeService: IThemeService,
private instantiationService: IInstantiationService
) {
super(themeService);
this.create();
}
get disposed(): boolean {
return this._disposed;
}
private create(): void {
const overlayOffsetHeight = this.getOverlayOffsetHeight();
// Container
this.container = document.createElement('div');
this.container.id = DropOverlay.OVERLAY_ID;
this.container.style.top = `${overlayOffsetHeight}px`;
// Parent
this.groupView.element.appendChild(this.container);
addClass(this.groupView.element, 'dragged-over');
this._register(toDisposable(() => {
this.groupView.element.removeChild(this.container);
removeClass(this.groupView.element, 'dragged-over');
}));
// Overlay
this.overlay = document.createElement('div');
addClass(this.overlay, 'editor-group-overlay-indicator');
this.container.appendChild(this.overlay);
// Overlay Event Handling
this.registerListeners();
// Styles
this.updateStyles();
}
protected updateStyles(): void {
// Overlay drop background
this.overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
// Overlay contrast border (if any)
const activeContrastBorderColor = this.getColor(activeContrastBorder);
this.overlay.style.outlineColor = activeContrastBorderColor;
this.overlay.style.outlineOffset = activeContrastBorderColor ? '-2px' : null;
this.overlay.style.outlineStyle = activeContrastBorderColor ? 'dashed' : null;
this.overlay.style.outlineWidth = activeContrastBorderColor ? '2px' : null;
}
private registerListeners(): void {
this._register(new DragAndDropObserver(this.container, {
onDragEnter: e => void 0,
onDragOver: e => {
const isDraggingGroup = this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype);
const isDraggingEditor = this.editorTransfer.hasData(DraggedEditorIdentifier.prototype);
// Update the dropEffect to "copy" if there is no local data to be dragged because
// in that case we can only copy the data into and not move it from its source
if (!isDraggingEditor && !isDraggingGroup) {
e.dataTransfer.dropEffect = 'copy';
}
// Find out if operation is valid
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier) : true;
if (!isCopy) {
const sourceGroupView = this.findSourceGroupView();
if (sourceGroupView === this.groupView) {
if (isDraggingGroup || (isDraggingEditor && sourceGroupView.count < 2)) {
this.hideOverlay();
return; // do not allow to drop group/editor on itself if this results in an empty group
}
}
}
// Position overlay
this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup);
},
onDragLeave: e => this.dispose(),
onDragEnd: e => this.dispose(),
onDrop: e => {
EventHelper.stop(e, true);
// Dispose overlay
this.dispose();
// Handle drop if we have a valid operation
if (this.currentDropOperation) {
this.handleDrop(e, this.currentDropOperation.splitDirection);
}
}
}));
this._register(addDisposableListener(this.container, EventType.MOUSE_OVER, () => {
// Under some circumstances we have seen reports where the drop overlay is not being
// cleaned up and as such the editor area remains under the overlay so that you cannot
// type into the editor anymore. This seems related to using VMs and DND via host and
// guest OS, though some users also saw it without VMs.
// 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);
}));
}
private findSourceGroupView(): IEditorGroupView {
// Check for group transfer
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier);
}
// Check for editor transfer
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier.groupId);
}
return void 0;
}
private handleDrop(event: DragEvent, splitDirection?: GroupDirection): void {
// Determine target group
const ensureTargetGroup = () => {
let targetGroup: IEditorGroupView;
if (typeof splitDirection === 'number') {
targetGroup = this.accessor.addGroup(this.groupView, splitDirection);
} else {
targetGroup = this.groupView;
}
return targetGroup;
};
// Check for group transfer
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier;
// Return if the drop is a no-op
const sourceGroup = this.accessor.getGroup(draggedEditorGroup);
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
return;
}
// Split to new group
let targetGroup: IEditorGroupView;
if (typeof splitDirection === 'number') {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection);
} else {
targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection);
}
}
// Merge into existing group
else {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS });
} else {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView);
}
}
this.accessor.activateGroup(targetGroup);
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
}
// Check for editor transfer
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const targetGroup = ensureTargetGroup();
// Return if the drop is a no-op
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
if (sourceGroup === targetGroup) {
return;
}
// Open in target group
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
targetGroup.openEditor(draggedEditor.editor, options);
// Ensure target has focus
targetGroup.focus();
// Close in source group unless we copy
const copyEditor = this.isCopyOperation(event, draggedEditor);
if (!copyEditor) {
sourceGroup.closeEditor(draggedEditor.editor);
}
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
}
// Check for URI transfer
else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup.focus());
}
}
private isCopyOperation(e: DragEvent, draggedEditor?: IEditorIdentifier): boolean {
if (draggedEditor && !(draggedEditor.editor as EditorInput).supportsSplitEditor()) {
return false;
}
return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
}
private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean): void {
const preferSplitVertically = this.accessor.partOptions.openSideBySideDirection === 'right';
const editorControlWidth = this.groupView.element.clientWidth;
const editorControlHeight = this.groupView.element.clientHeight - this.getOverlayOffsetHeight();
let edgeWidthThresholdFactor: number;
if (isDraggingGroup) {
edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction
} else {
edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors
}
let edgeHeightThresholdFactor: number;
if (isDraggingGroup) {
edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction
} else {
edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors
}
const edgeWidthThreshold = editorControlWidth * edgeWidthThresholdFactor;
const edgeHeightThreshold = editorControlHeight * edgeHeightThresholdFactor;
const splitWidthThreshold = editorControlWidth / 3; // offer to split left/right at 33%
const splitHeightThreshold = editorControlHeight / 3; // offer to split up/down at 33%
// Enable to debug the drop threshold square
// let child = this.overlay.children.item(0) as HTMLElement || this.overlay.appendChild(document.createElement('div'));
// child.style.backgroundColor = 'red';
// child.style.position = 'absolute';
// child.style.width = (groupViewWidth - (2 * edgeWidthThreshold)) + 'px';
// child.style.height = (groupViewHeight - (2 * edgeHeightThreshold)) + 'px';
// child.style.left = edgeWidthThreshold + 'px';
// child.style.top = edgeHeightThreshold + 'px';
// No split if mouse is above certain threshold in the center of the view
let splitDirection: GroupDirection;
if (
mousePosX > edgeWidthThreshold && mousePosX < editorControlWidth - edgeWidthThreshold &&
mousePosY > edgeHeightThreshold && mousePosY < editorControlHeight - edgeHeightThreshold
) {
splitDirection = void 0;
}
// Offer to split otherwise
else {
// User prefers to split vertically: offer a larger hitzone
// for this direction like so:
// ----------------------------------------------
// | | SPLIT UP | |
// | SPLIT |-----------------------| SPLIT |
// | | MERGE | |
// | LEFT |-----------------------| RIGHT |
// | | SPLIT DOWN | |
// ----------------------------------------------
if (preferSplitVertically) {
if (mousePosX < splitWidthThreshold) {
splitDirection = GroupDirection.LEFT;
} else if (mousePosX > splitWidthThreshold * 2) {
splitDirection = GroupDirection.RIGHT;
} else if (mousePosY < editorControlHeight / 2) {
splitDirection = GroupDirection.UP;
} else {
splitDirection = GroupDirection.DOWN;
}
}
// User prefers to split horizontally: offer a larger hitzone
// for this direction like so:
// ----------------------------------------------
// | SPLIT UP |
// |--------------------------------------------|
// | SPLIT LEFT | MERGE | SPLIT RIGHT |
// |--------------------------------------------|
// | SPLIT DOWN |
// ----------------------------------------------
else {
if (mousePosY < splitHeightThreshold) {
splitDirection = GroupDirection.UP;
} else if (mousePosY > splitHeightThreshold * 2) {
splitDirection = GroupDirection.DOWN;
} else if (mousePosX < editorControlWidth / 2) {
splitDirection = GroupDirection.LEFT;
} else {
splitDirection = GroupDirection.RIGHT;
}
}
}
// Draw overlay based on split direction
switch (splitDirection) {
case GroupDirection.UP:
this.doPositionOverlay({ top: '0', left: '0', width: '100%', height: '50%' });
break;
case GroupDirection.DOWN:
this.doPositionOverlay({ top: '50%', left: '0', width: '100%', height: '50%' });
break;
case GroupDirection.LEFT:
this.doPositionOverlay({ top: '0', left: '0', width: '50%', height: '100%' });
break;
case GroupDirection.RIGHT:
this.doPositionOverlay({ top: '0', left: '50%', width: '50%', height: '100%' });
break;
default:
this.doPositionOverlay({ top: '0', left: '0', width: '100%', height: '100%' });
}
// Make sure the overlay is visible now
this.overlay.style.opacity = '1';
// Enable transition after a timeout to prevent initial animation
setTimeout(() => addClass(this.overlay, 'overlay-move-transition'), 0);
// Remember as current split direction
this.currentDropOperation = { splitDirection };
}
private doPositionOverlay(options: { top: string, left: string, width: string, height: string }): void {
// Container
const offsetHeight = this.getOverlayOffsetHeight();
if (offsetHeight) {
this.container.style.height = `calc(100% - ${offsetHeight}px)`;
} else {
this.container.style.height = '100%';
}
// Overlay
this.overlay.style.top = options.top;
this.overlay.style.left = options.left;
this.overlay.style.width = options.width;
this.overlay.style.height = options.height;
}
private getOverlayOffsetHeight(): number {
if (!this.groupView.isEmpty() && this.accessor.partOptions.showTabs) {
return EDITOR_TITLE_HEIGHT; // show overlay below title if group shows tabs
}
return 0;
}
private hideOverlay(): void {
// Reset overlay
this.doPositionOverlay({ top: '0', left: '0', width: '100%', height: '100%' });
this.overlay.style.opacity = '0';
removeClass(this.overlay, 'overlay-move-transition');
// Reset current operation
this.currentDropOperation = void 0;
}
contains(element: HTMLElement): boolean {
return element === this.container || element === this.overlay;
}
dispose(): void {
super.dispose();
this._disposed = true;
}
}
export class EditorDropTarget extends Themable {
private _overlay: DropOverlay;
private counter = 0;
private readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
private readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
constructor(
private accessor: IEditorGroupsAccessor,
private container: HTMLElement,
@IThemeService themeService: IThemeService,
@IInstantiationService private instantiationService: IInstantiationService
) {
super(themeService);
this.registerListeners();
}
private get overlay(): DropOverlay {
if (this._overlay && !this._overlay.disposed) {
return this._overlay;
}
return void 0;
}
private registerListeners(): void {
this._register(addDisposableListener(this.container, EventType.DRAG_ENTER, e => this.onDragEnter(e)));
this._register(addDisposableListener(this.container, EventType.DRAG_LEAVE, () => this.onDragLeave()));
[this.container, window].forEach(node => this._register(addDisposableListener(node as HTMLElement, EventType.DRAG_END, () => this.onDragEnd())));
}
private onDragEnter(event: DragEvent): void {
this.counter++;
// Validate transfer
if (
!this.editorTransfer.hasData(DraggedEditorIdentifier.prototype) &&
!this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype) &&
!event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789
) {
event.dataTransfer.dropEffect = 'none';
return; // unsupported transfer
}
// Signal DND start
this.updateContainer(true);
const target = event.target as HTMLElement;
if (target) {
// Somehow we managed to move the mouse quickly out of the current overlay, so destroy it
if (this.overlay && !this.overlay.contains(target)) {
this.disposeOverlay();
}
// Create overlay over target
if (!this.overlay) {
const targetGroupView = this.findTargetGroupView(target);
if (targetGroupView) {
this._overlay = new DropOverlay(this.accessor, targetGroupView, this.themeService, this.instantiationService);
}
}
}
}
private onDragLeave(): void {
this.counter--;
if (this.counter === 0) {
this.updateContainer(false);
}
}
private onDragEnd(): void {
this.counter = 0;
this.updateContainer(false);
this.disposeOverlay();
}
private findTargetGroupView(child: HTMLElement): IEditorGroupView {
const groups = this.accessor.groups;
for (let i = 0; i < groups.length; i++) {
const groupView = groups[i];
if (isAncestor(child, groupView.element)) {
return groupView;
}
}
return void 0;
}
private updateContainer(isDraggedOver: boolean): void {
toggleClass(this.container, 'dragged-over', isDraggedOver);
}
dispose(): void {
super.dispose();
this.disposeOverlay();
}
private disposeOverlay(): void {
if (this.overlay) {
this.overlay.dispose();
this._overlay = void 0;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,6 @@ 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 * as errors from 'vs/base/common/errors';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';
@@ -16,61 +15,55 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { getIconClasses } from 'vs/workbench/browser/labels';
import { IModelService } from 'vs/editor/common/services/modelService';
import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
import { Position } from 'vs/platform/editor/common/editor';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { EditorInput, toResource, IEditorGroup, IEditorStacksModel } from 'vs/workbench/common/editor';
import { EditorInput, toResource } from 'vs/workbench/common/editor';
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
export class EditorPickerEntry extends QuickOpenEntryGroup {
private stacks: IEditorStacksModel;
constructor(
private editor: EditorInput,
private _group: IEditorGroup,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IModeService private modeService: IModeService,
@IModelService private modelService: IModelService,
@IEditorGroupService editorGroupService: IEditorGroupService
@IModelService private modelService: IModelService
) {
super();
this.stacks = editorGroupService.getStacksModel();
}
public getLabelOptions(): IIconLabelValueOptions {
getLabelOptions(): IIconLabelValueOptions {
return {
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()),
italic: this._group.isPreview(this.editor)
italic: !this._group.isPinned(this.editor)
};
}
public getLabel(): string {
getLabel(): string {
return this.editor.getName();
}
public getIcon(): string {
getIcon(): string {
return this.editor.isDirty() ? 'dirty' : '';
}
public get group(): IEditorGroup {
get group(): IEditorGroup {
return this._group;
}
public getResource(): URI {
getResource(): URI {
return toResource(this.editor, { supportSideBySide: true });
}
public getAriaLabel(): string {
getAriaLabel(): string {
return nls.localize('entryAriaLabel', "{0}, editor group picker", this.getLabel());
}
public getDescription(): string {
getDescription(): string {
return this.editor.getDescription();
}
public run(mode: Mode, context: IEntryRunContext): boolean {
run(mode: Mode, context: IEntryRunContext): boolean {
if (mode === Mode.OPEN) {
return this.runOpen(context);
}
@@ -79,7 +72,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
}
private runOpen(context: IEntryRunContext): boolean {
this.editorService.openEditor(this.editor, null, this.stacks.positionOfGroup(this.group)).done(null, errors.onUnexpectedError);
this._group.openEditor(this.editor);
return true;
}
@@ -90,15 +83,15 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
constructor(
@IInstantiationService protected instantiationService: IInstantiationService,
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
@IEditorGroupService protected editorGroupService: IEditorGroupService
@IEditorService protected editorService: IEditorService,
@IEditorGroupsService protected editorGroupService: IEditorGroupsService
) {
super();
this.scorerCache = Object.create(null);
}
public getResults(searchValue: string): TPromise<QuickOpenModel> {
getResults(searchValue: string): TPromise<QuickOpenModel> {
const editorEntries = this.getEditorEntries();
if (!editorEntries.length) {
return TPromise.as(null);
@@ -123,11 +116,11 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
});
// Sorting
const stacks = this.editorGroupService.getStacksModel();
if (query.value) {
const groups = this.editorGroupService.getGroups(GroupsOrder.CREATION_TIME);
entries.sort((e1, e2) => {
if (e1.group !== e2.group) {
return stacks.positionOfGroup(e1.group) - stacks.positionOfGroup(e2.group);
return groups.indexOf(e1.group) - groups.indexOf(e2.group); // older groups first
}
return compareItemsByScore(e1, e2, query, true, QuickOpenItemAccessor, this.scorerCache);
@@ -135,11 +128,11 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
}
// Grouping (for more than one group)
if (stacks.groups.length > 1) {
if (this.editorGroupService.count > 1) {
let lastGroup: IEditorGroup;
entries.forEach(e => {
if (!lastGroup || lastGroup !== e.group) {
e.setGroupLabel(nls.localize('groupLabel', "Group: {0}", e.group.label));
e.setGroupLabel(e.group.label);
e.setShowBorder(!!lastGroup);
lastGroup = e.group;
}
@@ -149,28 +142,26 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
return TPromise.as(new QuickOpenModel(entries));
}
public onClose(canceled: boolean): void {
onClose(canceled: boolean): void {
this.scorerCache = Object.create(null);
}
protected abstract getEditorEntries(): EditorPickerEntry[];
}
export abstract class EditorGroupPicker extends BaseEditorPicker {
export class ActiveEditorGroupPicker extends BaseEditorPicker {
static readonly ID = 'workbench.picker.activeEditors';
protected getEditorEntries(): EditorPickerEntry[] {
const stacks = this.editorGroupService.getStacksModel();
const group = stacks.groupAt(this.getPosition());
if (!group) {
return [];
}
return group.getEditors(true).map((editor, index) => this.instantiationService.createInstance(EditorPickerEntry, editor, group));
return this.group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE).map((editor, index) => this.instantiationService.createInstance(EditorPickerEntry, editor, this.group));
}
protected abstract getPosition(): Position;
private get group(): IEditorGroup {
return this.editorGroupService.activeGroup;
}
public getEmptyLabel(searchString: string): string {
getEmptyLabel(searchString: string): string {
if (searchString) {
return nls.localize('noResultsFoundInGroup', "No matching opened editor found in group");
}
@@ -178,76 +169,45 @@ export abstract class EditorGroupPicker extends BaseEditorPicker {
return nls.localize('noOpenedEditors', "List of opened editors is currently empty in group");
}
public getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
if (searchValue || !context.quickNavigateConfiguration) {
return {
autoFocusFirstEntry: true
};
}
const stacks = this.editorGroupService.getStacksModel();
const group = stacks.groupAt(this.getPosition());
if (!group) {
return super.getAutoFocus(searchValue, context);
}
const isShiftNavigate = (context.quickNavigateConfiguration && context.quickNavigateConfiguration.keybindings.some(k => {
const [firstPart, chordPart] = k.getParts();
if (chordPart) {
return false;
}
return firstPart.shiftKey;
}));
if (isShiftNavigate) {
return {
autoFocusLastEntry: true
};
}
const editors = this.group.count;
return {
autoFocusFirstEntry: group.count === 1,
autoFocusSecondEntry: group.count > 1
autoFocusFirstEntry: editors === 1,
autoFocusSecondEntry: editors > 1
};
}
}
export class GroupOnePicker extends EditorGroupPicker {
public static readonly ID = 'workbench.picker.editors.one';
protected getPosition(): Position {
return Position.ONE;
}
}
export class GroupTwoPicker extends EditorGroupPicker {
public static readonly ID = 'workbench.picker.editors.two';
protected getPosition(): Position {
return Position.TWO;
}
}
export class GroupThreePicker extends EditorGroupPicker {
public static readonly ID = 'workbench.picker.editors.three';
protected getPosition(): Position {
return Position.THREE;
}
}
export class AllEditorsPicker extends BaseEditorPicker {
public static readonly ID = 'workbench.picker.editors';
static readonly ID = 'workbench.picker.editors';
protected getEditorEntries(): EditorPickerEntry[] {
const entries: EditorPickerEntry[] = [];
const stacks = this.editorGroupService.getStacksModel();
stacks.groups.forEach((group, position) => {
group.getEditors().forEach((editor, index) => {
this.editorGroupService.getGroups(GroupsOrder.CREATION_TIME).forEach(group => {
group.editors.forEach(editor => {
entries.push(this.instantiationService.createInstance(EditorPickerEntry, editor, group));
});
});
@@ -255,7 +215,7 @@ export class AllEditorsPicker extends BaseEditorPicker {
return entries;
}
public getEmptyLabel(searchString: string): string {
getEmptyLabel(searchString: string): string {
if (searchString) {
return nls.localize('noResultsFound', "No matching opened editor found");
}
@@ -263,7 +223,7 @@ export class AllEditorsPicker extends BaseEditorPicker {
return nls.localize('noOpenedEditorsAllGroups', "List of opened editors is currently empty");
}
public getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
if (searchValue) {
return {
autoFocusFirstEntry: true

View File

@@ -20,7 +20,7 @@ import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common
import * as browser from 'vs/base/browser/browser';
import { IMode } from 'vs/editor/common/modes';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IEditorAction } from 'vs/editor/common/editorCommon';
@@ -30,21 +30,19 @@ import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/
import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/indentation';
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import { IEditor as IBaseEditor, IEditorInput } from 'vs/platform/editor/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { getCodeEditor as getEditorWidget, getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
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';
@@ -54,7 +52,7 @@ import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground, foreground, darken, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { deepClone } from 'vs/base/common/objects';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { 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';
@@ -64,11 +62,11 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr
class SideBySideEditorEncodingSupport implements IEncodingSupport {
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
public getEncoding(): string {
getEncoding(): string {
return this.master.getEncoding(); // always report from modified (right hand) side
}
public setEncoding(encoding: string, mode: EncodingMode): void {
setEncoding(encoding: string, mode: EncodingMode): void {
[this.master, this.details].forEach(s => s.setEncoding(encoding, mode));
}
}
@@ -133,7 +131,7 @@ class StateChange {
this.metadata = false;
}
public combine(other: StateChange) {
combine(other: StateChange) {
this.indentation = this.indentation || other.indentation;
this.selectionStatus = this.selectionStatus || other.selectionStatus;
this.mode = this.mode || other.mode;
@@ -158,28 +156,28 @@ interface StateDelta {
class State {
private _selectionStatus: string;
public get selectionStatus(): string { return this._selectionStatus; }
get selectionStatus(): string { return this._selectionStatus; }
private _mode: string;
public get mode(): string { return this._mode; }
get mode(): string { return this._mode; }
private _encoding: string;
public get encoding(): string { return this._encoding; }
get encoding(): string { return this._encoding; }
private _EOL: string;
public get EOL(): string { return this._EOL; }
get EOL(): string { return this._EOL; }
private _indentation: string;
public get indentation(): string { return this._indentation; }
get indentation(): string { return this._indentation; }
private _tabFocusMode: boolean;
public get tabFocusMode(): boolean { return this._tabFocusMode; }
get tabFocusMode(): boolean { return this._tabFocusMode; }
private _screenReaderMode: boolean;
public get screenReaderMode(): boolean { return this._screenReaderMode; }
get screenReaderMode(): boolean { return this._screenReaderMode; }
private _metadata: string;
public get metadata(): string { return this._metadata; }
get metadata(): string { return this._metadata; }
constructor() {
this._selectionStatus = null;
@@ -191,7 +189,7 @@ class State {
this._metadata = null;
}
public update(update: StateDelta): StateChange {
update(update: StateDelta): StateChange {
const e = new StateChange();
let somethingChanged = false;
@@ -282,7 +280,6 @@ function hide(el: HTMLElement): void {
}
export class EditorStatus implements IStatusbarItem {
private state: State;
private element: HTMLElement;
private tabFocusModeElement: HTMLElement;
@@ -300,8 +297,7 @@ export class EditorStatus implements IStatusbarItem {
private screenReaderExplanation: ScreenReaderDetectedExplanation;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@IEditorService private editorService: IEditorService,
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IInstantiationService private instantiationService: IInstantiationService,
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
@@ -314,7 +310,7 @@ export class EditorStatus implements IStatusbarItem {
this.state = new State();
}
public render(container: HTMLElement): IDisposable {
render(container: HTMLElement): IDisposable {
this.element = append(container, $('.editor-statusbar-item'));
this.tabFocusModeElement = append(this.element, $('a.editor-status-tabfocusmode.status-bar-info'));
@@ -370,7 +366,7 @@ export class EditorStatus implements IStatusbarItem {
}
}
},
this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()),
this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()),
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange()),
@@ -545,73 +541,81 @@ export class EditorStatus implements IStatusbarItem {
TabFocus.setTabFocusMode(false);
}
private onEditorsChanged(): void {
const activeEditor = this.editorService.getActiveEditor();
const control = getEditorWidget(activeEditor);
private updateStatusBar(): void {
const activeControl = this.editorService.activeControl;
const activeCodeEditor = activeControl ? getCodeEditor(activeControl.getControl()) : void 0;
// Update all states
this.onScreenReaderModeChange(control);
this.onSelectionChange(control);
this.onModeChange(control);
this.onEOLChange(control);
this.onEncodingChange(activeEditor);
this.onIndentationChange(control);
this.onMetadataChange(activeEditor);
this.onScreenReaderModeChange(activeCodeEditor);
this.onSelectionChange(activeCodeEditor);
this.onModeChange(activeCodeEditor);
this.onEOLChange(activeCodeEditor);
this.onEncodingChange(activeControl);
this.onIndentationChange(activeCodeEditor);
this.onMetadataChange(activeControl);
// Dispose old active editor listeners
dispose(this.activeEditorListeners);
// Attach new listeners to active editor
if (control) {
if (activeCodeEditor) {
// Hook Listener for Configuration changes
this.activeEditorListeners.push(control.onDidChangeConfiguration((event: IConfigurationChangedEvent) => {
this.activeEditorListeners.push(activeCodeEditor.onDidChangeConfiguration((event: IConfigurationChangedEvent) => {
if (event.accessibilitySupport) {
this.onScreenReaderModeChange(control);
this.onScreenReaderModeChange(activeCodeEditor);
}
}));
// Hook Listener for Selection changes
this.activeEditorListeners.push(control.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => {
this.onSelectionChange(control);
this.activeEditorListeners.push(activeCodeEditor.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => {
this.onSelectionChange(activeCodeEditor);
}));
// Hook Listener for mode changes
this.activeEditorListeners.push(control.onDidChangeModelLanguage((event: IModelLanguageChangedEvent) => {
this.onModeChange(control);
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelLanguage((event: IModelLanguageChangedEvent) => {
this.onModeChange(activeCodeEditor);
}));
// Hook Listener for content changes
this.activeEditorListeners.push(control.onDidChangeModelContent((e) => {
this.onEOLChange(control);
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelContent((e) => {
this.onEOLChange(activeCodeEditor);
let selections = activeCodeEditor.getSelections();
for (let i = 0; i < e.changes.length; i++) {
if (selections.some(selection => Range.areIntersecting(selection, e.changes[i].range))) {
this.onSelectionChange(activeCodeEditor);
break;
}
}
}));
// Hook Listener for content options changes
this.activeEditorListeners.push(control.onDidChangeModelOptions((event: IModelOptionsChangedEvent) => {
this.onIndentationChange(control);
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelOptions((event: IModelOptionsChangedEvent) => {
this.onIndentationChange(activeCodeEditor);
}));
}
// Handle binary editors
else if (activeEditor instanceof BaseBinaryResourceEditor || activeEditor instanceof BinaryResourceDiffEditor) {
else if (activeControl instanceof BaseBinaryResourceEditor || activeControl instanceof BinaryResourceDiffEditor) {
const binaryEditors: BaseBinaryResourceEditor[] = [];
if (activeEditor instanceof BinaryResourceDiffEditor) {
const details = activeEditor.getDetailsEditor();
if (activeControl instanceof BinaryResourceDiffEditor) {
const details = activeControl.getDetailsEditor();
if (details instanceof BaseBinaryResourceEditor) {
binaryEditors.push(details);
}
const master = activeEditor.getMasterEditor();
const master = activeControl.getMasterEditor();
if (master instanceof BaseBinaryResourceEditor) {
binaryEditors.push(master);
}
} else {
binaryEditors.push(activeEditor);
binaryEditors.push(activeControl);
}
binaryEditors.forEach(editor => {
this.activeEditorListeners.push(editor.onMetadataChanged(metadata => {
this.onMetadataChange(activeEditor);
this.onMetadataChange(activeControl);
}));
});
}
@@ -742,7 +746,7 @@ export class EditorStatus implements IStatusbarItem {
this.updateState(info);
}
private onEncodingChange(e: IBaseEditor): void {
private onEncodingChange(e?: IBaseEditor): void {
if (e && !this.isActiveEditor(e)) {
return;
}
@@ -750,7 +754,7 @@ export class EditorStatus implements IStatusbarItem {
const info: StateDelta = { encoding: null };
// We only support text based editors
if (getEditorWidget(e)) {
if (e && (isCodeEditor(e.getControl()) || isDiffEditor(e.getControl()))) {
const encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(e.input);
if (encodingSupport) {
const rawEncoding = encodingSupport.getEncoding();
@@ -767,11 +771,11 @@ export class EditorStatus implements IStatusbarItem {
}
private onResourceEncodingChange(resource: uri): void {
const activeEditor = this.editorService.getActiveEditor();
if (activeEditor) {
const activeResource = toResource(activeEditor.input, { supportSideBySide: true });
const activeControl = this.editorService.activeControl;
if (activeControl) {
const activeResource = toResource(activeControl.input, { supportSideBySide: true });
if (activeResource && activeResource.toString() === resource.toString()) {
return this.onEncodingChange(<IBaseEditor>activeEditor); // only update if the encoding changed for the active resource
return this.onEncodingChange(<IBaseEditor>activeControl); // only update if the encoding changed for the active resource
}
}
}
@@ -782,10 +786,10 @@ export class EditorStatus implements IStatusbarItem {
this.updateState(info);
}
private isActiveEditor(e: IBaseEditor): boolean {
const activeEditor = this.editorService.getActiveEditor();
private isActiveEditor(control: IBaseEditor): boolean {
const activeControl = this.editorService.activeControl;
return activeEditor && e && activeEditor === e;
return activeControl && activeControl === control;
}
}
@@ -798,7 +802,7 @@ function isWritableCodeEditor(codeEditor: ICodeEditor): boolean {
}
function isWritableBaseEditor(e: IBaseEditor): boolean {
return isWritableCodeEditor(getEditorWidget(e));
return e && isWritableCodeEditor(getCodeEditor(e.getControl()));
}
export class ShowLanguageExtensionsAction extends Action {
@@ -822,15 +826,15 @@ export class ShowLanguageExtensionsAction extends Action {
export class ChangeModeAction extends Action {
public static readonly ID = 'workbench.action.editor.changeLanguageMode';
public static readonly LABEL = nls.localize('changeMode', "Change Language Mode");
static readonly ID = 'workbench.action.editor.changeLanguageMode';
static readonly LABEL = nls.localize('changeMode', "Change Language Mode");
constructor(
actionId: string,
actionLabel: string,
@IModeService private modeService: IModeService,
@IModelService private modelService: IModelService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorService private editorService: IEditorService,
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IPreferencesService private preferencesService: IPreferencesService,
@@ -840,15 +844,14 @@ export class ChangeModeAction extends Action {
super(actionId, actionLabel);
}
public run(): TPromise<any> {
let activeEditor = this.editorService.getActiveEditor();
const editorWidget = getEditorWidget(activeEditor);
if (!editorWidget) {
run(): TPromise<any> {
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
if (!activeTextEditorWidget) {
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
const textModel = editorWidget.getModel();
const resource = toResource(activeEditor.input, { supportSideBySide: true });
const textModel = activeTextEditorWidget.getModel();
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
let hasLanguageSupport = !!resource;
if (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
@@ -946,17 +949,16 @@ export class ChangeModeAction extends Action {
}
// Change mode for active editor
activeEditor = this.editorService.getActiveEditor();
const codeOrDiffEditor = getCodeOrDiffEditor(activeEditor);
const activeEditor = this.editorService.activeControl;
const activeTextEditorWidget = this.editorService.activeTextEditorWidget;
const models: ITextModel[] = [];
if (codeOrDiffEditor.codeEditor) {
const codeEditorModel = codeOrDiffEditor.codeEditor.getModel();
if (isCodeEditor(activeTextEditorWidget)) {
const codeEditorModel = activeTextEditorWidget.getModel();
if (codeEditorModel) {
models.push(codeEditorModel);
}
}
if (codeOrDiffEditor.diffEditor) {
const diffEditorModel = codeOrDiffEditor.diffEditor.getModel();
} else if (isDiffEditor(activeTextEditorWidget)) {
const diffEditorModel = activeTextEditorWidget.getModel();
if (diffEditorModel) {
if (diffEditorModel.original) {
models.push(diffEditorModel.original);
@@ -1045,42 +1047,42 @@ export interface IChangeEOLEntry extends IPickOpenEntry {
class ChangeIndentationAction extends Action {
public static readonly ID = 'workbench.action.editor.changeIndentation';
public static readonly LABEL = nls.localize('changeIndentation', "Change Indentation");
static readonly ID = 'workbench.action.editor.changeIndentation';
static readonly LABEL = nls.localize('changeIndentation', "Change Indentation");
constructor(
actionId: string,
actionLabel: string,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorService private editorService: IEditorService,
@IQuickOpenService private quickOpenService: IQuickOpenService
) {
super(actionId, actionLabel);
}
public run(): TPromise<any> {
const activeEditor = this.editorService.getActiveEditor();
const control = getEditorWidget(activeEditor);
if (!control) {
run(): TPromise<any> {
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
if (!activeTextEditorWidget) {
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
if (!isWritableCodeEditor(control)) {
if (!isWritableCodeEditor(activeTextEditorWidget)) {
return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
}
const picks = [
control.getAction(IndentUsingSpaces.ID),
control.getAction(IndentUsingTabs.ID),
control.getAction(DetectIndentation.ID),
control.getAction(IndentationToSpacesAction.ID),
control.getAction(IndentationToTabsAction.ID),
control.getAction(TrimTrailingWhitespaceAction.ID)
activeTextEditorWidget.getAction(IndentUsingSpaces.ID),
activeTextEditorWidget.getAction(IndentUsingTabs.ID),
activeTextEditorWidget.getAction(DetectIndentation.ID),
activeTextEditorWidget.getAction(IndentationToSpacesAction.ID),
activeTextEditorWidget.getAction(IndentationToTabsAction.ID),
activeTextEditorWidget.getAction(TrimTrailingWhitespaceAction.ID)
].map((a: IEditorAction) => {
return {
id: a.id,
label: a.label,
detail: (language === LANGUAGE_DEFAULT) ? null : a.alias,
run: () => {
control.focus();
activeTextEditorWidget.focus();
a.run();
}
};
@@ -1095,30 +1097,29 @@ class ChangeIndentationAction extends Action {
export class ChangeEOLAction extends Action {
public static readonly ID = 'workbench.action.editor.changeEOL';
public static readonly LABEL = nls.localize('changeEndOfLine', "Change End of Line Sequence");
static readonly ID = 'workbench.action.editor.changeEOL';
static readonly LABEL = nls.localize('changeEndOfLine', "Change End of Line Sequence");
constructor(
actionId: string,
actionLabel: string,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorService private editorService: IEditorService,
@IQuickOpenService private quickOpenService: IQuickOpenService
) {
super(actionId, actionLabel);
}
public run(): TPromise<any> {
let activeEditor = this.editorService.getActiveEditor();
const editorWidget = getEditorWidget(activeEditor);
if (!editorWidget) {
run(): TPromise<any> {
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
if (!activeTextEditorWidget) {
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
if (!isWritableCodeEditor(editorWidget)) {
if (!isWritableCodeEditor(activeTextEditorWidget)) {
return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
}
const textModel = editorWidget.getModel();
const textModel = activeTextEditorWidget.getModel();
const EOLOptions: IChangeEOLEntry[] = [
{ label: nlsEOLLF, eol: EndOfLineSequence.LF },
@@ -1129,11 +1130,10 @@ export class ChangeEOLAction extends Action {
return this.quickOpenService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), autoFocus: { autoFocusIndex: selectedIndex } }).then(eol => {
if (eol) {
activeEditor = this.editorService.getActiveEditor();
const editorWidget = getEditorWidget(activeEditor);
if (editorWidget && isWritableCodeEditor(editorWidget)) {
const textModel = editorWidget.getModel();
textModel.setEOL(eol.eol);
const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget);
if (activeCodeEditor && isWritableCodeEditor(activeCodeEditor)) {
const textModel = activeCodeEditor.getModel();
textModel.pushEOL(eol.eol);
}
}
});
@@ -1142,13 +1142,13 @@ export class ChangeEOLAction extends Action {
export class ChangeEncodingAction extends Action {
public static readonly ID = 'workbench.action.editor.changeEncoding';
public static readonly LABEL = nls.localize('changeEncoding', "Change File Encoding");
static readonly ID = 'workbench.action.editor.changeEncoding';
static readonly LABEL = nls.localize('changeEncoding', "Change File Encoding");
constructor(
actionId: string,
actionLabel: string,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorService private editorService: IEditorService,
@IQuickOpenService private quickOpenService: IQuickOpenService,
@ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService,
@IFileService private fileService: IFileService
@@ -1156,13 +1156,13 @@ export class ChangeEncodingAction extends Action {
super(actionId, actionLabel);
}
public run(): TPromise<any> {
let activeEditor = this.editorService.getActiveEditor();
if (!getEditorWidget(activeEditor) || !activeEditor.input) {
run(): TPromise<any> {
if (!getCodeEditor(this.editorService.activeTextEditorWidget)) {
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
let encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(activeEditor.input);
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") }]);
}
@@ -1181,7 +1181,7 @@ export class ChangeEncodingAction extends Action {
if (encodingSupport instanceof UntitledEditorInput) {
pickActionPromise = TPromise.as(saveWithEncodingPick);
} else if (!isWritableBaseEditor(activeEditor)) {
} else if (!isWritableBaseEditor(activeControl)) {
pickActionPromise = TPromise.as(reopenWithEncodingPick);
} else {
pickActionPromise = this.quickOpenService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
@@ -1192,7 +1192,7 @@ export class ChangeEncodingAction extends Action {
return void 0;
}
const resource = toResource(activeEditor.input, { supportSideBySide: true });
const resource = toResource(activeControl.input, { supportSideBySide: true });
return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */)
.then(() => {
@@ -1249,8 +1249,8 @@ export class ChangeEncodingAction extends Action {
autoFocus: { autoFocusIndex: typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : void 0 }
}).then(encoding => {
if (encoding) {
activeEditor = this.editorService.getActiveEditor();
encodingSupport = toEditorWithEncodingSupport(activeEditor.input);
activeControl = this.editorService.activeControl;
encodingSupport = toEditorWithEncodingSupport(activeControl.input);
if (encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
}
@@ -1274,7 +1274,7 @@ class ScreenReaderDetectedExplanation extends Themable {
super(themeService);
}
public get visible(): boolean {
get visible(): boolean {
return this._visible;
}
@@ -1294,7 +1294,7 @@ class ScreenReaderDetectedExplanation extends Themable {
}
}
public show(anchorElement: HTMLElement): void {
show(anchorElement: HTMLElement): void {
this._visible = true;
this.contextViewService.showContextView({
@@ -1318,7 +1318,7 @@ class ScreenReaderDetectedExplanation extends Themable {
});
}
public hide(): void {
hide(): void {
this.contextViewService.hideContextView();
}

View File

@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control.hidden {
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;
}
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .hint-more,
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .hint-more {
text-decoration-line: underline;
}
/* todo@joh move somewhere else */
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree>.input {
padding: 5px 9px;
position: relative;
box-sizing: border-box;
height: 36px;
}
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree>.tree {
height: calc(100% - 36px);
}
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree.inactive>.input {
display: none;
}
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree.inactive>.tree {
height: 100%;
}
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree .monaco-highlighted-label .highlight{
font-weight: bold;
}

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#424242"/></svg>

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#C5C5C5"/></svg>

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#C5C5C5"/></svg>

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#424242"/></svg>

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#424242" cx="8" cy="8" r="4"/></svg>

After

Width:  |  Height:  |  Size: 167 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#C5C5C5" cx="8" cy="8" r="4"/></svg>

After

Width:  |  Height:  |  Size: 167 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#e8e8e8" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.428 8L12 10.573 10.572 12 8 9.428 5.428 12 4 10.573 6.572 8 4 5.428 5.427 4 8 6.572 10.573 4 12 5.428 9.428 8z" fill="#E8E8E8"/></svg>

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 241 B

View File

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

View File

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

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.428 8L12 10.573 10.572 12 8 9.428 5.428 12 4 10.573 6.572 8 4 5.428 5.427 4 8 6.572 10.573 4 12 5.428 9.428 8z" fill="#424242"/></svg>

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 241 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><path fill="#1E1E1E" d="M16 12h-2v2h-2v2H0V5h2V3h2V1h12v11z"/><g fill="#C5C5C5"><path d="M3 5h9v8h1V4H3zM5 2v1h9v8h1V2zM1 6v9h10V6H1zm8 7H7.5L6 11.5 4.5 13H3l2.3-2.3L3 8.5h1.5L6 10l1.5-1.5H9l-2.3 2.3L9 13z"/></g></svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><path fill="#F6F6F6" d="M16 12h-2v2h-2v2H0V5h2V3h2V1h12v11z"/><g fill="#424242"><path d="M3 5h9v8h1V4H3zM5 2v1h9v8h1V2zM1 6v9h10V6H1zm8 7H7.5L6 11.5 4.5 13H3l2.3-2.3L3 8.5h1.5L6 10l1.5-1.5H9l-2.3 2.3L9 13z"/></g></svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -1,92 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench > .editor > .content.dragging > .monaco-sash {
display: none; /* hide sashes while dragging editors around */
}
.monaco-workbench > .editor .one-editor-silo.centered .editor-container {
border-style: none solid;
border-width: 0px 1px;
}
#monaco-workbench-editor-move-overlay,
#monaco-workbench-editor-drop-overlay {
position: absolute;
left: 0;
width: 100%;
z-index: 10000;
}
#monaco-workbench-editor-drop-overlay {
opacity: 0; /* initially not visible until moving around */
}
.monaco-workbench > .editor > .content > .one-editor-silo {
position: absolute;
box-sizing: border-box; /* use border box to be able to draw a border as separator between editors */
}
.monaco-workbench > .editor > .content > .one-editor-silo.editor-one {
left: 0;
top: 0;
}
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three {
right: 0;
}
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three {
bottom: 0;
}
.monaco-workbench > .editor > .content > .one-editor-silo.dragging {
z-index: 70;
box-sizing: content-box;
}
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.dragging {
border-left: 1px solid;
border-right: 1px solid;
}
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.dragging {
border-top: 1px solid;
border-bottom: 1px solid;
}
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-two,
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three {
border-left: 1px solid;
}
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-two,
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three {
border-top: 1px solid;
}
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.draggedunder {
transition: left 200ms ease-out;
}
.monaco-workbench > .editor > .content.vertical-layout > .editor-three.draggedunder {
transition-property: right;
}
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.draggedunder {
transition: top 200ms ease-out;
}
.monaco-workbench > .editor > .content.horizontal-layout > .editor-three.draggedunder {
transition-property: bottom;
}
.monaco-workbench > .editor > .content > .one-editor-silo > .container {
height: 100%;
}
.monaco-workbench > .editor > .content > .one-editor-silo > .container > .editor-container {
height: calc(100% - 35px); /* Editor is below editor title */
}

View File

@@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#monaco-workbench-editor-drop-overlay {
position: absolute;
z-index: 10000;
width: 100%;
height: 100%;
left: 0;
}
#monaco-workbench-editor-drop-overlay > .editor-group-overlay-indicator {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* very important to not take events away from the parent */
opacity: 0; /* hidden initially */
transition: opacity 150ms ease-out;
}
#monaco-workbench-editor-drop-overlay > .editor-group-overlay-indicator.overlay-move-transition {
transition: top 70ms ease-out, left 70ms ease-out, width 70ms ease-out, height 70ms ease-out, opacity 150ms ease-out;
}

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* Container */
.monaco-workbench > .part.editor > .content .editor-group-container {
height: 100%;
}
.monaco-workbench > .part.editor > .content .editor-group-container.empty {
opacity: 0.5; /* dimmed to indicate inactive state */
}
.monaco-workbench > .part.editor > .content .editor-group-container.empty.active,
.monaco-workbench > .part.editor > .content .editor-group-container.empty.dragged-over {
opacity: 1; /* indicate active/dragged-over group through undimmed state */
}
/* Letterpress */
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-letterpress {
display: none; /* only visible when empty */
}
.monaco-workbench > .part.editor > .content .editor-group-container.empty > .editor-group-letterpress {
display: block;
margin: auto;
width: 100%;
height: calc(100% - 70px); /* centered below toolbar */
max-width: 260px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 70% 70%;
}
.monaco-workbench > .part.editor > .content.empty .editor-group-container.empty > .editor-group-letterpress {
background-size: 100% 100%; /* larger for empty editor part */
height: 100%; /* no toolbar in this case */
}
/* Title */
.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.title-border-bottom::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
z-index: 5;
pointer-events: none;
background-color: var(--title-border-bottom-color);
width: 100%;
height: 1px;
}
.monaco-workbench > .part.editor > .content .editor-group-container.empty > .title {
display: none;
}
/* Toolbar */
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar {
display: none;
}
.monaco-workbench > .part.editor > .content:not(.empty) .editor-group-container.empty > .editor-group-container-toolbar {
display: block;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .action-label {
display: block;
height: 35px;
line-height: 35px;
min-width: 28px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
}
.vs .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group {
background-image: url('close-big.svg');
}
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group,
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group {
background-image: url('close-big-inverse.svg');
}
/* Editor */
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-container {
height: calc(100% - 35px); /* below title control */
}
.monaco-workbench > .part.editor > .content .editor-group-container.empty > .editor-container {
display: none;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-container > .editor-instance {
height: 100%;
}
.monaco-workbench > .part.editor > .content .grid-view-container {
width: 100%;
height: 100%;
}

View File

@@ -1,11 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/** Letter press styling for empty editor */
.monaco-workbench > .part.editor.empty {
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 260px 260px;
}

View File

@@ -70,10 +70,10 @@
}
.monaco-shell.vs .screen-reader-detected-explanation .cancel {
background: url('close-big.svg') center center no-repeat;
background: url('close-statusview.svg') center center no-repeat;
}
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel,
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
background: url('close-big-dark.svg') center center no-repeat;
background: url('close-statusview-inverse.svg') center center no-repeat;
}

View File

@@ -1,47 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* Title Label */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label {
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
padding-left: 20px;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .monaco-icon-label::before {
height: 35px; /* tweak the icon size of the editor labels when icons are enabled */
}
/* Title Actions */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions {
display: flex;
flex: initial;
opacity: 0.5;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .title-actions {
opacity: 1;
}
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action {
background: url('close-dirty.svg') center center no-repeat;
}
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action,
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action {
background: url('close-dirty-inverse.svg') center center no-repeat;
}
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover {
background: url('close.svg') center center no-repeat;
}
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover,
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover {
background: url('close-inverse.svg') center center no-repeat;
}

View File

@@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench > .part.editor > .content .editor-group-container > .title > .label-container {
display: flex;
justify-content: flex-start;
align-items: center;
overflow: hidden;
flex: auto;
}
/* Title Label */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label {
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
padding-left: 20px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .no-tabs.title-label {
flex: none;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-icon-label::before {
height: 35px; /* tweak the icon size of the editor labels when icons are enabled */
}
/* Breadcrumbs */
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control {
flex: 1 50%;
overflow: hidden;
padding: 0 6px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item {
font-size: 0.9em;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item {
font-style: italic;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
content: '/';
opacity: 1;
height: inherit;
width: inherit;
background-image: none;
}
/* {{SQL CARBON EDIT}} */
.monaco-workbench.windows > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
content: '/';
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before,
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before,
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before {
/* workspace folder, item following workspace folder, or relative path -> hide first seperator */
display: none;
}
.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;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child {
padding-right: 4px; /* does not have trailing separator*/
}
/* Title Actions */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions {
display: flex;
flex: initial;
opacity: 0.5;
height: 35px;
}
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .title-actions {
opacity: 1;
}

View File

Before

Width:  |  Height:  |  Size: 552 B

After

Width:  |  Height:  |  Size: 552 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 15H0V1h16v14z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M1 2v12h14V2H1zm13 11H2v-3h12v3zm0-5H2V5h12v3z" id="iconBg"/><g id="iconFg" style="display: none;"><path class="icon-vs-fg" d="M14 8H2V5h12v3zm0 2H2v3h12v-3z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>SplitScreenVertical_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="M16,1V15H0V1Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M1,2V14H15V2ZM7,13H2V5H7Zm7,0H9V5h5Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 578 B

After

Width:  |  Height:  |  Size: 519 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 15H0V1h16v14z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M1 2v12h14V2H1zm13 11H2v-3h12v3zm0-5H2V5h12v3z" id="iconBg"/><g id="iconFg" style="display: none;"><path class="icon-vs-fg" d="M14 8H2V5h12v3zm0 2H2v3h12v-3z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>SplitScreenVertical_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="M16,1V15H0V1Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M1,2V14H15V2ZM7,13H2V5H7Zm7,0H9V5h5Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 578 B

After

Width:  |  Height:  |  Size: 519 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 -1 16 16" enable-background="new 0 -1 16 16"><path fill="#C5C5C5" d="M1 1v12h14v-12h-14zm1 3h4.999v8h-4.999v-8zm12 8h-5.001v-8h5.001v8z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>SplitScreenHorizontal_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="M16,1V15H0V1Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M1,2V14H15V2ZM14,13H2V10H14Zm0-5H2V5H14Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 525 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 -1 16 16" enable-background="new 0 -1 16 16"><path fill="#656565" d="M1 1v12h14v-12h-14zm1 3h4.999v8h-4.999v-8zm12 8h-5.001v-8h5.001v8z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>SplitScreenHorizontal_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="M16,1V15H0V1Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M1,2V14H15V2ZM14,13H2V10H14Zm0-5H2V5H14Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 525 B

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
<style type="text/css">
.st0{fill:#C5C5C5;}
</style>
<g id="outline">
</g>
<g id="icon_x5F_bg">
<path class="st0" d="M7.6,5H3c0,0-1,0-1,1c0,0.8,0,5.4,0,8c0,1,1,1,1,1s1.5,0,3,0s3,0,3,0s1,0,1-1c0-2.6,0-6.9,0-6.9L7.6,5z M9,14
H3V6h4v2h2V14z"/>
<path class="st0" d="M9.6,3H5c0,0-1,0-1,1h5v0.9L10.2,6H11v7c1,0,1-1,1-1V5.1L9.6,3z"/>
<path class="st0" d="M11.6,1H7c0,0-1,0-1,1h5v0.9L12.2,4H13v7c1,0,1-1,1-1V3.1L11.6,1z"/>
</g>
<g id="color_x5F_action">
</g>
<g id="icon_x5F_fg">
</g>
</svg>

Before

Width:  |  Height:  |  Size: 822 B

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
<style type="text/css">
.st0{fill:#656565;}
</style>
<g id="outline">
</g>
<g id="icon_x5F_bg">
<path class="st0" d="M7.6,5H3c0,0-1,0-1,1c0,0.8,0,5.4,0,8c0,1,1,1,1,1s1.5,0,3,0s3,0,3,0s1,0,1-1c0-2.6,0-6.9,0-6.9L7.6,5z M9,14
H3V6h4v2h2V14z"/>
<path class="st0" d="M9.6,3H5c0,0-1,0-1,1h5v0.9L10.2,6H11v7c1,0,1-1,1-1V5.1L9.6,3z"/>
<path class="st0" d="M11.6,1H7c0,0-1,0-1,1h5v0.9L12.2,4H13v7c1,0,1-1,1-1V3.1L11.6,1z"/>
</g>
<g id="color_x5F_action">
</g>
<g id="icon_x5F_fg">
</g>
</svg>

Before

Width:  |  Height:  |  Size: 822 B

View File

@@ -1,228 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* Title Container */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.empty {
background: inherit !important; /* prevents some ugly flickering when opening first tab */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs > .monaco-scrollable-element {
flex: 1;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs > .monaco-scrollable-element .scrollbar {
z-index: 3; /* on top of tabs */
cursor: default;
}
/* Tabs Container */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container {
display: flex;
height: 35px;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.scroll {
overflow: scroll !important;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container::-webkit-scrollbar {
display: none;
}
/* Tab */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab {
display: flex;
white-space: nowrap;
cursor: pointer;
height: 35px;
box-sizing: border-box;
border: 1px solid transparent;
padding-left: 10px;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-right,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-off {
padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab close button is not left */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-fit {
width: 120px;
min-width: fit-content;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink {
min-width: 60px;
flex-basis: 0; /* all tabs are even */
flex-grow: 1; /* all tabs grow even */
max-width: fit-content;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after {
content: "";
display: flex;
flex: 0;
width: 5px; /* Reserve space to hide tab fade when close button is left or off (fixes https://github.com/Microsoft/vscode/issues/45728) */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-left {
min-width: 80px; /* make more room for close button when it shows to the left */
padding-right: 5px; /* we need less room when sizing is shrink */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged {
will-change: transform; /* forces tab to be drawn on a separate layer (fixes https://github.com/Microsoft/vscode/issues/18733) */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged-over div {
pointer-events: none; /* prevents cursor flickering (fixes https://github.com/Microsoft/vscode/issues/38753) */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-left {
flex-direction: row-reverse;
padding-left: 0;
padding-right: 10px;
}
/* Tab Label */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
margin-top: auto;
margin-bottom: auto;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink .tab-label {
position: relative;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .tab-label::after {
content: "";
position: absolute;
right: 0;
height: 100%;
width: 5px;
opacity: 1;
padding: 0;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-description-container {
overflow: visible; /* fixes https://github.com/Microsoft/vscode/issues/20182 */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
text-overflow: clip;
}
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
text-overflow: ellipsis;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before {
height: 16px; /* tweak the icon size of the editor labels when icons are enabled */
}
/* Tab Close */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab > .tab-close {
margin-top: auto;
margin-bottom: auto;
width: 28px;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close {
flex: 0;
overflow: hidden; /* let the close button be pushed out of view when sizing is set to shrink to make more room... */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty.close-button-right.sizing-shrink > .tab-close,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .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 > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off > .tab-close {
display: none; /* hide the close action bar when we are configured to hide it */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active:hover > .tab-close .action-label, /* always show it on hover */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.dirty > .tab-close .action-label { /* always show it for dirty tabs */
opacity: 1;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active > .tab-close .action-label, /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty > .tab-close .action-label, /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:hover > .tab-close .action-label { /* show dimmed for inactive group */
opacity: 0.5;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab > .tab-close .action-label {
opacity: 0;
display: block;
height: 16px;
width: 16px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
margin-right: 0.5em;
}
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action {
background: url('close-dirty.svg') center center no-repeat;
}
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action,
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action {
background: url('close-dirty-inverse.svg') center center no-repeat;
}
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
background: url('close.svg') center center no-repeat;
}
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover,
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
background: url('close-inverse.svg') center center no-repeat;
}
/* No Tab Close Button */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off {
padding-right: 10px; /* give a little bit more room if close button is off */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-off {
padding-right: 5px; /* we need less room when sizing is shrink */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty {
background-repeat: no-repeat;
background-position-y: center;
background-position-x: calc(100% - 6px); /* to the right of the tab label */
padding-right: 28px; /* make room for dirty indication when we are running without close button */
}
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty {
background-image: url('close-dirty.svg');
}
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty,
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty {
background-image: url('close-dirty-inverse.svg');
}
/* Editor Actions */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions {
cursor: default;
flex: initial;
padding-left: 4px;
}

View File

@@ -0,0 +1,281 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* Title Container */
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .monaco-scrollable-element {
flex: 1;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .monaco-scrollable-element .scrollbar {
z-index: 3; /* on top of tabs */
cursor: default;
}
/* Tabs Container */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container {
display: flex;
height: 35px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container.scroll {
overflow: scroll !important;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container::-webkit-scrollbar {
display: none;
}
/* Tab */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab {
position: relative;
display: flex;
white-space: nowrap;
cursor: pointer;
height: 35px;
box-sizing: border-box;
padding-left: 10px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-right,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-off {
padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab close button is not left */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit {
width: 120px;
min-width: fit-content;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink {
min-width: 60px;
flex-basis: 0; /* all tabs are even */
flex-grow: 1; /* all tabs grow even */
max-width: fit-content;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after {
content: '';
display: flex;
flex: 0;
width: 5px; /* Reserve space to hide tab fade when close button is left or off (fixes https://github.com/Microsoft/vscode/issues/45728) */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left {
min-width: 80px; /* make more room for close button when it shows to the left */
padding-right: 5px; /* we need less room when sizing is shrink */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged {
will-change: transform; /* forces tab to be drawn on a separate layer (fixes https://github.com/Microsoft/vscode/issues/18733) */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged-over div {
pointer-events: none; /* prevents cursor flickering (fixes https://github.com/Microsoft/vscode/issues/38753) */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-left {
flex-direction: row-reverse;
padding-left: 0;
padding-right: 10px;
}
/* Tab border top/bottom */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-top-container,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-bottom-container {
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 {
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%;
height: 1px;
}
.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;
}
/* Tab Label */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label {
margin-top: auto;
margin-bottom: auto;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label {
position: relative;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label::after {
content: '';
position: absolute;
right: 0;
height: 100%;
width: 5px;
opacity: 1;
padding: 0;
}
.monaco-workbench > .part.editor > .content .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 */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
text-overflow: clip;
}
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
text-overflow: ellipsis;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before {
height: 16px; /* tweak the icon size of the editor labels when icons are enabled */
}
/* Tab Close */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close {
margin-top: auto;
margin-bottom: auto;
width: 28px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close {
flex: 0;
overflow: hidden; /* let the close button be pushed out of view when sizing is set to shrink to make more room... */
}
.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-off > .tab-close {
display: none; /* hide the close action bar when we are configured to hide it */
}
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* always show it on hover */
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-close .action-label { /* always show it for dirty tabs */
opacity: 1;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-close .action-label, /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label, /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-close .action-label { /* show dimmed for inactive group */
opacity: 0.5;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close .action-label {
opacity: 0;
display: block;
height: 16px;
width: 16px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
margin-right: 0.5em;
}
.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action {
background: url('close-dirty.svg') center center no-repeat;
}
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action,
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action {
background: url('close-dirty-inverse.svg') center center no-repeat;
}
.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
background: url('close.svg') center center no-repeat;
}
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover,
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
background: url('close-inverse.svg') center center no-repeat;
}
/* No Tab Close Button */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off {
padding-right: 10px; /* give a little bit more room if close button is off */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off {
padding-right: 5px; /* we need less room when sizing is shrink */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty {
background-repeat: no-repeat;
background-position-y: center;
background-position-x: calc(100% - 6px); /* to the right of the tab label */
padding-right: 28px; /* make room for dirty indication when we are running without close button */
}
.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty {
background-image: url('close-dirty.svg');
}
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty,
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty {
background-image: url('close-dirty-inverse.svg');
}
/* Editor Actions */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions {
cursor: default;
flex: initial;
padding-left: 4px;
height: 35px;
}
/* Breadcrumbs */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control {
flex: 1 100%;
height: 25px;
cursor: default;
}
.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 */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item {
max-width: 260px;
}
.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;
} */

View File

@@ -23,12 +23,12 @@
.vs .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace {
opacity: 1;
background: url('Paragraph_16x_nohalo.svg') center center no-repeat;
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_16x_nohalo_inversep.svg') center center no-repeat;
background: url('paragraph-inverse.svg') center center no-repeat;
}
.vs .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace.is-checked {

View File

@@ -5,36 +5,31 @@
/* Editor Label */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label {
white-space: nowrap;
flex: 1;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo.centered > .container > .title .title-label {
flex-direction: row;
justify-content: center;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a {
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a {
text-decoration: none;
font-size: 13px;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .monaco-icon-label::before,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label span,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label span {
.monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-icon-label::before,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label h2,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label span {
cursor: pointer;
}
/* Title Actions */
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label {
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label {
display: block;
height: 35px;
line-height: 35px;
@@ -44,69 +39,38 @@
background-repeat: no-repeat;
}
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label,
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label {
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label,
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label {
line-height: initial;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label .label,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label .label {
.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label .label,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label .label {
display: none;
}
/* Drag Cursor */
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title.tabs .scrollbar .slider,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title .monaco-icon-label::before,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title .title-label a,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title .title-label span {
.monaco-workbench > .part.editor > .content .editor-group-container > .title {
cursor: -webkit-grab;
}
#monaco-workbench-editor-move-overlay,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title.tabs .scrollbar .slider,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title .monaco-icon-label::before,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title .title-label a,
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title .title-label span {
cursor: -webkit-grabbing;
}
/* Actions */
.monaco-workbench .close-editor-action {
.monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action {
background: url('close.svg') center center no-repeat;
}
.vs-dark .monaco-workbench .close-editor-action,
.hc-black .monaco-workbench .close-editor-action {
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action,
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action {
background: url('close-inverse.svg') center center no-repeat;
}
.monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action {
background: url('split-editor-vertical.svg') center center no-repeat;
}
/* Drag and Drop Feedback */
.vs-dark .monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action,
.hc-black .monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action {
background: url('split-editor-vertical-inverse.svg') center center no-repeat;
}
.monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action {
background: url('split-editor-horizontal.svg') center center no-repeat;
}
.vs-dark .monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action,
.hc-black .monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action {
background: url('split-editor-horizontal-inverse.svg') center center no-repeat;
}
.monaco-workbench .show-group-editors-action {
background: url('stackview.svg') center center no-repeat;
}
.vs-dark .monaco-workbench .show-group-editors-action,
.hc-black .monaco-workbench .show-group-editors-action {
background: url('stackview-inverse.svg') center center no-repeat;
}
.monaco-editor-group-drag-image {
display: inline-block;
padding: 1px 7px;
border-radius: 10px;
font-size: 12px;
position: absolute;
}

View File

@@ -5,147 +5,222 @@
'use strict';
import 'vs/css!./media/notabstitle';
import * as errors from 'vs/base/common/errors';
import { toResource } from 'vs/workbench/common/editor';
import * as DOM from 'vs/base/browser/dom';
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import 'vs/css!./media/notabstitlecontrol';
import { toResource, Verbosity, IEditorInput } from 'vs/workbench/common/editor';
import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { Verbosity } from 'vs/platform/editor/common/editor';
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom';
import { IEditorPartOptions, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
import { IAction } from 'vs/base/common/actions';
import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
export class NoTabsTitleControl extends TitleControl {
private titleContainer: HTMLElement;
private editorLabel: ResourceLabel;
private lastRenderedActiveEditor: IEditorInput;
public create(parent: HTMLElement): void {
super.create(parent);
protected create(parent: HTMLElement): void {
this.titleContainer = parent;
this.titleContainer.draggable = true;
//Container listeners
this.registerContainerListeners();
// Gesture Support
Gesture.addTarget(this.titleContainer);
// Pin on double click
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
// Detect mouse click
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CLICK, (e: MouseEvent) => this.onTitleClick(e)));
// Detect touch
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
const labelContainer = document.createElement('div');
addClass(labelContainer, 'label-container');
this.titleContainer.appendChild(labelContainer);
// Editor Label
this.editorLabel = this.instantiationService.createInstance(ResourceLabel, this.titleContainer, void 0);
this.toUnbind.push(this.editorLabel);
this.toUnbind.push(this.editorLabel.onClick(e => this.onTitleLabelClick(e)));
this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0));
this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e)));
// Breadcrumbs
this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, extraClasses: ['no-tabs-breadcrumbs'] });
// Right Actions Container
const actionsContainer = document.createElement('div');
DOM.addClass(actionsContainer, 'title-actions');
addClass(actionsContainer, 'title-actions');
this.titleContainer.appendChild(actionsContainer);
// Editor actions toolbar
this.createEditorActionsToolBar(actionsContainer);
}
private registerContainerListeners(): void {
// Group dragging
this.enableGroupDragging(this.titleContainer);
// Pin on double click
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)));
// Detect touch
this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
// Context Menu
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu({ group: this.context, editor: this.context.activeEditor }, e, this.titleContainer)));
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu({ group: this.context, editor: this.context.activeEditor }, e, this.titleContainer)));
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
}
private onTitleLabelClick(e: MouseEvent): void {
DOM.EventHelper.stop(e, false);
if (!this.dragged) {
setTimeout(() => this.quickOpenService.show()); // delayed to let the onTitleClick() come first which can cause a focus change which can close quick open
}
EventHelper.stop(e, false);
// delayed to let the onTitleClick() come first which can cause a focus change which can close quick open
setTimeout(() => this.quickOpenService.show());
}
private onTitleDoubleClick(e: MouseEvent): void {
DOM.EventHelper.stop(e);
if (!this.context) {
return;
}
EventHelper.stop(e);
const group = this.context;
this.editorGroupService.pinEditor(group, group.activeEditor);
this.group.pinEditor();
}
private onTitleClick(e: MouseEvent | GestureEvent): void {
if (!this.context) {
return;
}
const group = this.context;
// Close editor on middle mouse click
if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) {
this.closeOneEditorAction.run({ groupId: group.id, editorIndex: group.indexOf(group.activeEditor) }).done(null, errors.onUnexpectedError);
}
// Focus editor group unless:
// - click on toolbar: should trigger actions within
// - mouse click: do not focus group if there are more than one as it otherwise makes group DND funky
// - touch: always focus
else if ((this.stacks.groups.length === 1 || !(e instanceof MouseEvent)) && !DOM.isAncestor(((e as GestureEvent).initialTarget || e.target || e.srcElement) as HTMLElement, this.editorActionsToolbar.getContainer())) {
this.editorGroupService.focusGroup(group);
this.group.closeEditor(this.group.activeEditor);
}
}
protected doRefresh(): void {
const group = this.context;
const editor = group && group.activeEditor;
getPreferredHeight(): number {
return EDITOR_TITLE_HEIGHT;
}
openEditor(editor: IEditorInput): void {
this.ifActiveEditorChanged(() => this.redraw());
}
closeEditor(editor: IEditorInput): void {
this.ifActiveEditorChanged(() => this.redraw());
}
closeEditors(editors: IEditorInput[]): void {
this.ifActiveEditorChanged(() => this.redraw());
}
closeAllEditors(): void {
this.redraw();
}
moveEditor(editor: IEditorInput, fromIndex: number, targetIndex: number): void {
this.ifActiveEditorChanged(() => this.redraw());
}
pinEditor(editor: IEditorInput): void {
this.ifEditorIsActive(editor, () => this.redraw());
}
setActive(isActive: boolean): void {
this.redraw();
}
updateEditorLabel(editor: IEditorInput): void {
this.ifEditorIsActive(editor, () => this.redraw());
}
updateEditorDirty(editor: IEditorInput): void {
this.ifEditorIsActive(editor, () => {
if (editor.isDirty()) {
addClass(this.titleContainer, 'dirty');
} else {
removeClass(this.titleContainer, 'dirty');
}
});
}
updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void {
if (oldOptions.labelFormat !== newOptions.labelFormat) {
this.redraw();
}
}
updateStyles(): void {
this.redraw();
}
private ifActiveEditorChanged(fn: () => void): void {
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
) {
fn();
}
}
private ifEditorIsActive(editor: IEditorInput, fn: () => void): void {
if (this.group.isActive(editor)) {
fn(); // only run if editor is current active
}
}
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;
// Update Breadcrumbs
if (this.breadcrumbsControl) {
if (isGroupActive) {
this.breadcrumbsControl.update();
toggleClass(this.breadcrumbsControl.domNode, 'preview', !isEditorPinned);
} else {
this.breadcrumbsControl.hide();
}
}
// Clear if there is no editor
if (!editor) {
removeClass(this.titleContainer, 'dirty');
this.editorLabel.clear();
this.clearEditorActionsToolbar();
return; // return early if we are being closed
}
const isPinned = group.isPinned(group.activeEditor);
const isActive = this.stacks.isActive(group);
// Otherwise render it
else {
// Dirty state
this.updateEditorDirty(editor);
// Activity state
if (isActive) {
DOM.addClass(this.titleContainer, 'active');
} else {
DOM.removeClass(this.titleContainer, 'active');
// Editor Label
const resource = toResource(editor, { supportSideBySide: true });
const name = editor.getName() || '';
const { labelFormat } = this.accessor.partOptions;
let description: string;
if (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden()) {
description = ''; // hide description when showing breadcrumbs
} else if (labelFormat === 'default' && !isGroupActive) {
description = ''; // hide description when group is not active and style is 'default'
} else {
description = editor.getDescription(this.getVerbosity(labelFormat)) || '';
}
let title = editor.getTitle(Verbosity.LONG);
if (description === title) {
title = ''; // dont repeat what is already shown
}
this.editorLabel.setLabel({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
if (isGroupActive) {
this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
} else {
this.editorLabel.element.style.color = this.getColor(TAB_UNFOCUSED_ACTIVE_FOREGROUND);
}
// Update Editor Actions Toolbar
this.updateEditorActionsToolbar();
}
// Dirty state
if (editor.isDirty()) {
DOM.addClass(this.titleContainer, 'dirty');
} else {
DOM.removeClass(this.titleContainer, 'dirty');
}
// Editor Label
const resource = toResource(editor, { supportSideBySide: true });
const name = editor.getName() || '';
const labelFormat = this.editorGroupService.getTabOptions().labelFormat;
let description: string;
if (labelFormat === 'default' && !isActive) {
description = ''; // hide description when group is not active and style is 'default'
} else {
description = editor.getDescription(this.getVerbosity(labelFormat)) || '';
}
let title = editor.getTitle(Verbosity.LONG);
if (description === title) {
title = ''; // dont repeat what is already shown
}
this.editorLabel.setLabel({ name, description, resource }, { title, italic: !isPinned, extraClasses: ['title-label'] });
if (isActive) {
this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
} else {
this.editorLabel.element.style.color = this.getColor(TAB_UNFOCUSED_ACTIVE_FOREGROUND);
}
// Update Editor Actions Toolbar
this.updateEditorActionsToolbar();
}
private getVerbosity(style: string): Verbosity {
@@ -155,4 +230,16 @@ export class NoTabsTitleControl extends TitleControl {
default: return Verbosity.MEDIUM;
}
}
protected prepareEditorActions(editorActions: IToolbarActions): { primaryEditorActions: IAction[], secondaryEditorActions: IAction[] } {
const isGroupActive = this.accessor.activeGroup === this.group;
// Group active: show all actions
if (isGroupActive) {
return super.prepareEditorActions(editorActions);
}
// Group inactive: only show close action
return { primaryEditorActions: editorActions.primary.filter(action => action.id === CLOSE_EDITOR_COMMAND_ID), secondaryEditorActions: [] };
}
}

View File

@@ -3,10 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IRange } from 'vs/editor/common/core/range';
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
@@ -19,27 +19,29 @@ export interface IRangeHighlightDecoration {
isWholeLine?: boolean;
}
export class RangeHighlightDecorations implements IDisposable {
export class RangeHighlightDecorations extends Disposable {
private rangeHighlightDecorationId: string = null;
private editor: ICodeEditor = null;
private editorDisposables: IDisposable[] = [];
private readonly _onHighlightRemoved: Emitter<void> = new Emitter<void>();
public readonly onHighlghtRemoved: Event<void> = this._onHighlightRemoved.event;
private readonly _onHighlightRemoved: Emitter<void> = this._register(new Emitter<void>());
get onHighlghtRemoved(): Event<void> { return this._onHighlightRemoved.event; }
constructor(@IWorkbenchEditorService private editorService: IWorkbenchEditorService) {
constructor(@IEditorService private editorService: IEditorService) {
super();
}
public removeHighlightRange() {
removeHighlightRange() {
if (this.editor && this.editor.getModel() && this.rangeHighlightDecorationId) {
this.editor.deltaDecorations([this.rangeHighlightDecorationId], []);
this._onHighlightRemoved.fire();
}
this.rangeHighlightDecorationId = null;
}
public highlightRange(range: IRangeHighlightDecoration, editor?: ICodeEditor) {
highlightRange(range: IRangeHighlightDecoration, editor?: ICodeEditor) {
editor = editor ? editor : this.getEditor(range);
if (editor) {
this.doHighlightRange(editor, range);
@@ -48,20 +50,23 @@ export class RangeHighlightDecorations implements IDisposable {
private doHighlightRange(editor: ICodeEditor, selectionRange: IRangeHighlightDecoration) {
this.removeHighlightRange();
editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {
this.rangeHighlightDecorationId = changeAccessor.addDecoration(selectionRange.range, this.createRangeHighlightDecoration(selectionRange.isWholeLine));
});
this.setEditor(editor);
}
private getEditor(resourceRange: IRangeHighlightDecoration): ICodeEditor {
const activeInput = this.editorService.getActiveEditorInput();
const resource = activeInput && activeInput.getResource();
const activeEditor = this.editorService.activeEditor;
const resource = activeEditor && activeEditor.getResource();
if (resource) {
if (resource.toString() === resourceRange.resource.toString()) {
return <ICodeEditor>this.editorService.getActiveEditor().getControl();
return this.editorService.activeTextEditorWidget as ICodeEditor;
}
}
return null;
}
@@ -107,7 +112,9 @@ export class RangeHighlightDecorations implements IDisposable {
return (isWholeLine ? RangeHighlightDecorations._WHOLE_LINE_RANGE_HIGHLIGHT : RangeHighlightDecorations._RANGE_HIGHLIGHT);
}
public dispose() {
dispose() {
super.dispose();
if (this.editor && this.editor.getModel()) {
this.removeHighlightRange();
this.disposeEditorListeners();

View File

@@ -3,13 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
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 * as paths from 'vs/base/common/paths';
import { Builder, $ } from 'vs/base/browser/builder';
import * as DOM from 'vs/base/browser/dom';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
@@ -24,79 +21,26 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Registry } from 'vs/platform/registry/common/platform';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { memoize } from 'vs/base/common/decorators';
import * as platform from 'vs/base/common/platform';
interface MapExtToMediaMimes {
[index: string]: string;
}
// Known media mimes that we can handle
const mapExtToMediaMimes: MapExtToMediaMimes = {
'.bmp': 'image/bmp',
'.gif': 'image/gif',
'.jpg': 'image/jpg',
'.jpeg': 'image/jpg',
'.jpe': 'image/jpg',
'.png': 'image/png',
'.tiff': 'image/tiff',
'.tif': 'image/tiff',
'.ico': 'image/x-icon',
'.tga': 'image/x-tga',
'.psd': 'image/vnd.adobe.photoshop',
'.webp': 'image/webp',
'.mid': 'audio/midi',
'.midi': 'audio/midi',
'.mp4a': 'audio/mp4',
'.mpga': 'audio/mpeg',
'.mp2': 'audio/mpeg',
'.mp2a': 'audio/mpeg',
'.mp3': 'audio/mpeg',
'.m2a': 'audio/mpeg',
'.m3a': 'audio/mpeg',
'.oga': 'audio/ogg',
'.ogg': 'audio/ogg',
'.spx': 'audio/ogg',
'.aac': 'audio/x-aac',
'.wav': 'audio/x-wav',
'.wma': 'audio/x-ms-wma',
'.mp4': 'video/mp4',
'.mp4v': 'video/mp4',
'.mpg4': 'video/mp4',
'.mpeg': 'video/mpeg',
'.mpg': 'video/mpeg',
'.mpe': 'video/mpeg',
'.m1v': 'video/mpeg',
'.m2v': 'video/mpeg',
'.ogv': 'video/ogg',
'.qt': 'video/quicktime',
'.mov': 'video/quicktime',
'.webm': 'video/webm',
'.mkv': 'video/x-matroska',
'.mk3d': 'video/x-matroska',
'.mks': 'video/x-matroska',
'.wmv': 'video/x-ms-wmv',
'.flv': 'video/x-flv',
'.avi': 'video/x-msvideo',
'.movie': 'video/x-sgi-movie'
};
import { IFileService } from 'vs/platform/files/common/files';
export interface IResourceDescriptor {
resource: URI;
name: string;
size: number;
etag: string;
mime: string;
readonly resource: URI;
readonly name: string;
readonly size: number;
readonly etag: string;
readonly mime: string;
}
class BinarySize {
public static readonly KB = 1024;
public static readonly MB = BinarySize.KB * BinarySize.KB;
public static readonly GB = BinarySize.MB * BinarySize.KB;
public static readonly TB = BinarySize.GB * BinarySize.KB;
static readonly KB = 1024;
static readonly MB = BinarySize.KB * BinarySize.KB;
static readonly GB = BinarySize.MB * BinarySize.KB;
static readonly TB = BinarySize.GB * BinarySize.KB;
public static formatSize(size: number): string {
static formatSize(size: number): string {
if (size < BinarySize.KB) {
return nls.localize('sizeB', "{0}B", size);
}
@@ -129,8 +73,9 @@ export class ResourceViewer {
private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally
public static show(
static show(
descriptor: IResourceDescriptor,
fileService: IFileService,
container: HTMLElement,
scrollbar: DomScrollableElement,
openInternalClb: (uri: URI) => void,
@@ -143,7 +88,7 @@ export class ResourceViewer {
// Images
if (ResourceViewer.isImageResource(descriptor)) {
return ImageView.create(container, descriptor, scrollbar, openExternalClb, metadataClb);
return ImageView.create(container, descriptor, fileService, scrollbar, openExternalClb, metadataClb);
}
// Large Files
@@ -160,37 +105,26 @@ export class ResourceViewer {
}
private static isImageResource(descriptor: IResourceDescriptor) {
const mime = ResourceViewer.getMime(descriptor);
const mime = getMime(descriptor);
return mime.indexOf('image/') >= 0;
}
private static getMime(descriptor: IResourceDescriptor): string {
let mime = descriptor.mime;
if (!mime && descriptor.resource.scheme !== Schemas.data) {
const ext = paths.extname(descriptor.resource.toString());
if (ext) {
mime = mapExtToMediaMimes[ext.toLowerCase()];
}
}
return mime || mimes.MIME_BINARY;
}
}
class ImageView {
private static readonly MAX_IMAGE_SIZE = BinarySize.MB; // showing images inline is memory intense, so we have a limit
private static readonly BASE64_MARKER = 'base64,';
public static create(
static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
fileService: IFileService,
scrollbar: DomScrollableElement,
openExternalClb: (uri: URI) => void,
metadataClb: (meta: string) => void
): ResourceViewerContext | null {
if (ImageView.shouldShowImageInline(descriptor)) {
return InlineImageView.create(container, descriptor, scrollbar, metadataClb);
return InlineImageView.create(container, descriptor, fileService, scrollbar, metadataClb);
}
LargeImageView.create(container, descriptor, openExternalClb);
@@ -219,7 +153,7 @@ class ImageView {
}
class LargeImageView {
public static create(
static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
openExternalClb: (uri: URI) => void
@@ -245,7 +179,7 @@ class LargeImageView {
}
class FileTooLargeFileView {
public static create(
static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
@@ -268,7 +202,7 @@ class FileTooLargeFileView {
}
class FileSeemsBinaryFileView {
public static create(
static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
@@ -302,29 +236,32 @@ class FileSeemsBinaryFileView {
type Scale = number | 'fit';
class ZoomStatusbarItem extends Themable implements IStatusbarItem {
static instance: ZoomStatusbarItem;
showTimeout: number;
public static instance: ZoomStatusbarItem;
private statusBarItem: HTMLElement;
private onSelectScale?: (scale: Scale) => void;
constructor(
@IContextMenuService private contextMenuService: IContextMenuService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@IEditorService editorService: IEditorService,
@IThemeService themeService: IThemeService
) {
super(themeService);
ZoomStatusbarItem.instance = this;
this.toUnbind.push(editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
this._register(editorService.onDidActiveEditorChange(() => this.onActiveEditorChanged()));
}
private onEditorsChanged(): void {
private onActiveEditorChanged(): void {
this.hide();
this.onSelectScale = void 0;
}
public show(scale: Scale, onSelectScale: (scale: number) => void) {
show(scale: Scale, onSelectScale: (scale: number) => void) {
clearTimeout(this.showTimeout);
this.showTimeout = setTimeout(() => {
this.onSelectScale = onSelectScale;
@@ -333,11 +270,11 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
}, 0);
}
public hide() {
hide() {
this.statusBarItem.style.display = 'none';
}
public render(container: HTMLElement): IDisposable {
render(container: HTMLElement): IDisposable {
if (!this.statusBarItem && container) {
this.statusBarItem = $(container).a()
.addClass('.zoom-statusbar-item')
@@ -419,22 +356,15 @@ class InlineImageView {
*/
private static readonly PIXELATION_THRESHOLD = 3;
/**
* Chrome is caching images very aggressively and so we use the ETag information to find out if
* we need to bypass the cache or not. We could always bypass the cache everytime we show the image
* however that has very bad impact on memory consumption because each time the image gets shown,
* memory grows (see also https://github.com/electron/electron/issues/6275)
*/
private static IMAGE_RESOURCE_ETAG_CACHE = new LRUCache<string, { etag: string, src: string }>(100);
/**
* Store the scale and position of an image so it can be restored when changing editor tabs
*/
private static readonly imageStateCache = new LRUCache<string, ImageState>(100);
public static create(
static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
fileService: IFileService,
scrollbar: DomScrollableElement,
metadataClb: (meta: string) => void
) {
@@ -601,7 +531,7 @@ class InlineImageView {
$(container)
.empty()
.addClass('image', 'zoom-in')
.img({ src: InlineImageView.imageSrc(descriptor) })
.img({})
.style('visibility', 'hidden')
.addClass('scale-to-fit')
.on(DOM.EventType.LOAD, (e, i) => {
@@ -619,27 +549,33 @@ class InlineImageView {
}
});
InlineImageView.imageSrc(descriptor, fileService).then(dataUri => {
const imgs = container.getElementsByTagName('img');
if (imgs.length) {
imgs[0].src = dataUri;
}
});
return context;
}
private static imageSrc(descriptor: IResourceDescriptor): string {
private static imageSrc(descriptor: IResourceDescriptor, fileService: IFileService): TPromise<string> {
if (descriptor.resource.scheme === Schemas.data) {
return descriptor.resource.toString(true /* skip encoding */);
return TPromise.as(descriptor.resource.toString(true /* skip encoding */));
}
const src = descriptor.resource.toString();
return fileService.resolveContent(descriptor.resource, { encoding: 'base64' }).then(data => {
const mime = getMime(descriptor);
let cached = InlineImageView.IMAGE_RESOURCE_ETAG_CACHE.get(src);
if (!cached) {
cached = { etag: descriptor.etag, src };
InlineImageView.IMAGE_RESOURCE_ETAG_CACHE.set(src, cached);
}
if (cached.etag !== descriptor.etag) {
cached.etag = descriptor.etag;
cached.src = `${src}?${Date.now()}`; // bypass cache with this trick
}
return cached.src;
return `data:${mime};base64,${data.value}`;
});
}
}
}
function getMime(descriptor: IResourceDescriptor) {
let mime = descriptor.mime;
if (!mime && descriptor.resource.scheme !== Schemas.data) {
mime = mimes.getMediaMime(descriptor.resource.toString());
}
return mime || mimes.MIME_BINARY;
}

View File

@@ -6,29 +6,55 @@
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 } from 'vs/workbench/common/editor';
import { EditorInput, EditorOptions, SideBySideEditorInput, IEditorControl, IEditor } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorControl, Position, IEditor } from 'vs/platform/editor/common/editor';
import { VSash } from 'vs/base/browser/ui/sash/sash';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
import { SplitView, Sizing, Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { Event, Relay, anyEvent, mapEvent, Emitter } from 'vs/base/common/event';
export class SideBySideEditor extends BaseEditor {
public static readonly ID: string = 'workbench.editor.sidebysideEditor';
static readonly ID: string = 'workbench.editor.sidebysideEditor';
private dimension: DOM.Dimension;
get minimumMasterWidth() { return this.masterEditor ? this.masterEditor.minimumWidth : 0; }
get maximumMasterWidth() { return this.masterEditor ? this.masterEditor.maximumWidth : Number.POSITIVE_INFINITY; }
get minimumMasterHeight() { return this.masterEditor ? this.masterEditor.minimumHeight : 0; }
get maximumMasterHeight() { return this.masterEditor ? this.masterEditor.maximumHeight : Number.POSITIVE_INFINITY; }
get minimumDetailsWidth() { return this.detailsEditor ? this.detailsEditor.minimumWidth : 0; }
get maximumDetailsWidth() { return this.detailsEditor ? this.detailsEditor.maximumWidth : Number.POSITIVE_INFINITY; }
get minimumDetailsHeight() { return this.detailsEditor ? this.detailsEditor.minimumHeight : 0; }
get maximumDetailsHeight() { return this.detailsEditor ? this.detailsEditor.maximumHeight : Number.POSITIVE_INFINITY; }
// these setters need to exist because this extends from BaseEditor
set minimumWidth(value: number) { /* noop */ }
set maximumWidth(value: number) { /* noop */ }
set minimumHeight(value: number) { /* noop */ }
set maximumHeight(value: number) { /* noop */ }
get minimumWidth() { return this.minimumMasterWidth + this.minimumDetailsWidth; }
get maximumWidth() { return this.maximumMasterWidth + this.maximumDetailsWidth; }
get minimumHeight() { return this.minimumMasterHeight + this.minimumDetailsHeight; }
get maximumHeight() { return this.maximumMasterHeight + this.maximumDetailsHeight; }
protected masterEditor: BaseEditor;
private masterEditorContainer: HTMLElement;
protected detailsEditor: BaseEditor;
private masterEditorContainer: HTMLElement;
private detailsEditorContainer: HTMLElement;
private sash: VSash;
private splitview: SplitView;
private dimension: DOM.Dimension = new DOM.Dimension(0, 0);
private onDidCreateEditors = this._register(new Emitter<{ width: number; height: number; }>());
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; }>());
readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = anyEvent(this.onDidCreateEditors.event, this._onDidSizeConstraintsChange.event);
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -40,36 +66,54 @@ export class SideBySideEditor extends BaseEditor {
protected createEditor(parent: HTMLElement): void {
DOM.addClass(parent, 'side-by-side-editor');
this.createSash(parent);
this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL }));
this._register(this.splitview.onDidSashReset(() => this.splitview.distributeViewSizes()));
this.detailsEditorContainer = DOM.$('.details-editor-container');
this.splitview.addView({
element: this.detailsEditorContainer,
layout: size => this.detailsEditor && this.detailsEditor.layout(new DOM.Dimension(size, this.dimension.height)),
minimumSize: 220,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
}, Sizing.Distribute);
this.masterEditorContainer = DOM.$('.master-editor-container');
this.splitview.addView({
element: this.masterEditorContainer,
layout: size => this.masterEditor && this.masterEditor.layout(new DOM.Dimension(size, this.dimension.height)),
minimumSize: 220,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
}, Sizing.Distribute);
this.updateStyles();
}
public setInput(newInput: SideBySideEditorInput, options?: EditorOptions): TPromise<void> {
setInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
const oldInput = <SideBySideEditorInput>this.input;
return super.setInput(newInput, options)
.then(() => this.updateInput(oldInput, newInput, options));
return super.setInput(newInput, options, token)
.then(() => this.updateInput(oldInput, newInput, options, token));
}
protected setEditorVisible(visible: boolean, position: Position): void {
setOptions(options: EditorOptions): void {
if (this.masterEditor) {
this.masterEditor.setVisible(visible, position);
this.masterEditor.setOptions(options);
}
}
protected setEditorVisible(visible: boolean, group: IEditorGroup): void {
if (this.masterEditor) {
this.masterEditor.setVisible(visible, group);
}
if (this.detailsEditor) {
this.detailsEditor.setVisible(visible, position);
this.detailsEditor.setVisible(visible, group);
}
super.setEditorVisible(visible, position);
super.setEditorVisible(visible, group);
}
public changePosition(position: Position): void {
if (this.masterEditor) {
this.masterEditor.changePosition(position);
}
if (this.detailsEditor) {
this.detailsEditor.changePosition(position);
}
super.changePosition(position);
}
public clearInput(): void {
clearInput(): void {
if (this.masterEditor) {
this.masterEditor.clearInput();
}
@@ -80,57 +124,49 @@ export class SideBySideEditor extends BaseEditor {
super.clearInput();
}
public focus(): void {
focus(): void {
if (this.masterEditor) {
this.masterEditor.focus();
}
}
public layout(dimension: DOM.Dimension): void {
layout(dimension: DOM.Dimension): void {
this.dimension = dimension;
this.sash.setDimenesion(this.dimension);
this.splitview.layout(dimension.width);
}
public getControl(): IEditorControl {
getControl(): IEditorControl {
if (this.masterEditor) {
return this.masterEditor.getControl();
}
return null;
}
public getMasterEditor(): IEditor {
getMasterEditor(): IEditor {
return this.masterEditor;
}
public getDetailsEditor(): IEditor {
getDetailsEditor(): IEditor {
return this.detailsEditor;
}
public supportsCenteredLayout(): boolean {
return false;
}
private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options?: EditorOptions): void {
private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
if (!newInput.matches(oldInput)) {
if (oldInput) {
this.disposeEditors();
}
this.createEditorContainers();
return this.setNewInput(newInput, options);
} else {
this.detailsEditor.setInput(newInput.details);
this.masterEditor.setInput(newInput.master, options);
return void 0;
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);
}
private setNewInput(newInput: SideBySideEditorInput, options?: EditorOptions): void {
private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
const detailsEditor = this._createEditor(<EditorInput>newInput.details, this.detailsEditorContainer);
const masterEditor = this._createEditor(<EditorInput>newInput.master, this.masterEditorContainer);
this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options);
return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token);
}
private _createEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor {
@@ -138,29 +174,26 @@ export class SideBySideEditor extends BaseEditor {
const editor = descriptor.instantiate(this.instantiationService);
editor.create(container);
editor.setVisible(this.isVisible(), this.position);
editor.setVisible(this.isVisible(), this.group);
return editor;
}
private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions): TPromise<void> {
private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<void> {
this.detailsEditor = details;
this.masterEditor = master;
this.dolayout(this.sash.getVerticalSashLeft());
return TPromise.join([this.detailsEditor.setInput(detailsInput), this.masterEditor.setInput(masterInput, options)]).then(() => this.focus());
this._onDidSizeConstraintsChange.input = anyEvent(
mapEvent(details.onDidSizeConstraintsChange, () => undefined),
mapEvent(master.onDidSizeConstraintsChange, () => undefined)
);
this.onDidCreateEditors.fire();
return TPromise.join([this.detailsEditor.setInput(detailsInput, null, token), this.masterEditor.setInput(masterInput, options, token)]).then(() => this.focus());
}
private createEditorContainers(): void {
const parentElement = this.getContainer();
this.detailsEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container'));
this.detailsEditorContainer.style.position = 'absolute';
this.masterEditorContainer = DOM.append(parentElement, DOM.$('.master-editor-container'));
this.masterEditorContainer.style.position = 'absolute';
this.updateStyles();
}
public updateStyles(): void {
updateStyles(): void {
super.updateStyles();
if (this.masterEditorContainer) {
@@ -168,52 +201,24 @@ export class SideBySideEditor extends BaseEditor {
}
}
private createSash(parentElement: HTMLElement): void {
this.sash = this._register(new VSash(parentElement, 220));
this._register(this.sash.onPositionChange(position => this.dolayout(position)));
}
private dolayout(splitPoint: number): void {
if (!this.detailsEditor || !this.masterEditor || !this.dimension) {
return;
}
const masterEditorWidth = this.dimension.width - splitPoint;
const detailsEditorWidth = this.dimension.width - masterEditorWidth;
this.detailsEditorContainer.style.width = `${detailsEditorWidth}px`;
this.detailsEditorContainer.style.height = `${this.dimension.height}px`;
this.detailsEditorContainer.style.left = '0px';
this.masterEditorContainer.style.width = `${masterEditorWidth}px`;
this.masterEditorContainer.style.height = `${this.dimension.height}px`;
this.masterEditorContainer.style.left = `${splitPoint}px`;
this.detailsEditor.layout(new DOM.Dimension(detailsEditorWidth, this.dimension.height));
this.masterEditor.layout(new DOM.Dimension(masterEditorWidth, this.dimension.height));
}
private disposeEditors(): void {
const parentContainer = this.getContainer();
if (this.detailsEditor) {
this.detailsEditor.dispose();
this.detailsEditor = null;
}
if (this.masterEditor) {
this.masterEditor.dispose();
this.masterEditor = null;
}
if (this.detailsEditorContainer) {
parentContainer.removeChild(this.detailsEditorContainer);
this.detailsEditorContainer = null;
}
if (this.masterEditorContainer) {
parentContainer.removeChild(this.masterEditorContainer);
this.masterEditorContainer = null;
}
this.detailsEditorContainer.innerHTML = '';
this.masterEditorContainer.innerHTML = '';
}
public dispose(): void {
dispose(): void {
this.disposeEditors();
super.dispose();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -10,12 +10,11 @@ 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 { onUnexpectedError } from 'vs/base/common/errors';
import * as types from 'vs/base/common/types';
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ITextDiffEditor, IEditorMemento } from 'vs/workbench/common/editor';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator';
@@ -26,28 +25,28 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { 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 { getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
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';
/**
* The text editor that leverages the diff text editor for the editing experience.
*/
export class TextDiffEditor extends BaseTextEditor {
export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
public static readonly ID = TEXT_DIFF_EDITOR_ID;
static readonly ID = TEXT_DIFF_EDITOR_ID;
private diffNavigator: DiffNavigator;
private diffNavigatorDisposables: IDisposable[];
private diffNavigatorDisposables: IDisposable[] = [];
private nextDiffAction: NavigateAction;
private previousDiffAction: NavigateAction;
private toggleIgnoreTrimWhitespaceAction: ToggleIgnoreTrimWhitespaceAction;
@@ -58,22 +57,25 @@ export class TextDiffEditor extends BaseTextEditor {
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IConfigurationService private readonly _actualConfigurationService: IConfigurationService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorService editorService: IEditorService,
@IThemeService themeService: IThemeService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ITextFileService textFileService: ITextFileService
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@ITextFileService textFileService: ITextFileService,
) {
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService);
this.diffNavigatorDisposables = [];
this.toUnbind.push(this._actualConfigurationService.onDidChangeConfiguration((e) => {
this._register(this._actualConfigurationService.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('diffEditor.ignoreTrimWhitespace')) {
this.updateIgnoreTrimWhitespaceAction();
}
}));
}
public getTitle(): string {
protected getEditorMemento<T>(storageService: IStorageService, 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
}
getTitle(): string {
if (this.input) {
return this.input.getName();
}
@@ -81,7 +83,7 @@ export class TextDiffEditor extends BaseTextEditor {
return nls.localize('textDiffEditor', "Text Diff Editor");
}
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IDiffEditor {
createEditorControl(parent: HTMLElement, configuration: ICodeEditorOptions): IDiffEditor {
// Actions
this.nextDiffAction = new NavigateAction(this, true);
@@ -89,55 +91,10 @@ export class TextDiffEditor extends BaseTextEditor {
this.toggleIgnoreTrimWhitespaceAction = new ToggleIgnoreTrimWhitespaceAction(this._actualConfigurationService);
this.updateIgnoreTrimWhitespaceAction();
// Support navigation within the diff editor by overriding the editor service within
const delegatingEditorService = this.instantiationService.createInstance(DelegatingWorkbenchEditorService);
delegatingEditorService.setEditorOpenHandler((input: EditorInput, options?: EditorOptions, arg3?: any) => {
// Check if arg4 is a position argument that differs from this editors position
if (types.isUndefinedOrNull(arg3) || arg3 === false || arg3 === this.position) {
const activeDiffInput = <DiffEditorInput>this.input;
if (input && options && activeDiffInput) {
// Input matches modified side of the diff editor: perform the action on modified side
if (input.matches(activeDiffInput.modifiedInput)) {
return this.setInput(this.input, options).then(() => this);
}
// Input matches original side of the diff editor: perform the action on original side
else if (input.matches(activeDiffInput.originalInput)) {
const originalEditor = this.getControl().getOriginalEditor();
if (options instanceof TextEditorOptions) {
(<TextEditorOptions>options).apply(originalEditor, ScrollType.Smooth);
return TPromise.as(this);
}
}
}
}
return TPromise.as(null);
});
// Create a special child of instantiator that will delegate all calls to openEditor() to the same diff editor if the input matches with the modified one
const diffEditorInstantiator = this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService]));
return diffEditorInstantiator.createInstance(DiffEditorWidget, parent, configuration);
return this.instantiationService.createInstance(DiffEditorWidget, parent, configuration);
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
// Return early for same input unless we force to open
const forceOpen = options && options.forceOpen;
if (!forceOpen && input.matches(this.input)) {
// Still apply options if any (avoiding instanceof here for a reason, do not change!)
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(<IDiffEditor>this.getControl(), ScrollType.Smooth);
}
return TPromise.wrap<void>(null);
}
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
// Dispose previous diff navigator
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
@@ -146,27 +103,28 @@ export class TextDiffEditor extends BaseTextEditor {
this.saveTextDiffEditorViewState(this.input);
// Set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then(resolvedModel => {
return super.setInput(input, options, token).then(() => {
return input.resolve().then(resolvedModel => {
// Check for cancellation
if (token.isCancellationRequested) {
return void 0;
}
// Assert Model Instance
if (!(resolvedModel instanceof TextDiffEditorModel) && this.openAsBinary(input, options)) {
return null;
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading a diff takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
return void 0;
}
// Set Editor Model
const diffEditor = <IDiffEditor>this.getControl();
diffEditor.setModel((<TextDiffEditorModel>resolvedModel).textDiffEditorModel);
const diffEditor = this.getControl();
const resolvedDiffEditorModel = <TextDiffEditorModel>resolvedModel;
diffEditor.setModel(resolvedDiffEditorModel.textDiffEditorModel);
// Apply Options from TextOptions
let optionsGotApplied = false;
if (options && types.isFunction((<TextEditorOptions>options).apply)) {
optionsGotApplied = (<TextEditorOptions>options).apply(<IDiffEditor>diffEditor, ScrollType.Immediate);
optionsGotApplied = (<TextEditorOptions>options).apply(diffEditor, ScrollType.Immediate);
}
// Otherwise restore View State
@@ -175,6 +133,7 @@ export class TextDiffEditor extends BaseTextEditor {
hasPreviousViewState = this.restoreTextDiffEditorViewState(input);
}
// Diff navigator
this.diffNavigator = new DiffNavigator(diffEditor, {
alwaysRevealFirst: !optionsGotApplied && !hasPreviousViewState // only reveal first change if we had no options or viewstate
});
@@ -185,7 +144,11 @@ export class TextDiffEditor extends BaseTextEditor {
this.previousDiffAction.updateEnablement();
}));
// Enablement of actions
this.updateIgnoreTrimWhitespaceAction();
// Readonly flag
diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() });
}, error => {
// In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff.
@@ -199,8 +162,11 @@ export class TextDiffEditor extends BaseTextEditor {
});
}
public supportsCenteredLayout(): boolean {
return false;
setOptions(options: EditorOptions): void {
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(this.getControl(), ScrollType.Smooth);
}
}
private restoreTextDiffEditorViewState(input: EditorInput): boolean {
@@ -243,7 +209,7 @@ export class TextDiffEditor extends BaseTextEditor {
modifiedInput.setForceOpenAsBinary();
}
this.editorService.openEditor(binaryDiffInput, options, this.position).done(null, onUnexpectedError);
this.editorService.openEditor(binaryDiffInput, options, this.group);
return true;
}
@@ -251,7 +217,7 @@ export class TextDiffEditor extends BaseTextEditor {
return false;
}
protected computeConfiguration(configuration: IEditorConfiguration): IEditorOptions {
protected computeConfiguration(configuration: IEditorConfiguration): ICodeEditorOptions {
const editorConfiguration = super.computeConfiguration(configuration);
// Handle diff editor specially by merging in diffEditor configuration
@@ -262,7 +228,7 @@ export class TextDiffEditor extends BaseTextEditor {
return editorConfiguration;
}
protected getConfigurationOverrides(): IEditorOptions {
protected getConfigurationOverrides(): ICodeEditorOptions {
const options: IDiffEditorOptions = super.getConfigurationOverrides();
options.readOnly = this.isReadOnly();
@@ -304,7 +270,7 @@ export class TextDiffEditor extends BaseTextEditor {
return (<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY;
}
public clearInput(): void {
clearInput(): void {
// Dispose previous diff navigator
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
@@ -319,11 +285,11 @@ export class TextDiffEditor extends BaseTextEditor {
super.clearInput();
}
public getDiffNavigator(): DiffNavigator {
getDiffNavigator(): DiffNavigator {
return this.diffNavigator;
}
public getActions(): IAction[] {
getActions(): IAction[] {
return [
this.toggleIgnoreTrimWhitespaceAction,
this.previousDiffAction,
@@ -331,7 +297,7 @@ export class TextDiffEditor extends BaseTextEditor {
];
}
public getControl(): IDiffEditor {
getControl(): IDiffEditor {
return super.getControl() as IDiffEditor;
}
@@ -370,12 +336,8 @@ export class TextDiffEditor extends BaseTextEditor {
}
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState {
const editor = getCodeOrDiffEditor(this).diffEditor;
if (!editor) {
return null; // not supported for non-diff editors
}
const model = editor.getModel();
const control = this.getControl();
const model = control.getModel();
if (!model || !model.modified || !model.original) {
return null; // view state always needs a model
}
@@ -389,7 +351,7 @@ export class TextDiffEditor extends BaseTextEditor {
return null; // prevent saving view state for a model that is not the expected one
}
return editor.saveViewState();
return control.saveViewState();
}
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI {
@@ -412,7 +374,7 @@ export class TextDiffEditor extends BaseTextEditor {
return URI.from({ scheme: 'diff', path: `${btoa(original.toString())}${btoa(modified.toString())}` });
}
public dispose(): void {
dispose(): void {
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
super.dispose();
@@ -437,7 +399,7 @@ class NavigateAction extends Action {
this.enabled = false;
}
public run(): TPromise<any> {
run(): TPromise<any> {
if (this.next) {
this.editor.getDiffNavigator().next();
} else {
@@ -447,7 +409,7 @@ class NavigateAction extends Action {
return null;
}
public updateEnablement(): void {
updateEnablement(): void {
this.enabled = this.editor.getDiffNavigator().canNavigate();
}
}
@@ -464,12 +426,12 @@ class ToggleIgnoreTrimWhitespaceAction extends Action {
this.label = nls.localize('toggleIgnoreTrimWhitespace.label', "Ignore Trim Whitespace");
}
public updateClassName(ignoreTrimWhitespace: boolean): void {
updateClassName(ignoreTrimWhitespace: boolean): void {
this._isChecked = ignoreTrimWhitespace;
this.class = `textdiff-editor-action toggleIgnoreTrimWhitespace${this._isChecked ? ' is-checked' : ''}`;
}
public run(): TPromise<any> {
run(): TPromise<any> {
this._configurationService.updateValue(`diffEditor.ignoreTrimWhitespace`, !this._isChecked);
return null;
}

View File

@@ -6,28 +6,26 @@
'use strict';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
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 { CodeEditor } from 'vs/editor/browser/codeEditor';
import { EditorInput, EditorOptions, EditorViewStateMemento } from 'vs/workbench/common/editor';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { EditorInput, EditorOptions, IEditorMemento, ITextEditor } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorViewState, IEditor } from 'vs/editor/common/editorCommon';
import { Position } from 'vs/platform/editor/common/editor';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Scope } from 'vs/workbench/common/memento';
import { getCodeEditor, getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
import { ITextFileService, SaveReason, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { isDiffEditor, isCodeEditor, ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
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';
const TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'textEditorViewState';
@@ -40,12 +38,12 @@ export interface IEditorConfiguration {
* The base class of editors that leverage the text editor for the editing experience. This class is only intended to
* be subclassed and not instantiated.
*/
export abstract class BaseTextEditor extends BaseEditor {
export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
private editorControl: IEditor;
private _editorContainer: HTMLElement;
private hasPendingConfigurationChange: boolean;
private lastAppliedEditorOptions: IEditorOptions;
private editorViewStateMemento: EditorViewStateMemento<IEditorViewState>;
private editorMemento: IEditorMemento<IEditorViewState>;
constructor(
id: string,
@@ -55,13 +53,14 @@ export abstract class BaseTextEditor extends BaseEditor {
@ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService,
@IThemeService protected themeService: IThemeService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IEditorGroupService protected editorGroupService: IEditorGroupService
@IEditorService protected editorService: IEditorService,
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
) {
super(id, telemetryService, themeService);
this.editorViewStateMemento = new EditorViewStateMemento<IEditorViewState>(this.getMemento(storageService, Scope.WORKSPACE), TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
this.editorMemento = this.getEditorMemento<IEditorViewState>(storageService, editorGroupService, TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
this._register(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
}
protected get instantiationService(): IInstantiationService {
@@ -107,8 +106,10 @@ export abstract class BaseTextEditor extends BaseEditor {
let ariaLabel = this.getAriaLabel();
// Apply group information to help identify in which group we are
if (ariaLabel && typeof this.position === 'number') {
ariaLabel = nls.localize('editorLabelWithGroup', "{0}, Group {1}.", ariaLabel, this.position + 1);
if (ariaLabel) {
if (this.group) {
ariaLabel = nls.localize('editorLabelWithGroup', "{0}, {1}.", ariaLabel, this.group.label);
}
}
return ariaLabel;
@@ -129,25 +130,25 @@ export abstract class BaseTextEditor extends BaseEditor {
// Editor for Text
this._editorContainer = parent;
this.editorControl = this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue<IEditorConfiguration>(this.getResource())));
this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
// Model & Language changes
const codeEditor = getCodeEditor(this);
const codeEditor = getCodeEditor(this.editorControl);
if (codeEditor) {
this.toUnbind.push(codeEditor.onDidChangeModelLanguage(e => this.updateEditorConfiguration()));
this.toUnbind.push(codeEditor.onDidChangeModel(e => this.updateEditorConfiguration()));
this._register(codeEditor.onDidChangeModelLanguage(e => this.updateEditorConfiguration()));
this._register(codeEditor.onDidChangeModel(e => this.updateEditorConfiguration()));
}
// Application & Editor focus change to respect auto save settings
if (isCodeEditor(this.editorControl)) {
this.toUnbind.push(this.editorControl.onDidBlurEditor(() => this.onEditorFocusLost()));
this._register(this.editorControl.onDidBlurEditorWidget(() => this.onEditorFocusLost()));
} else if (isDiffEditor(this.editorControl)) {
this.toUnbind.push(this.editorControl.getOriginalEditor().onDidBlurEditor(() => this.onEditorFocusLost()));
this.toUnbind.push(this.editorControl.getModifiedEditor().onDidBlurEditor(() => this.onEditorFocusLost()));
this._register(this.editorControl.getOriginalEditor().onDidBlurEditorWidget(() => this.onEditorFocusLost()));
this._register(this.editorControl.getModifiedEditor().onDidBlurEditorWidget(() => this.onEditorFocusLost()));
}
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorFocusLost()));
this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => this.onWindowFocusLost()));
this._register(this.editorService.onDidActiveEditorChange(() => this.onEditorFocusLost()));
this._register(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => this.onWindowFocusLost()));
}
private onEditorFocusLost(): void {
@@ -182,11 +183,11 @@ export abstract class BaseTextEditor extends BaseEditor {
protected createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IEditor {
// Use a getter for the instantiation service since some subclasses might use scoped instantiation services
return this.instantiationService.createInstance(CodeEditor, parent, configuration);
return this.instantiationService.createInstance(CodeEditorWidget, parent, configuration, {});
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
return super.setInput(input, options).then(() => {
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
return super.setInput(input, options, token).then(() => {
// Update editor options after having set the input. We do this because there can be
// editor input specific options (e.g. an ARIA label depending on the input showing)
@@ -195,16 +196,7 @@ export abstract class BaseTextEditor extends BaseEditor {
});
}
public changePosition(position: Position): void {
super.changePosition(position);
// Make sure to update ARIA label if the position of this editor changed
if (this.editorControl) {
this.editorControl.updateOptions({ ariaLabel: this.computeAriaLabel() });
}
}
protected setEditorVisible(visible: boolean, position: Position = null): void {
protected setEditorVisible(visible: boolean, group: IEditorGroup): void {
// Pass on to Editor
if (visible) {
@@ -214,20 +206,20 @@ export abstract class BaseTextEditor extends BaseEditor {
this.editorControl.onHide();
}
super.setEditorVisible(visible, position);
super.setEditorVisible(visible, group);
}
public focus(): void {
focus(): void {
this.editorControl.focus();
}
public layout(dimension: DOM.Dimension): void {
layout(dimension: DOM.Dimension): void {
// Pass on to Editor
this.editorControl.layout(dimension);
}
public getControl(): IEditor {
getControl(): IEditor {
return this.editorControl;
}
@@ -240,16 +232,12 @@ export abstract class BaseTextEditor extends BaseEditor {
return;
}
this.editorViewStateMemento.saveState(resource, this.position, editorViewState);
this.editorMemento.saveState(this.group, resource, editorViewState);
}
protected retrieveTextEditorViewState(resource: URI): IEditorViewState {
const editor = getCodeOrDiffEditor(this).codeEditor;
if (!editor) {
return null; // not supported for diff editors
}
const model = editor.getModel();
const control = this.getControl() as ICodeEditor;
const model = control.getModel();
if (!model) {
return null; // view state always needs a model
}
@@ -263,7 +251,7 @@ export abstract class BaseTextEditor extends BaseEditor {
return null; // prevent saving view state for a model that is not the expected one
}
return editor.saveViewState();
return control.saveViewState();
}
/**
@@ -271,7 +259,7 @@ export abstract class BaseTextEditor extends BaseEditor {
*/
protected clearTextEditorViewState(resources: URI[]): void {
resources.forEach(resource => {
this.editorViewStateMemento.clearState(resource);
this.editorMemento.clearState(resource);
});
}
@@ -279,7 +267,7 @@ export abstract class BaseTextEditor extends BaseEditor {
* Loads the text editor view state for the given resource and returns it.
*/
protected loadTextEditorViewState(resource: URI): IEditorViewState {
return this.editorViewStateMemento.loadState(resource, this.position);
return this.editorMemento.loadState(this.group, resource);
}
private updateEditorConfiguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
@@ -304,7 +292,7 @@ export abstract class BaseTextEditor extends BaseEditor {
}
protected getResource(): URI {
const codeEditor = getCodeEditor(this);
const codeEditor = getCodeEditor(this.editorControl);
if (codeEditor) {
const model = codeEditor.getModel();
if (model) {
@@ -321,17 +309,8 @@ export abstract class BaseTextEditor extends BaseEditor {
protected abstract getAriaLabel(): string;
protected saveMemento(): void {
// ensure to first save our view state memento
this.editorViewStateMemento.save();
super.saveMemento();
}
public dispose(): void {
dispose(): void {
this.lastAppliedEditorOptions = void 0;
this.editorControl.dispose();
super.dispose();
}

View File

@@ -19,10 +19,12 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { once } from 'vs/base/common/event';
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';
/**
* An editor implementation that is capable of showing the contents of resource inputs. Uses
@@ -37,13 +39,14 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ITextFileService textFileService: ITextFileService
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@ITextFileService textFileService: ITextFileService,
@IEditorService editorService: IEditorService
) {
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService);
}
public getTitle(): string {
getTitle(): string {
if (this.input) {
return this.input.getName();
}
@@ -51,38 +54,25 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
return nls.localize('textEditor', "Text Editor");
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
// Return early for same input unless we force to open
const forceOpen = options && options.forceOpen;
if (!forceOpen && input.matches(this.input)) {
// Still apply options if any (avoiding instanceof here for a reason, do not change!)
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(this.getControl(), ScrollType.Smooth);
}
return TPromise.wrap<void>(null);
}
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
// Remember view settings if input changes
this.saveTextResourceEditorViewState(this.input);
// Set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then((resolvedModel: EditorModel) => {
return super.setInput(input, options, token).then(() => {
return input.resolve().then((resolvedModel: EditorModel) => {
// Check for cancellation
if (token.isCancellationRequested) {
return void 0;
}
// Assert Model instance
if (!(resolvedModel instanceof BaseTextEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as text'));
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// Set Editor Model
const textEditor = this.getControl();
const textEditorModel = resolvedModel.textEditorModel;
@@ -114,6 +104,13 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
}
}
setOptions(options: EditorOptions): void {
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(this.getControl(), ScrollType.Smooth);
}
}
protected getConfigurationOverrides(): IEditorOptions {
const options = super.getConfigurationOverrides();
@@ -139,18 +136,23 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
/**
* Reveals the last line of this editor if it has a model set.
* When smart is true only scroll if the cursor is currently on the last line of the output panel.
* This allows users to click on the output panel to stop scrolling when they see something of interest.
* To resume, they should scroll to the end of the output panel again.
*/
public revealLastLine(): void {
revealLastLine(smart: boolean): void {
const codeEditor = <ICodeEditor>this.getControl();
const model = codeEditor.getModel();
if (model) {
const lastLine = model.getLineCount();
codeEditor.revealPosition({ lineNumber: lastLine, column: model.getLineMaxColumn(lastLine) }, ScrollType.Smooth);
if (!smart || codeEditor.getPosition().lineNumber === lastLine) {
codeEditor.revealPosition({ lineNumber: lastLine, column: model.getLineMaxColumn(lastLine) }, ScrollType.Smooth);
}
}
}
public clearInput(): void {
clearInput(): void {
// Keep editor view state in settings to restore when coming back
this.saveTextResourceEditorViewState(this.input);
@@ -161,7 +163,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
super.clearInput();
}
public shutdown(): void {
shutdown(): void {
// Save View State (only for untitled)
if (this.input instanceof UntitledEditorInput) {
@@ -198,7 +200,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
export class TextResourceEditor extends AbstractTextResourceEditor {
public static readonly ID = 'workbench.editors.textResourceEditor';
static readonly ID = 'workbench.editors.textResourceEditor';
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -206,9 +208,10 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ITextFileService textFileService: ITextFileService
@ITextFileService textFileService: ITextFileService,
@IEditorService editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
) {
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, textFileService);
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, textFileService, editorService);
}
}
}

View File

@@ -5,234 +5,133 @@
'use strict';
import 'vs/css!./media/titlecontrol';
import * as nls from 'vs/nls';
import { prepareActions } from 'vs/workbench/browser/actions';
import { IAction, Action, IRunEvent } from 'vs/base/common/actions';
import * as errors from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { RunOnceScheduler } from 'vs/base/common/async';
import * as arrays from 'vs/base/common/arrays';
import { IEditorStacksModel, IEditorGroup, IEditorIdentifier, EditorInput, IStacksModelChangeEvent, toResource, IEditorCommandsContext } from 'vs/workbench/common/editor';
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { applyDragImage } from 'vs/base/browser/dnd';
import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ActionsOrientation, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
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';
import { createActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { ExecuteCommandAction, IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { SplitEditorAction, CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createActionItem, fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IMenuService, MenuId, IMenu, ExecuteCommandAction } from 'vs/platform/actions/common/actions';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Themable } from 'vs/workbench/common/theme';
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension, findParentWithClass } from 'vs/base/browser/dom';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { prepareActions } from 'vs/workbench/browser/actions';
import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceDataTransfers, LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions } from 'vs/workbench/browser/parts/editor/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource } from 'vs/workbench/common/editor';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { Themable } from 'vs/workbench/common/theme';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
export interface IToolbarActions {
primary: IAction[];
secondary: IAction[];
}
export interface ITitleAreaControl {
setContext(group: IEditorGroup): void;
hasContext(): boolean;
allowDragging(element: HTMLElement): boolean;
setDragged(dragged: boolean): void;
create(parent: HTMLElement): void;
getContainer(): HTMLElement;
refresh(instant?: boolean): void;
update(instant?: boolean): void;
updateEditorActionsToolbar(): void;
layout(dimension: Dimension): void;
dispose(): void;
}
export abstract class TitleControl extends Themable {
export abstract class TitleControl extends Themable implements ITitleAreaControl {
protected readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
protected readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
protected stacks: IEditorStacksModel;
protected context: IEditorGroup;
protected dragged: boolean;
protected closeOneEditorAction: CloseOneEditorAction;
protected splitEditorAction: SplitEditorAction;
private parent: HTMLElement;
protected breadcrumbsControl: BreadcrumbsControl;
private currentPrimaryEditorActionIds: string[] = [];
private currentSecondaryEditorActionIds: string[] = [];
protected editorActionsToolbar: ToolBar;
private mapActionsToEditors: { [editorId: string]: IToolbarActions; };
private titleAreaUpdateScheduler: RunOnceScheduler;
private titleAreaToolbarUpdateScheduler: RunOnceScheduler;
private refreshScheduled: boolean;
private mapEditorToActions: Map<string, IToolbarActions> = new Map();
private resourceContext: ResourceContextKey;
private disposeOnEditorActions: IDisposable[] = [];
private editorToolBarMenuDisposables: IDisposable[] = [];
private contextMenu: IMenu;
constructor(
@IContextMenuService protected contextMenuService: IContextMenuService,
parent: HTMLElement,
protected accessor: IEditorGroupsAccessor,
protected group: IEditorGroupView,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
@IEditorGroupService protected editorGroupService: IEditorGroupService,
@IContextKeyService protected contextKeyService: IContextKeyService,
@IKeybindingService protected keybindingService: IKeybindingService,
@ITelemetryService protected telemetryService: ITelemetryService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IKeybindingService private keybindingService: IKeybindingService,
@ITelemetryService private telemetryService: ITelemetryService,
@INotificationService private notificationService: INotificationService,
@IMenuService protected menuService: IMenuService,
@IMenuService private menuService: IMenuService,
@IQuickOpenService protected quickOpenService: IQuickOpenService,
@IThemeService protected themeService: IThemeService
@IThemeService themeService: IThemeService,
@IExtensionService private extensionService: IExtensionService,
@IConfigurationService protected configurationService: IConfigurationService
) {
super(themeService);
this.stacks = editorGroupService.getStacksModel();
this.mapActionsToEditors = Object.create(null);
this.titleAreaUpdateScheduler = new RunOnceScheduler(() => this.onSchedule(), 0);
this.toUnbind.push(this.titleAreaUpdateScheduler);
this.titleAreaToolbarUpdateScheduler = new RunOnceScheduler(() => this.updateEditorActionsToolbar(), 0);
this.toUnbind.push(this.titleAreaToolbarUpdateScheduler);
this.resourceContext = instantiationService.createInstance(ResourceContextKey);
this.contextMenu = this._register(this.menuService.createMenu(MenuId.EditorTitleContext, this.contextKeyService));
this.contextMenu = this.menuService.createMenu(MenuId.EditorTitleContext, this.contextKeyService);
this.toUnbind.push(this.contextMenu);
this.initActions(this.instantiationService);
this.create(parent);
this.registerListeners();
}
public setDragged(dragged: boolean): void {
this.dragged = dragged;
}
private registerListeners(): void {
this.toUnbind.push(this.stacks.onModelChanged(e => this.onStacksChanged(e)));
this._register(this.extensionService.onDidRegisterExtensions(() => this.updateEditorActionsToolbar()));
}
private onStacksChanged(e: IStacksModelChangeEvent): void {
if (e.structural) {
this.updateSplitActionEnablement();
protected abstract create(parent: HTMLElement): void;
protected createBreadcrumbsControl(container: HTMLElement, options: IBreadcrumbsControlOptions): void {
const config = this._register(BreadcrumbsConfig.IsEnabled.bindTo(this.configurationService));
config.onDidChange(value => {
if (!value && this.breadcrumbsControl) {
this.breadcrumbsControl.dispose();
this.breadcrumbsControl = undefined;
this.group.relayout();
} else if (value && !this.breadcrumbsControl) {
this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group);
this.breadcrumbsControl.update();
this.group.relayout();
}
});
if (config.value) {
this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group);
}
}
private updateSplitActionEnablement(): void {
if (!this.context) {
return;
}
const groupCount = this.stacks.groups.length;
// Split editor
this.splitEditorAction.enabled = groupCount < 3;
}
protected updateStyles(): void {
super.updateStyles();
this.update(true); // run an update when the theme changes to new styles
}
private onSchedule(): void {
if (this.refreshScheduled) {
this.doRefresh();
} else {
this.doUpdate();
}
this.refreshScheduled = false;
}
public setContext(group: IEditorGroup): void {
this.context = group;
this.editorActionsToolbar.context = { groupId: group ? group.id : void 0 } as IEditorCommandsContext;
}
public hasContext(): boolean {
return !!this.context;
}
public update(instant?: boolean): void {
if (instant) {
this.titleAreaUpdateScheduler.cancel();
this.onSchedule();
} else {
this.titleAreaUpdateScheduler.schedule();
}
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
}
public refresh(instant?: boolean) {
this.refreshScheduled = true;
if (instant) {
this.titleAreaUpdateScheduler.cancel();
this.onSchedule();
} else {
this.titleAreaUpdateScheduler.schedule();
}
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
}
public create(parent: HTMLElement): void {
this.parent = parent;
}
public getContainer(): HTMLElement {
return this.parent;
}
protected abstract doRefresh(): void;
protected doUpdate(): void {
this.doRefresh();
}
public layout(dimension: Dimension): void {
// Subclasses can opt in to react on layout
}
public allowDragging(element: HTMLElement): boolean {
return !findParentWithClass(element, 'monaco-action-bar', 'one-editor-silo');
}
protected initActions(services: IInstantiationService): void {
this.closeOneEditorAction = services.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL);
this.splitEditorAction = services.createInstance(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL);
}
protected createEditorActionsToolBar(container: HTMLElement): void {
this.editorActionsToolbar = new ToolBar(container, this.contextMenuService, {
actionItemProvider: (action: Action) => this.actionItemProvider(action),
const context = { groupId: this.group.id } as IEditorCommandsContext;
this.editorActionsToolbar = this._register(new ToolBar(container, this.contextMenuService, {
actionItemProvider: action => this.actionItemProvider(action as Action),
orientation: ActionsOrientation.HORIZONTAL,
ariaLabel: nls.localize('araLabelEditorActions', "Editor actions"),
getKeyBinding: (action) => this.getKeybinding(action)
});
ariaLabel: localize('araLabelEditorActions', "Editor actions"),
getKeyBinding: action => this.getKeybinding(action),
actionRunner: this._register(new EditorCommandsContextActionRunner(context))
}));
// Context
this.editorActionsToolbar.context = context;
// Action Run Handling
this.toUnbind.push(this.editorActionsToolbar.actionRunner.onDidRun((e: IRunEvent) => {
this._register(this.editorActionsToolbar.actionRunner.onDidRun((e: IRunEvent) => {
// Check for Error
if (e.error && !errors.isPromiseCanceledError(e.error)) {
this.notificationService.error(e.error);
}
// Notify for Error
this.notificationService.error(e.error);
// Log in telemetry
if (this.telemetryService) {
@@ -247,20 +146,13 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
}));
}
protected actionItemProvider(action: Action): IActionItem {
if (!this.context) {
return null;
}
const group = this.context;
const position = this.stacks.positionOfGroup(group);
const editor = this.editorService.getVisibleEditors()[position];
let actionItem: IActionItem;
private actionItemProvider(action: Action): IActionItem {
const activeControl = this.group.activeControl;
// Check Active Editor
if (editor instanceof BaseEditor) {
actionItem = editor.getActionItem(action);
let actionItem: IActionItem;
if (activeControl instanceof BaseEditor) {
actionItem = activeControl.getActionItem(action);
}
// Check extensions
@@ -271,86 +163,13 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
return actionItem;
}
protected getEditorActions(identifier: IEditorIdentifier): IToolbarActions {
const primary: IAction[] = [];
const secondary: IAction[] = [];
const { group } = identifier;
const position = this.stacks.positionOfGroup(group);
// Update the resource context
this.resourceContext.set(group && toResource(group.activeEditor, { supportSideBySide: true }));
// Editor actions require the editor control to be there, so we retrieve it via service
const control = this.editorService.getVisibleEditors()[position];
if (control instanceof BaseEditor && control.input && typeof control.position === 'number') {
// Editor Control Actions
let editorActions = this.mapActionsToEditors[control.getId()];
if (!editorActions) {
editorActions = { primary: control.getActions(), secondary: control.getSecondaryActions() };
this.mapActionsToEditors[control.getId()] = editorActions;
}
primary.push(...editorActions.primary);
secondary.push(...editorActions.secondary);
// MenuItems
// TODO This isn't very proper but needed as we have failed to
// use the correct context key service per editor only once. Don't
// take this code as sample of how to work with menus
this.disposeOnEditorActions = dispose(this.disposeOnEditorActions);
const widget = control.getControl();
const codeEditor = isCodeEditor(widget) && widget || isDiffEditor(widget) && widget.getModifiedEditor();
const scopedContextKeyService = codeEditor && codeEditor.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
this.disposeOnEditorActions.push(titleBarMenu, titleBarMenu.onDidChange(_ => {
// schedule the update for the title area toolbar only if no other
// update to the title area is scheduled which will always also
// update the toolbar
if (!this.titleAreaUpdateScheduler.isScheduled()) {
this.titleAreaToolbarUpdateScheduler.schedule();
}
}));
fillInActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary }, this.contextMenuService);
}
return { primary, secondary };
}
public updateEditorActionsToolbar(): void {
const group = this.context;
if (!group) {
return;
}
const editor = group && group.activeEditor;
const isActive = this.stacks.isActive(group);
protected updateEditorActionsToolbar(): void {
// Update Editor Actions Toolbar
let primaryEditorActions: IAction[] = [];
let secondaryEditorActions: IAction[] = [];
const editorActions = this.getEditorActions({ group, editor });
// Primary actions only for the active group
if (isActive) {
primaryEditorActions = prepareActions(editorActions.primary);
if (editor instanceof EditorInput && editor.supportsSplitEditor()) {
this.updateSplitActionEnablement();
primaryEditorActions.push(this.splitEditorAction);
}
}
secondaryEditorActions = prepareActions(editorActions.secondary);
const tabOptions = this.editorGroupService.getTabOptions();
const { primaryEditorActions, secondaryEditorActions } = this.prepareEditorActions(this.getEditorActions());
// Only update if something actually has changed
const primaryEditorActionIds = primaryEditorActions.map(a => a.id);
if (!tabOptions.showTabs) {
primaryEditorActionIds.push(this.closeOneEditorAction.id); // always show "Close" when tabs are disabled
}
const secondaryEditorActionIds = secondaryEditorActions.map(a => a.id);
if (
!arrays.equals(primaryEditorActionIds, this.currentPrimaryEditorActionIds) ||
@@ -360,15 +179,66 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
) {
this.editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)();
if (!tabOptions.showTabs) {
this.editorActionsToolbar.addPrimaryAction(this.closeOneEditorAction)();
}
this.currentPrimaryEditorActionIds = primaryEditorActionIds;
this.currentSecondaryEditorActionIds = secondaryEditorActionIds;
}
}
protected prepareEditorActions(editorActions: IToolbarActions): { primaryEditorActions: IAction[]; secondaryEditorActions: IAction[]; } {
let primaryEditorActions: IAction[];
let secondaryEditorActions: IAction[];
// Primary actions only for the active group
if (this.accessor.activeGroup === this.group) {
primaryEditorActions = prepareActions(editorActions.primary);
} else {
primaryEditorActions = [];
}
// Secondary actions for all groups
secondaryEditorActions = prepareActions(editorActions.secondary);
return { primaryEditorActions, secondaryEditorActions };
}
private getEditorActions(): IToolbarActions {
const primary: IAction[] = [];
const secondary: IAction[] = [];
// Dispose previous listeners
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
// Update the resource context
this.resourceContext.set(toResource(this.group.activeEditor, { supportSideBySide: true }));
// 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);
this.editorToolBarMenuDisposables.push(titleBarMenu);
this.editorToolBarMenuDisposables.push(titleBarMenu.onDidChange(() => {
this.updateEditorActionsToolbar(); // Update editor toolbar whenever contributed actions change
}));
fillInActionBarActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary });
}
return { primary, secondary };
}
protected clearEditorActionsToolbar(): void {
this.editorActionsToolbar.setActions([], [])();
@@ -376,11 +246,46 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
this.currentSecondaryEditorActionIds = [];
}
protected onContextMenu(identifier: IEditorIdentifier, e: Event, node: HTMLElement): void {
protected enableGroupDragging(element: HTMLElement): void {
// Drag start
this._register(addDisposableListener(element, EventType.DRAG_START, (e: DragEvent) => {
if (e.target !== element) {
return; // only if originating from tabs container
}
// Set editor group as transfer
this.groupTransfer.setData([new DraggedEditorGroupIdentifier(this.group.id)], DraggedEditorGroupIdentifier.prototype);
e.dataTransfer.effectAllowed = 'copyMove';
// If tabs are disabled, treat dragging as if an editor tab was dragged
if (!this.accessor.partOptions.showTabs) {
const resource = toResource(this.group.activeEditor, { supportSideBySide: true });
if (resource) {
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
}
}
// Drag Image
let label = this.group.activeEditor.getName();
if (this.accessor.partOptions.showTabs && this.group.count > 1) {
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
}
applyDragImage(e, label, 'monaco-editor-group-drag-image');
}));
// Drag end
this._register(addDisposableListener(element, EventType.DRAG_END, () => {
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
}));
}
protected onContextMenu(editor: IEditorInput, e: Event, node: HTMLElement): void {
// Update the resource context
const currentContext = this.resourceContext.get();
this.resourceContext.set(toResource(identifier.editor, { supportSideBySide: true }));
this.resourceContext.set(toResource(editor, { supportSideBySide: true }));
// Find target anchor
let anchor: HTMLElement | { x: number, y: number } = node;
@@ -391,24 +296,21 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
// Fill in contributed actions
const actions: IAction[] = [];
fillInActions(this.contextMenu, { shouldForwardArgs: true, arg: this.resourceContext.get() }, actions, this.contextMenuService);
fillInContextMenuActions(this.contextMenu, { shouldForwardArgs: true, arg: this.resourceContext.get() }, actions, this.contextMenuService);
// Show it
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(actions),
getActionsContext: () => ({ groupId: identifier.group.id, editorIndex: identifier.group.indexOf(identifier.editor) } as IEditorCommandsContext),
getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) } as IEditorCommandsContext),
getKeyBinding: (action) => this.getKeybinding(action),
onHide: (cancel) => {
onHide: () => {
// restore previous context
this.resourceContext.set(currentContext);
// restore focus to active editor if any
const editor = this.editorService.getActiveEditor();
if (editor) {
editor.focus();
}
// restore focus to active group
this.accessor.activeGroup.focus();
}
});
}
@@ -423,18 +325,61 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
return keybinding ? keybinding.getLabel() : void 0;
}
public dispose(): void {
super.dispose();
//#region ITitleAreaControl
// Actions
[
this.splitEditorAction,
this.closeOneEditorAction
].forEach((action) => {
action.dispose();
});
abstract openEditor(editor: IEditorInput): void;
// Toolbar
this.editorActionsToolbar.dispose();
abstract closeEditor(editor: IEditorInput): void;
abstract closeEditors(editors: IEditorInput[]): void;
abstract closeAllEditors(): void;
abstract moveEditor(editor: IEditorInput, fromIndex: number, targetIndex: number): void;
abstract pinEditor(editor: IEditorInput): void;
abstract setActive(isActive: boolean): void;
abstract updateEditorLabel(editor: IEditorInput): void;
abstract updateEditorDirty(editor: IEditorInput): void;
abstract updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void;
abstract updateStyles(): void;
layout(dimension: Dimension): void {
// Optionally implemented in subclasses
if (this.breadcrumbsControl) {
this.breadcrumbsControl.layout(undefined);
}
}
getPreferredHeight(): number {
return EDITOR_TITLE_HEIGHT + (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden() ? BreadcrumbsControl.HEIGHT : 0);
}
dispose(): void {
this.breadcrumbsControl = dispose(this.breadcrumbsControl);
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
super.dispose();
}
//#endregion
}
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
// Drag Feedback
const dragImageBackground = theme.getColor(listActiveSelectionBackground);
const dragImageForeground = theme.getColor(listActiveSelectionForeground);
collector.addRule(`
.monaco-editor-group-drag-image {
background: ${dragImageBackground};
color: ${dragImageForeground};
}
`);
});