editors - remove delegating editor service

This commit is contained in:
Benjamin Pasero 2021-06-16 08:36:20 +02:00
parent 5cfa24eba1
commit 9f59e87b66
No known key found for this signature in database
GPG key ID: E6380CC4C8219E65
4 changed files with 44 additions and 157 deletions

View file

@ -5,7 +5,7 @@
import 'vs/css!./media/explorerviewlet';
import { localize } from 'vs/nls';
import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, VIEW_ID } from 'vs/workbench/contrib/files/common/files';
import { VIEWLET_ID, ExplorerViewletVisibleContext, OpenEditorsVisibleContext, VIEW_ID, IFilesConfiguration } from 'vs/workbench/contrib/files/common/files';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { ExplorerView } from 'vs/workbench/contrib/files/browser/views/explorerView';
@ -16,7 +16,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService, ViewContentGroups } from 'vs/workbench/common/views';
@ -24,10 +23,6 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { Disposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { DelegatingEditorService } from 'vs/workbench/services/editor/browser/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorPane } from 'vs/workbench/common/editor';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
@ -175,7 +170,6 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer {
@ITelemetryService telemetryService: ITelemetryService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService,
@IContextKeyService contextKeyService: IContextKeyService,
@ -199,53 +193,54 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer {
protected override createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane {
if (viewDescriptor.id === VIEW_ID) {
// Create a delegating editor service for the explorer to be able to delay the refresh in the opened
// editors view above. This is a workaround for being able to double click on a file to make it pinned
// without causing the animation in the opened editors view to kick in and change scroll position.
// We try to be smart and only use the delay if we recognize that the user action is likely to cause
// a new entry in the opened editors view.
const delegatingEditorService = this.instantiationService.createInstance(DelegatingEditorService, async (group, delegate): Promise<IEditorPane | undefined> => {
let openEditorsView = this.getOpenEditorsView();
if (openEditorsView) {
let delay = 0;
const config = this.configurationService.getValue<IFilesConfiguration>();
const delayEditorOpeningInOpenedEditors = !!config.workbench?.editor?.enablePreview; // No need to delay if preview is disabled
const activeGroup = this.editorGroupService.activeGroup;
if (delayEditorOpeningInOpenedEditors && group === activeGroup && !activeGroup.previewEditor) {
delay = 250; // a new editor entry is likely because there is either no group or no preview in group
return this.instantiationService.createInstance(ExplorerView, options, {
willOpenElement: e => {
if (!(e instanceof MouseEvent)) {
return; // only delay when user clicks
}
openEditorsView.setStructuralRefreshDelay(delay);
}
const openEditorsView = this.getOpenEditorsView();
if (openEditorsView) {
let delay = 0;
try {
return await delegate();
} catch (error) {
return undefined; // ignore
} finally {
const config = this.configurationService.getValue<IFilesConfiguration>();
if (!!config.workbench?.editor?.enablePreview) {
// delay open editors view when preview is enabled
// to accomodate for the user doing a double click
// to pin the editor.
// without this delay a double click would be not
// possible because the next element would move
// under the mouse after the first click.
delay = 250;
}
openEditorsView.setStructuralRefreshDelay(delay);
}
},
didOpenElement: e => {
if (!(e instanceof MouseEvent)) {
return; // only delay when user clicks
}
const openEditorsView = this.getOpenEditorsView();
if (openEditorsView) {
openEditorsView.setStructuralRefreshDelay(0);
}
}
});
const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IEditorService, delegatingEditorService]));
return explorerInstantiator.createInstance(ExplorerView, options);
}
return super.createView(viewDescriptor, options);
}
public getExplorerView(): ExplorerView {
getExplorerView(): ExplorerView {
return <ExplorerView>this.getView(VIEW_ID);
}
public getOpenEditorsView(): OpenEditorsView {
getOpenEditorsView(): OpenEditorsView {
return <OpenEditorsView>this.getView(OpenEditorsView.ID);
}
public override setVisible(visible: boolean): void {
override setVisible(visible: boolean): void {
this.viewletVisibleContextKey.set(visible);
super.setVisible(visible);
}

View file

@ -131,6 +131,11 @@ export function getContext(focus: ExplorerItem[], selection: ExplorerItem[], res
return [focusedStat];
}
export interface IExplorerViewContainerDelegate {
willOpenElement(event?: UIEvent): void;
didOpenElement(event?: UIEvent): void;
}
export class ExplorerView extends ViewPane {
static readonly TREE_VIEW_STATE_STORAGE_KEY: string = 'workbench.explorer.treeViewState';
@ -164,6 +169,7 @@ export class ExplorerView extends ViewPane {
constructor(
options: IViewPaneOptions,
private readonly delegate: IExplorerViewContainerDelegate,
@IContextMenuService contextMenuService: IContextMenuService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IInstantiationService instantiationService: IInstantiationService,
@ -439,7 +445,12 @@ export class ExplorerView extends ViewPane {
return;
}
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' });
await this.editorService.openEditor({ resource: element.resource, options: { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned } }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
try {
this.delegate.willOpenElement(e.browserEvent);
await this.editorService.openEditor({ resource: element.resource, options: { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned } }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
} finally {
this.delegate.didOpenElement();
}
}
}));

View file

@ -1290,88 +1290,4 @@ export class EditorService extends Disposable implements EditorServiceImpl {
}
}
export interface IEditorOpenHandler {
(
group: IEditorGroup,
delegate: () => Promise<IEditorPane | undefined>,
): Promise<IEditorPane | undefined>;
}
/**
* The delegating workbench editor service can be used to override the behaviour of the openEditor()
* method by providing a IEditorOpenHandler. All calls are being delegated to the existing editor
* service otherwise.
*/
export class DelegatingEditorService implements IEditorService {
declare readonly _serviceBrand: undefined;
constructor(
private editorOpenHandler: IEditorOpenHandler,
@IEditorService private editorService: EditorService
) { }
openEditor(editor: IEditorInput, options?: IEditorOptions, group?: OpenInEditorGroup): Promise<IEditorPane | undefined>;
openEditor(editor: IResourceEditorInput | IUntitledTextResourceEditorInput, group?: OpenInEditorGroup): Promise<ITextEditorPane | undefined>;
openEditor(editor: IResourceDiffEditorInput, group?: OpenInEditorGroup): Promise<ITextDiffEditorPane | undefined>;
async openEditor(editor: IEditorInput | IResourceEditorInputType, optionsOrGroup?: IEditorOptions | OpenInEditorGroup, group?: OpenInEditorGroup): Promise<IEditorPane | undefined> {
const result = this.editorService.doResolveEditorOpenRequest(editor, optionsOrGroup, group);
if (result) {
const [resolvedGroup, resolvedEditor, resolvedOptions] = result;
return this.editorOpenHandler(resolvedGroup, () => this.editorService.openEditor(resolvedEditor, resolvedOptions, resolvedGroup));
}
return undefined;
}
//#region Delegate to IEditorService
get onDidActiveEditorChange(): Event<void> { return this.editorService.onDidActiveEditorChange; }
get onDidVisibleEditorsChange(): Event<void> { return this.editorService.onDidVisibleEditorsChange; }
get onDidCloseEditor(): Event<IEditorCloseEvent> { return this.editorService.onDidCloseEditor; }
get activeEditor(): IEditorInput | undefined { return this.editorService.activeEditor; }
get activeEditorPane(): IVisibleEditorPane | undefined { return this.editorService.activeEditorPane; }
get activeTextEditorControl(): ICodeEditor | IDiffEditor | undefined { return this.editorService.activeTextEditorControl; }
get activeTextEditorMode(): string | undefined { return this.editorService.activeTextEditorMode; }
get visibleEditors(): readonly IEditorInput[] { return this.editorService.visibleEditors; }
get visibleEditorPanes(): readonly IVisibleEditorPane[] { return this.editorService.visibleEditorPanes; }
get visibleTextEditorControls(): readonly (ICodeEditor | IDiffEditor)[] { return this.editorService.visibleTextEditorControls; }
get editors(): readonly IEditorInput[] { return this.editorService.editors; }
get count(): number { return this.editorService.count; }
getEditors(order: EditorsOrder, options?: { excludeSticky?: boolean }): readonly IEditorIdentifier[] { return this.editorService.getEditors(order, options); }
openEditors(editors: IEditorInputWithOptions[], group?: OpenInEditorGroup, options?: IOpenEditorsOptions): Promise<IEditorPane[]>;
openEditors(editors: IResourceEditorInputType[], group?: OpenInEditorGroup, options?: IOpenEditorsOptions): Promise<IEditorPane[]>;
openEditors(editors: Array<IEditorInputWithOptions | IResourceEditorInputType>, group?: OpenInEditorGroup, options?: IOpenEditorsOptions): Promise<IEditorPane[]> {
return this.editorService.openEditors(editors, group, options);
}
replaceEditors(editors: IResourceEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise<void>;
replaceEditors(editors: IEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise<void>;
replaceEditors(editors: Array<IEditorReplacement | IResourceEditorReplacement>, group: IEditorGroup | GroupIdentifier): Promise<void> {
return this.editorService.replaceEditors(editors, group);
}
isOpened(editor: IResourceEditorInputIdentifier): boolean { return this.editorService.isOpened(editor); }
findEditors(resource: URI): readonly IEditorIdentifier[];
findEditors(resource: IResourceEditorInputIdentifier): readonly IEditorIdentifier[];
findEditors(resource: URI, group: IEditorGroup | GroupIdentifier): readonly IEditorInput[];
findEditors(resource: IResourceEditorInputIdentifier, group: IEditorGroup | GroupIdentifier): IEditorInput | undefined;
findEditors(arg1: URI | IResourceEditorInputIdentifier, arg2?: IEditorGroup | GroupIdentifier): readonly IEditorIdentifier[] | readonly IEditorInput[] | IEditorInput | undefined { return this.editorService.findEditors(arg1, arg2); }
createEditorInput(input: IResourceEditorInputType): EditorInput { return this.editorService.createEditorInput(input); }
save(editors: IEditorIdentifier | IEditorIdentifier[], options?: ISaveEditorsOptions): Promise<boolean> { return this.editorService.save(editors, options); }
saveAll(options?: ISaveAllEditorsOptions): Promise<boolean> { return this.editorService.saveAll(options); }
revert(editors: IEditorIdentifier | IEditorIdentifier[], options?: IRevertOptions): Promise<boolean> { return this.editorService.revert(editors, options); }
revertAll(options?: IRevertAllEditorsOptions): Promise<boolean> { return this.editorService.revertAll(options); }
//#endregion
}
registerSingleton(IEditorService, EditorService);

View file

@ -7,12 +7,10 @@ import * as assert from 'assert';
import { EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { EditorsOrder, IResourceDiffEditorInput, isResourceDiffEditorInput } from 'vs/workbench/common/editor';
import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, TestFileEditorInput, ITestInstantiationService, registerTestResourceEditor, registerTestSideBySideEditor, createEditorPart } from 'vs/workbench/test/browser/workbenchTestServices';
import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { EditorService, DelegatingEditorService } from 'vs/workbench/services/editor/browser/editorService';
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
import { IEditorGroup, IEditorGroupsService, GroupDirection, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService';
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
@ -27,7 +25,6 @@ import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSystemProvider';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
import { isLinux } from 'vs/base/common/platform';
import { MockScopableContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorOverrideService';
@ -493,38 +490,6 @@ suite('EditorService', () => {
assert.strictEqual(untypedDiffInput.modifiedInput.resource?.toString(), resourceDiffInput.modifiedInput.resource.toString());
});
test('delegate', function (done) {
const instantiationService = workbenchInstantiationService();
class MyEditor extends EditorPane {
constructor(id: string) {
super(id, undefined!, new TestThemeService(), new TestStorageService());
}
override getId(): string {
return 'myEditor';
}
layout(): void { }
createEditor(): void { }
}
const editor = instantiationService.createInstance(MyEditor, 'my.editor');
const input = instantiationService.createInstance(TextResourceEditorInput, URI.parse('my://resource-delegate'), 'name', 'description', undefined, undefined);
const delegate = instantiationService.createInstance(DelegatingEditorService, async (group, delegate) => {
assert.ok(group);
done();
return editor;
});
delegate.openEditor(input);
});
test('close editor does not dispose when editor opened in other group', async () => {
const [part, service] = await createEditorService();