Merge from vscode 7653d836944892f83ce9e1f95c1204bafa1aec31

This commit is contained in:
ADS Merger
2020-05-08 03:58:34 +00:00
parent dac1970c43
commit fa62ec1f34
209 changed files with 5131 additions and 2480 deletions

View File

@@ -35,6 +35,7 @@ export const ActiveEditorIsReadonlyContext = new RawContextKey<boolean>('activeE
export const ActiveEditorAvailableEditorIdsContext = new RawContextKey<string>('activeEditorAvailableEditorIds', '');
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
export const EditorPinnedContext = new RawContextKey<boolean>('editorPinned', false);
export const EditorStickyContext = new RawContextKey<boolean>('editorSticky', false);
export const EditorGroupActiveEditorDirtyContext = new RawContextKey<boolean>('groupActiveEditorDirty', false);
export const EditorGroupEditorsCountContext = new RawContextKey<number>('groupEditorsCount', 0);
export const NoEditorsVisibleContext = EditorsVisibleContext.toNegated();
@@ -1046,6 +1047,12 @@ export class EditorOptions implements IEditorOptions {
*/
pinned: boolean | undefined;
/**
* An editor that is sticky moves to the beginning of the editors list within the group and will remain
* there unless explicitly closed. Operations such as "Close All" will not close sticky editors.
*/
sticky: boolean | undefined;
/**
* The index in the document stack where to insert the editor into when opening.
*/
@@ -1111,6 +1118,10 @@ export class EditorOptions implements IEditorOptions {
this.pinned = options.pinned;
}
if (typeof options.sticky === 'boolean') {
this.sticky = options.sticky;
}
if (typeof options.inactive === 'boolean') {
this.inactive = options.inactive;
}
@@ -1291,6 +1302,7 @@ export class EditorCommandsContextActionRunner extends ActionRunner {
export interface IEditorCloseEvent extends IEditorIdentifier {
replaced: boolean;
index: number;
sticky: boolean;
}
export type GroupIdentifier = number;

View File

@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, CloseDirection, SideBySideEditorInput, IEditorInput, EditorsOrder } from 'vs/workbench/common/editor';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, IEditorInput, EditorsOrder } from 'vs/workbench/common/editor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { coalesce } from 'vs/base/common/arrays';
@@ -30,6 +30,7 @@ export interface EditorIdentifier extends IEditorIdentifier {
export interface IEditorOpenOptions {
pinned?: boolean;
sticky?: boolean;
active?: boolean;
index?: number;
}
@@ -44,6 +45,7 @@ export interface ISerializedEditorGroup {
editors: ISerializedEditorInput[];
mru: number[];
preview?: number;
sticky?: number;
}
export function isSerializedEditorGroup(obj?: unknown): obj is ISerializedEditorGroup {
@@ -92,6 +94,7 @@ export class EditorGroup extends Disposable {
private preview: EditorInput | null = null; // editor in preview state
private active: EditorInput | null = null; // editor in active state
private sticky: number = -1; // index of first editor in sticky state
private editorOpenPositioning: ('left' | 'right' | 'first' | 'last') | undefined;
private focusRecentEditorAfterClose: boolean | undefined;
@@ -114,10 +117,10 @@ export class EditorGroup extends Disposable {
}
private registerListeners(): void {
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)));
this._register(this.configurationService.onDidChangeConfiguration(() => this.onConfigurationUpdated()));
}
private onConfigurationUpdated(event?: IConfigurationChangeEvent): void {
private onConfigurationUpdated(): void {
this.editorOpenPositioning = this.configurationService.getValue('workbench.editor.openPositioning');
this.focusRecentEditorAfterClose = this.configurationService.getValue('workbench.editor.focusRecentEditorAfterClose');
}
@@ -126,8 +129,25 @@ export class EditorGroup extends Disposable {
return this.editors.length;
}
getEditors(order: EditorsOrder): EditorInput[] {
return order === EditorsOrder.MOST_RECENTLY_ACTIVE ? this.mru.slice(0) : this.editors.slice(0);
get stickyCount(): number {
return this.sticky + 1;
}
getEditors(order: EditorsOrder, options?: { excludeSticky?: boolean }): EditorInput[] {
const editors = order === EditorsOrder.MOST_RECENTLY_ACTIVE ? this.mru.slice(0) : this.editors.slice(0);
if (options?.excludeSticky) {
// MRU: need to check for index on each
if (order === EditorsOrder.MOST_RECENTLY_ACTIVE) {
return editors.filter(editor => !this.isSticky(editor));
}
// Sequential: simply start after sticky index
return editors.slice(this.sticky + 1);
}
return editors;
}
getEditorByIndex(index: number): EditorInput | undefined {
@@ -146,18 +166,15 @@ export class EditorGroup extends Disposable {
return this.preview;
}
isPreview(editor: EditorInput): boolean {
return this.matches(this.preview, editor);
}
openEditor(candidate: EditorInput, options?: IEditorOpenOptions): EditorInput {
const makePinned = options?.pinned;
const makeSticky = options?.sticky || (typeof options?.index === 'number' && this.isSticky(options.index));
const makePinned = options?.pinned || options?.sticky;
const makeActive = options?.active || !this.activeEditor || (!makePinned && this.matches(this.preview, this.activeEditor));
const existingEditor = this.findEditor(candidate);
const existingEditorAndIndex = this.findEditor(candidate);
// New editor
if (!existingEditor) {
if (!existingEditorAndIndex) {
const newEditor = candidate;
const indexOfActive = this.indexOf(this.active);
@@ -170,6 +187,12 @@ export class EditorGroup extends Disposable {
// Insert to the BEGINNING
else if (this.editorOpenPositioning === EditorOpenPositioning.FIRST) {
targetIndex = 0;
// Always make sure targetIndex is after sticky editors
// unless we are explicitly told to make the editor sticky
if (!makeSticky && this.isSticky(targetIndex)) {
targetIndex = this.sticky + 1;
}
}
// Insert to the END
@@ -177,18 +200,38 @@ export class EditorGroup extends Disposable {
targetIndex = this.editors.length;
}
// Insert to the LEFT of active editor
else if (this.editorOpenPositioning === EditorOpenPositioning.LEFT) {
if (indexOfActive === 0 || !this.editors.length) {
targetIndex = 0; // to the left becoming first editor in list
} else {
targetIndex = indexOfActive; // to the left of active editor
// Insert to LEFT or RIGHT of active editor
else {
// Insert to the LEFT of active editor
if (this.editorOpenPositioning === EditorOpenPositioning.LEFT) {
if (indexOfActive === 0 || !this.editors.length) {
targetIndex = 0; // to the left becoming first editor in list
} else {
targetIndex = indexOfActive; // to the left of active editor
}
}
// Insert to the RIGHT of active editor
else {
targetIndex = indexOfActive + 1;
}
// Always make sure targetIndex is after sticky editors
// unless we are explicitly told to make the editor sticky
if (!makeSticky && this.isSticky(targetIndex)) {
targetIndex = this.sticky + 1;
}
}
// Insert to the RIGHT of active editor
else {
targetIndex = indexOfActive + 1;
// If the editor becomes sticky, increment the sticky index and adjust
// the targetIndex to be at the end of sticky editors unless already.
if (makeSticky) {
this.sticky++;
if (!this.isSticky(targetIndex)) {
targetIndex = this.sticky;
}
}
// Insert into our list of editors if pinned or we have no preview editor
@@ -228,6 +271,7 @@ export class EditorGroup extends Disposable {
// Existing editor
else {
const [existingEditor] = existingEditorAndIndex;
// Pin it
if (makePinned) {
@@ -244,6 +288,12 @@ export class EditorGroup extends Disposable {
this.moveEditor(existingEditor, options.index);
}
// Stick it (intentionally after the moveEditor call in case
// the editor was already moved into the sticky range)
if (makeSticky) {
this.doStick(existingEditor, this.indexOf(existingEditor));
}
return existingEditor;
}
}
@@ -252,8 +302,7 @@ export class EditorGroup extends Disposable {
const listeners = new DisposableStore();
// Re-emit disposal of editor input as our own event
const onceDispose = Event.once(editor.onDispose);
listeners.add(onceDispose(() => {
listeners.add(Event.once(editor.onDispose)(() => {
if (this.indexOf(editor) >= 0) {
this._onDidDisposeEditor.fire(editor);
}
@@ -309,6 +358,7 @@ export class EditorGroup extends Disposable {
}
const editor = this.editors[index];
const sticky = this.isSticky(index);
// Active Editor closed
if (openNext && this.matches(this.active, editor)) {
@@ -344,45 +394,18 @@ export class EditorGroup extends Disposable {
this.splice(index, true);
// Event
return { editor, replaced, index, groupId: this.id };
}
closeEditors(except: EditorInput, direction?: CloseDirection): void {
const index = this.indexOf(except);
if (index === -1) {
return; // not found
}
// Close to the left
if (direction === CloseDirection.LEFT) {
for (let i = index - 1; i >= 0; i--) {
this.closeEditor(this.editors[i]);
}
}
// Close to the right
else if (direction === CloseDirection.RIGHT) {
for (let i = this.editors.length - 1; i > index; i--) {
this.closeEditor(this.editors[i]);
}
}
// Both directions
else {
this.mru.filter(e => !this.matches(e, except)).forEach(e => this.closeEditor(e));
}
}
closeAllEditors(): void {
// Optimize: close all non active editors first to produce less upstream work
this.mru.filter(e => !this.matches(e, this.active)).forEach(e => this.closeEditor(e));
if (this.active) {
this.closeEditor(this.active);
}
return { editor, replaced, sticky, index, groupId: this.id };
}
moveEditor(candidate: EditorInput, toIndex: number): EditorInput | undefined {
// Ensure toIndex is in bounds of our model
if (toIndex >= this.editors.length) {
toIndex = this.editors.length - 1;
} else if (toIndex < 0) {
toIndex = 0;
}
const index = this.indexOf(candidate);
if (index < 0 || toIndex === index) {
return undefined; // {{SQL CARBON EDIT}} strict-null-check
@@ -390,6 +413,16 @@ export class EditorGroup extends Disposable {
const editor = this.editors[index];
// Adjust sticky index: editor moved out of sticky state into unsticky state
if (this.isSticky(index) && toIndex > this.sticky) {
this.sticky--;
}
// ...or editor moved into sticky state from unsticky state
else if (!this.isSticky(index) && toIndex <= this.sticky) {
this.sticky++;
}
// Move
this.editors.splice(index, 1);
this.editors.splice(toIndex, 0, editor);
@@ -401,11 +434,13 @@ export class EditorGroup extends Disposable {
}
setActive(candidate: EditorInput): EditorInput | undefined {
const editor = this.findEditor(candidate);
if (!editor) {
const res = this.findEditor(candidate);
if (!res) {
return undefined; // not found {{SQL CARBON EDIT}} strict-null-check
}
const [editor] = res;
this.doSetActive(editor);
return editor;
@@ -428,18 +463,20 @@ export class EditorGroup extends Disposable {
}
pin(candidate: EditorInput): EditorInput | undefined {
const editor = this.findEditor(candidate);
if (!editor) {
const res = this.findEditor(candidate);
if (!res) {
return undefined; // not found {{SQL CARBON EDIT}} strict-null-check
}
const [editor] = res;
this.doPin(editor);
return editor;
}
private doPin(editor: EditorInput): void {
if (!this.isPreview(editor)) {
if (this.isPinned(editor)) {
return; // can only pin a preview editor
}
@@ -451,11 +488,13 @@ export class EditorGroup extends Disposable {
}
unpin(candidate: EditorInput): EditorInput | undefined {
const editor = this.findEditor(candidate);
if (!editor) {
const res = this.findEditor(candidate);
if (!res) {
return undefined; // not found {{SQL CARBON EDIT}} strict-null-check
}
const [editor] = res;
this.doUnpin(editor);
return editor;
@@ -479,33 +518,97 @@ export class EditorGroup extends Disposable {
}
}
isPinned(editor: EditorInput): boolean;
isPinned(index: number): boolean;
isPinned(arg1: EditorInput | number): boolean {
if (!this.preview) {
return true; // no preview editor
}
isPinned(editorOrIndex: EditorInput | number): boolean {
let editor: EditorInput;
let index: number;
if (typeof arg1 === 'number') {
editor = this.editors[arg1];
index = arg1;
if (typeof editorOrIndex === 'number') {
editor = this.editors[editorOrIndex];
} else {
editor = arg1;
index = this.indexOf(editor);
}
if (index === -1 || !editor) {
return false; // editor not found
editor = editorOrIndex;
}
return !this.matches(this.preview, editor);
}
stick(candidate: EditorInput): EditorInput | undefined {
const res = this.findEditor(candidate);
if (!res) {
return undefined; // not found {{SQL CARBON EDIT}} strict-null-check
}
const [editor, index] = res;
this.doStick(editor, index);
return editor;
}
private doStick(editor: EditorInput, index: number): void {
if (this.isSticky(index)) {
return; // can only stick a non-sticky editor
}
// Pin editor
this.pin(editor);
// Move editor to be the last sticky editor
this.moveEditor(editor, this.sticky + 1);
// Adjust sticky index
this.sticky++;
}
unstick(candidate: EditorInput): EditorInput | undefined {
const res = this.findEditor(candidate);
if (!res) {
return undefined; // not found {{SQL CARBON EDIT}} strict-null-check
}
const [editor, index] = res;
this.doUnstick(editor, index);
return editor;
}
private doUnstick(editor: EditorInput, index: number): void {
if (!this.isSticky(index)) {
return; // can only unstick a sticky editor
}
// Move editor to be the first non-sticky editor
this.moveEditor(editor, this.sticky);
// Adjust sticky index
this.sticky--;
}
isSticky(candidateOrIndex: EditorInput | number): boolean {
if (this.sticky < 0) {
return false; // no sticky editor
}
let index: number;
if (typeof candidateOrIndex === 'number') {
index = candidateOrIndex;
} else {
index = this.indexOf(candidateOrIndex);
}
if (index < 0) {
return false;
}
return index <= this.sticky;
}
private splice(index: number, del: boolean, editor?: EditorInput): void {
const editorToDeleteOrReplace = this.editors[index];
// Perform on sticky index
if (del && this.isSticky(index)) {
this.sticky--;
}
// Perform on editors array
if (editor) {
this.editors.splice(index, del ? 1 : 0, editor);
@@ -513,35 +616,38 @@ export class EditorGroup extends Disposable {
this.editors.splice(index, del ? 1 : 0);
}
// Add
if (!del && editor) {
if (this.mru.length === 0) {
// the list of most recent editors is empty
// so this editor can only be the most recent
this.mru.push(editor);
} else {
// we have most recent editors. as such we
// put this newly opened editor right after
// the current most recent one because it cannot
// be the most recently active one unless
// it becomes active. but it is still more
// active then any other editor in the list.
this.mru.splice(1, 0, editor);
}
}
// Remove / Replace
else {
const indexInMRU = this.indexOf(editorToDeleteOrReplace, this.mru);
// Remove
if (del && !editor) {
this.mru.splice(indexInMRU, 1); // remove from MRU
// Perform on MRU
{
// Add
if (!del && editor) {
if (this.mru.length === 0) {
// the list of most recent editors is empty
// so this editor can only be the most recent
this.mru.push(editor);
} else {
// we have most recent editors. as such we
// put this newly opened editor right after
// the current most recent one because it cannot
// be the most recently active one unless
// it becomes active. but it is still more
// active then any other editor in the list.
this.mru.splice(1, 0, editor);
}
}
// Replace
else if (del && editor) {
this.mru.splice(indexInMRU, 1, editor); // replace MRU at location
// Remove / Replace
else {
const indexInMRU = this.indexOf(editorToDeleteOrReplace, this.mru);
// Remove
if (del && !editor) {
this.mru.splice(indexInMRU, 1); // remove from MRU
}
// Replace
else if (del && editor) {
this.mru.splice(indexInMRU, 1, editor); // replace MRU at location
}
}
}
}
@@ -560,13 +666,13 @@ export class EditorGroup extends Disposable {
return -1;
}
private findEditor(candidate: EditorInput | null): EditorInput | undefined {
private findEditor(candidate: EditorInput | null): [EditorInput, number /* index */] | undefined {
const index = this.indexOf(candidate, this.editors);
if (index === -1) {
return undefined;
}
return this.editors[index];
return [this.editors[index], index];
}
contains(candidate: EditorInput, searchInSideBySideEditors?: boolean): boolean {
@@ -599,7 +705,7 @@ export class EditorGroup extends Disposable {
group.mru = this.mru.slice(0);
group.preview = this.preview;
group.active = this.active;
group.editorOpenPositioning = this.editorOpenPositioning;
group.sticky = this.sticky;
return group;
}
@@ -609,32 +715,52 @@ export class EditorGroup extends Disposable {
// Serialize all editor inputs so that we can store them.
// Editors that cannot be serialized need to be ignored
// from mru, active and preview if any.
// from mru, active, preview and sticky if any.
let serializableEditors: EditorInput[] = [];
let serializedEditors: ISerializedEditorInput[] = [];
let serializablePreviewIndex: number | undefined;
this.editors.forEach(e => {
const factory = registry.getEditorInputFactory(e.getTypeId());
if (factory) {
const value = factory.serialize(e);
if (typeof value === 'string') {
serializedEditors.push({ id: e.getTypeId(), value });
serializableEditors.push(e);
let serializableSticky = this.sticky;
if (this.preview === e) {
for (let i = 0; i < this.editors.length; i++) {
const editor = this.editors[i];
let canSerializeEditor = false;
const factory = registry.getEditorInputFactory(editor.getTypeId());
if (factory) {
const value = factory.serialize(editor);
// Editor can be serialized
if (typeof value === 'string') {
canSerializeEditor = true;
serializedEditors.push({ id: editor.getTypeId(), value });
serializableEditors.push(editor);
if (this.preview === editor) {
serializablePreviewIndex = serializableEditors.length - 1;
}
}
}
});
const serializableMru = this.mru.map(e => this.indexOf(e, serializableEditors)).filter(i => i >= 0);
// Editor cannot be serialized
else {
canSerializeEditor = false;
}
}
// Adjust index of sticky editors if the editor cannot be serialized and is pinned
if (!canSerializeEditor && this.isSticky(i)) {
serializableSticky--;
}
}
const serializableMru = this.mru.map(editor => this.indexOf(editor, serializableEditors)).filter(i => i >= 0);
return {
id: this.id,
editors: serializedEditors,
mru: serializableMru,
preview: serializablePreviewIndex,
sticky: serializableSticky >= 0 ? serializableSticky : undefined
};
}
@@ -649,18 +775,22 @@ export class EditorGroup extends Disposable {
this._id = EditorGroup.IDS++; // backwards compatibility
}
this.editors = coalesce(data.editors.map(e => {
this.editors = coalesce(data.editors.map((e, index) => {
let editor: EditorInput | undefined = undefined;
const factory = registry.getEditorInputFactory(e.id);
if (factory) {
const editor = doHandleUpgrade(factory.deserialize(this.instantiationService, e.value)); // {{SQL CARBON EDIT}} handle upgrade path to new serialization
editor = doHandleUpgrade(factory.deserialize(this.instantiationService, e.value)); // {{SQL CARBON EDIT}} handle upgrade path to new serialization
if (editor) {
this.registerEditorListeners(editor);
}
return editor;
}
return null;
if (!editor && typeof data.sticky === 'number' && index <= data.sticky) {
data.sticky--; // if editor cannot be deserialized but was sticky, we need to decrease sticky index
}
return editor;
}));
this.mru = coalesce(data.mru.map(i => this.editors[i]));
@@ -671,6 +801,10 @@ export class EditorGroup extends Disposable {
this.preview = this.editors[data.preview];
}
if (typeof data.sticky === 'number') {
this.sticky = data.sticky;
}
return this._id;
}
}

View File

@@ -282,7 +282,7 @@ export interface IViewContentDescriptor {
export interface IViewsRegistry {
readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>;
readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }[]>;
readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>;
@@ -290,6 +290,8 @@ export interface IViewsRegistry {
registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void;
registerViews2(views: { views: IViewDescriptor[], viewContainer: ViewContainer }[]): void;
deregisterViews(views: IViewDescriptor[], viewContainer: ViewContainer): void;
moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void;
@@ -319,8 +321,8 @@ function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewConten
class ViewsRegistry extends Disposable implements IViewsRegistry {
private readonly _onViewsRegistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>());
readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsRegistered.event;
private readonly _onViewsRegistered = this._register(new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }[]>());
readonly onViewsRegistered = this._onViewsRegistered.event;
private readonly _onViewsDeregistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>());
readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsDeregistered.event;
@@ -336,8 +338,12 @@ class ViewsRegistry extends Disposable implements IViewsRegistry {
private _viewWelcomeContents = new SetMap<string, IViewContentDescriptor>();
registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void {
this.addViews(views, viewContainer);
this._onViewsRegistered.fire({ views: views, viewContainer });
this.registerViews2([{ views, viewContainer }]);
}
registerViews2(views: { views: IViewDescriptor[], viewContainer: ViewContainer }[]): void {
views.forEach(({ views, viewContainer }) => this.addViews(views, viewContainer));
this._onViewsRegistered.fire(views);
}
deregisterViews(viewDescriptors: IViewDescriptor[], viewContainer: ViewContainer): void {