editors - introduce aggregated editors change event and adopt (fix #123864)
This commit is contained in:
parent
967e3b1509
commit
4b254e6288
|
@ -3,12 +3,12 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtHostContext, IExtHostEditorTabsShape, IExtHostContext, MainContext, IEditorTabDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { EditorResourceAccessor, Verbosity } from 'vs/workbench/common/editor';
|
||||
import { GroupChangeKind, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export interface ITabInfo {
|
||||
|
@ -19,10 +19,7 @@ export interface ITabInfo {
|
|||
@extHostNamedCustomer(MainContext.MainThreadEditorTabs)
|
||||
export class MainThreadEditorTabs {
|
||||
|
||||
private static _GroupEventFilter = new Set([GroupChangeKind.EDITOR_CLOSE, GroupChangeKind.EDITOR_OPEN]);
|
||||
|
||||
private readonly _dispoables = new DisposableStore();
|
||||
private readonly _groups = new Map<IEditorGroup, IDisposable>();
|
||||
private readonly _proxy: IExtHostEditorTabsShape;
|
||||
|
||||
constructor(
|
||||
|
@ -33,35 +30,14 @@ export class MainThreadEditorTabs {
|
|||
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditorTabs);
|
||||
|
||||
this._editorGroupsService.whenReady.then(() => this._editorGroupsService.groups.forEach(this._subscribeToGroup, this));
|
||||
this._dispoables.add(_editorGroupsService.onDidAddGroup(this._subscribeToGroup, this));
|
||||
this._dispoables.add(_editorGroupsService.onDidRemoveGroup(e => {
|
||||
const subscription = this._groups.get(e);
|
||||
if (subscription) {
|
||||
subscription.dispose();
|
||||
this._groups.delete(e);
|
||||
this._pushEditorTabs();
|
||||
}
|
||||
}));
|
||||
this._dispoables.add(editorService.onDidActiveEditorChange(this._pushEditorTabs, this));
|
||||
this._pushEditorTabs();
|
||||
this._dispoables.add(editorService.onDidEditorsChange(this._pushEditorTabs, this));
|
||||
this._editorGroupsService.whenReady.then(() => this._pushEditorTabs());
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._groups.values());
|
||||
this._dispoables.dispose();
|
||||
}
|
||||
|
||||
private _subscribeToGroup(group: IEditorGroup) {
|
||||
this._groups.get(group)?.dispose();
|
||||
const listener = group.onDidGroupChange(e => {
|
||||
if (MainThreadEditorTabs._GroupEventFilter.has(e.kind)) {
|
||||
this._pushEditorTabs();
|
||||
}
|
||||
});
|
||||
this._groups.set(group, listener);
|
||||
}
|
||||
|
||||
private _pushEditorTabs(): void {
|
||||
const tabs: IEditorTabDto[] = [];
|
||||
for (const group of this._editorGroupsService.groups) {
|
||||
|
|
|
@ -14,7 +14,7 @@ import { ResourceMap } from 'vs/base/common/map';
|
|||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
import { IFileService, FileOperationEvent, FileOperation, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter, DebounceEmitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { basename, joinPath } from 'vs/base/common/resources';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
|
@ -54,6 +54,9 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
private readonly _onDidVisibleEditorsChange = this._register(new Emitter<void>());
|
||||
readonly onDidVisibleEditorsChange = this._onDidVisibleEditorsChange.event;
|
||||
|
||||
private readonly _onDidEditorsChange = this._register(new DebounceEmitter<void>({ delay: 0, merge: () => undefined }));
|
||||
readonly onDidEditorsChange = this._onDidEditorsChange.event;
|
||||
|
||||
private readonly _onDidCloseEditor = this._register(new Emitter<IEditorCloseEvent>());
|
||||
readonly onDidCloseEditor = this._onDidCloseEditor.event;
|
||||
|
||||
|
@ -97,6 +100,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
this.editorGroupService.whenReady.then(() => this.onEditorGroupsReady());
|
||||
this.editorGroupService.onDidChangeActiveGroup(group => this.handleActiveEditorChange(group));
|
||||
this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView));
|
||||
this.editorGroupService.onDidMoveGroup(group => this.handleGroupMove(group));
|
||||
this.editorsObserver.onDidMostRecentlyActiveEditorsChange(() => this._onDidMostRecentlyActiveEditorsChange.fire());
|
||||
|
||||
// Out of workspace file watchers
|
||||
|
@ -148,6 +152,14 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
}
|
||||
}
|
||||
|
||||
private handleGroupMove(group: IEditorGroup): void {
|
||||
if (group.isEmpty) {
|
||||
return; // empty groups do not change structure of editors
|
||||
}
|
||||
|
||||
this._onDidEditorsChange.fire();
|
||||
}
|
||||
|
||||
private handleActiveEditorChange(group: IEditorGroup): void {
|
||||
if (group !== this.editorGroupService.activeGroup) {
|
||||
return; // ignore if not the active group
|
||||
|
@ -168,15 +180,23 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
|
||||
// Fire event to outside parties
|
||||
this._onDidActiveEditorChange.fire();
|
||||
this._onDidEditorsChange.fire();
|
||||
}
|
||||
|
||||
private registerGroupListeners(group: IEditorGroupView): void {
|
||||
const groupDisposables = new DisposableStore();
|
||||
|
||||
groupDisposables.add(group.onDidGroupChange(e => {
|
||||
if (e.kind === GroupChangeKind.EDITOR_ACTIVE) {
|
||||
this.handleActiveEditorChange(group);
|
||||
this._onDidVisibleEditorsChange.fire();
|
||||
switch (e.kind) {
|
||||
case GroupChangeKind.EDITOR_ACTIVE:
|
||||
this.handleActiveEditorChange(group);
|
||||
this._onDidVisibleEditorsChange.fire();
|
||||
break;
|
||||
case GroupChangeKind.EDITOR_CLOSE:
|
||||
case GroupChangeKind.EDITOR_OPEN:
|
||||
case GroupChangeKind.EDITOR_MOVE:
|
||||
this._onDidEditorsChange.fire();
|
||||
break;
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -97,6 +97,16 @@ export interface IEditorService {
|
|||
*/
|
||||
readonly onDidVisibleEditorsChange: Event<void>;
|
||||
|
||||
/**
|
||||
* An aggregated event for a set of editor related events
|
||||
* across all editor groups:
|
||||
* - active editor changes
|
||||
* - editors opening/closing
|
||||
* - editors moving
|
||||
* - groups moving (unless they are empty)
|
||||
*/
|
||||
readonly onDidEditorsChange: Event<void>;
|
||||
|
||||
/**
|
||||
* Emitted when an editor is closed.
|
||||
*/
|
||||
|
|
|
@ -1858,6 +1858,69 @@ suite('EditorService', () => {
|
|||
visibleEditorChangeListener.dispose();
|
||||
});
|
||||
|
||||
test('editors change event', async function () {
|
||||
const [part, service] = await createEditorService();
|
||||
const rootGroup = part.activeGroup;
|
||||
|
||||
let input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID);
|
||||
let otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID);
|
||||
|
||||
let editorsChangeEventCounter = 0;
|
||||
async function assertEditorsChangeEvent(expected: number) {
|
||||
await Event.toPromise(service.onDidEditorsChange);
|
||||
editorsChangeEventCounter++;
|
||||
|
||||
assert.strictEqual(editorsChangeEventCounter, expected);
|
||||
}
|
||||
|
||||
// open
|
||||
let p: Promise<unknown> = service.openEditor(input, { pinned: true });
|
||||
await assertEditorsChangeEvent(1);
|
||||
await p;
|
||||
|
||||
// open (other)
|
||||
p = service.openEditor(otherInput, { pinned: true });
|
||||
await assertEditorsChangeEvent(2);
|
||||
await p;
|
||||
|
||||
// close (inactive)
|
||||
p = rootGroup.closeEditor(input);
|
||||
await assertEditorsChangeEvent(3);
|
||||
await p;
|
||||
|
||||
// close (active)
|
||||
p = rootGroup.closeEditor(otherInput);
|
||||
await assertEditorsChangeEvent(4);
|
||||
await p;
|
||||
|
||||
input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID);
|
||||
otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID);
|
||||
|
||||
// open editors
|
||||
p = service.openEditors([{ editor: input, options: { pinned: true } }, { editor: otherInput, options: { pinned: true } }]);
|
||||
await assertEditorsChangeEvent(5);
|
||||
await p;
|
||||
|
||||
// active editor change
|
||||
p = service.openEditor(otherInput);
|
||||
await assertEditorsChangeEvent(6);
|
||||
await p;
|
||||
|
||||
// move editor (in group)
|
||||
p = service.openEditor(input, { pinned: true, index: 1 });
|
||||
await assertEditorsChangeEvent(7);
|
||||
await p;
|
||||
|
||||
// move editor (across groups)
|
||||
const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT);
|
||||
rootGroup.moveEditor(input, rightGroup);
|
||||
await assertEditorsChangeEvent(8);
|
||||
|
||||
// move group
|
||||
part.moveGroup(rightGroup, rootGroup, GroupDirection.LEFT);
|
||||
await assertEditorsChangeEvent(9);
|
||||
});
|
||||
|
||||
test('two active editor change events when opening editor to the side', async function () {
|
||||
const [, service] = await createEditorService();
|
||||
|
||||
|
|
|
@ -798,6 +798,7 @@ export class TestEditorService implements EditorServiceImpl {
|
|||
|
||||
onDidActiveEditorChange: Event<void> = Event.None;
|
||||
onDidVisibleEditorsChange: Event<void> = Event.None;
|
||||
onDidEditorsChange: Event<void> = Event.None;
|
||||
onDidCloseEditor: Event<IEditorCloseEvent> = Event.None;
|
||||
onDidOpenEditorFail: Event<IEditorIdentifier> = Event.None;
|
||||
onDidMostRecentlyActiveEditorsChange: Event<void> = Event.None;
|
||||
|
|
Loading…
Reference in a new issue