Make default text editor replace existing editors for resource (#112848)
* Make default text editor replace existing editors for resource
Fixes #111474
This ports the logic that we previouly had for custom editors to the generic open with flow: f0bb23ca02/src%2Fvs%2Fworkbench%2Fcontrib%2FcustomEditor%2Fbrowser%2FcustomEditors.ts (L281)
* Add a new `getEditorsForResource` helper method to editor service
This adds a `IEditorService.getEditorsForResource` method that returns all editors associated with a given resource. I've adopted this for custom editors and notebooks
* Making suggested changes
* Add unit tests for findEditors
This commit is contained in:
parent
a59f30011c
commit
6c4a00ce74
7 changed files with 104 additions and 21 deletions
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { coalesce, distinct } from 'vs/base/common/arrays';
|
||||
import { coalesce, distinct, firstOrDefault } from 'vs/base/common/arrays';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
|
@ -154,13 +154,8 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
|
|||
...this.getAllCustomEditors(resource).allEditors,
|
||||
]);
|
||||
|
||||
let currentlyOpenedEditorType: undefined | string;
|
||||
for (const editor of group ? group.editors : []) {
|
||||
if (editor.resource && isEqual(editor.resource, resource)) {
|
||||
currentlyOpenedEditorType = editor instanceof CustomEditorInput ? editor.viewType : defaultCustomEditor.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const existingEditorForResource = group && firstOrDefault(this.editorService.findEditors(resource, group));
|
||||
const currentlyOpenedEditorType: undefined | string = existingEditorForResource instanceof CustomEditorInput ? existingEditorForResource.viewType : defaultCustomEditor.id;
|
||||
|
||||
const resourceExt = extname(resource);
|
||||
|
||||
|
@ -276,9 +271,8 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
|
|||
}
|
||||
|
||||
// Try to replace existing editors for resource
|
||||
const existingEditors = targetGroup.editors.filter(editor => editor.resource && isEqual(editor.resource, resource));
|
||||
if (existingEditors.length) {
|
||||
const existing = existingEditors[0];
|
||||
const existing = firstOrDefault(this.editorService.findEditors(resource, targetGroup));
|
||||
if (existing) {
|
||||
if (!input.matches(existing)) {
|
||||
await this.editorService.replaceEditors([{
|
||||
editor: existing,
|
||||
|
@ -453,7 +447,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo
|
|||
return this.onEditorOpening(editor, options, group);
|
||||
},
|
||||
getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): IOpenEditorOverrideEntry[] => {
|
||||
const currentEditor = group?.editors.find(editor => isEqual(editor.resource, resource));
|
||||
const currentEditor = group && firstOrDefault(this.editorService.findEditors(resource, group));
|
||||
|
||||
const toOverride = (entry: CustomEditorInfo): IOpenEditorOverrideEntry => {
|
||||
return {
|
||||
|
@ -542,7 +536,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo
|
|||
return;
|
||||
}
|
||||
|
||||
const existingEditorForResource = group.editors.find(editor => isEqual(resource, editor.resource));
|
||||
const existingEditorForResource = firstOrDefault(this.editorService.findEditors(resource, group));
|
||||
if (existingEditorForResource) {
|
||||
if (editor === existingEditorForResource) {
|
||||
return;
|
||||
|
|
|
@ -230,7 +230,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
|
|||
this._register(this.editorService.overrideOpenEditor({
|
||||
getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) => {
|
||||
|
||||
const currentEditorForResource = group?.editors.find(editor => isEqual(editor.resource, resource));
|
||||
const currentEditorForResource = group && this.editorService.findEditors(resource, group);
|
||||
|
||||
const associatedEditors = distinct([
|
||||
...this.getUserAssociatedNotebookEditors(resource),
|
||||
|
@ -341,10 +341,8 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
|
|||
}
|
||||
|
||||
if (id === undefined) {
|
||||
const existingEditors = group.editors.filter(editor =>
|
||||
editor.resource
|
||||
&& isEqual(editor.resource, notebookUri)
|
||||
&& !(editor instanceof NotebookEditorInput)
|
||||
const existingEditors = this.editorService.findEditors(notebookUri, group).filter(editor =>
|
||||
!(editor instanceof NotebookEditorInput)
|
||||
&& !(editor instanceof NotebookDiffEditorInput)
|
||||
);
|
||||
|
||||
|
@ -409,7 +407,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, notebookUri) && !(editor instanceof NotebookEditorInput));
|
||||
const existingEditors = this.editorService.findEditors(notebookUri, group).filter(editor => !(editor instanceof NotebookEditorInput));
|
||||
|
||||
if (existingEditors.length) {
|
||||
return undefined;
|
||||
|
|
|
@ -756,6 +756,26 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region findEditor()
|
||||
|
||||
findEditors(resource: URI, group: IEditorGroup | GroupIdentifier): IEditorInput[] {
|
||||
if (!this.isOpen({ resource })) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const canonicalResource = this.asCanonicalEditorResource(resource);
|
||||
const targetGroup = typeof group === 'number' ? this.editorGroupService.getGroup(group) : group;
|
||||
if (!targetGroup) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return targetGroup.getEditors(EditorsOrder.SEQUENTIAL).filter(editor => {
|
||||
return editor.resource && isEqual(editor.resource, canonicalResource);
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region replaceEditors()
|
||||
|
||||
async replaceEditors(editors: IResourceEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise<void>;
|
||||
|
@ -1322,6 +1342,8 @@ export class DelegatingEditorService implements IEditorService {
|
|||
isOpen(editor: IResourceEditorInput): boolean;
|
||||
isOpen(editor: IEditorInput | IResourceEditorInput): boolean { return this.editorService.isOpen(editor as IResourceEditorInput /* TS fail */); }
|
||||
|
||||
findEditors(resource: URI, group: IEditorGroup | GroupIdentifier) { return this.editorService.findEditors(resource, group); }
|
||||
|
||||
overrideOpenEditor(handler: IOpenEditorOverrideHandler): IDisposable { return this.editorService.overrideOpenEditor(handler); }
|
||||
getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) { return this.editorService.getEditorOverrides(resource, options, group); }
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platf
|
|||
import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ICustomEditorInfo, IEditorService, IOpenEditorOverrideHandler, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorInput, IEditorPane, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorResourceAccessor } from 'vs/workbench/common/editor';
|
||||
import { IEditorInput, IEditorPane, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorResourceAccessor, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorGroup, IEditorGroupsService, OpenEditorContext, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
@ -18,6 +18,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { extname, basename, isEqual } from 'vs/base/common/resources';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { firstOrDefault } from 'vs/base/common/arrays';
|
||||
|
||||
/**
|
||||
* Id of the default editor for open with.
|
||||
|
@ -214,7 +215,21 @@ export function getAllAvailableEditors(
|
|||
|
||||
const fileEditorInput = editorService.createEditorInput({ resource, forceFile: true });
|
||||
const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: false } : { override: false };
|
||||
return { override: editorService.openEditor(fileEditorInput, textOptions, group) };
|
||||
return {
|
||||
override: (async () => {
|
||||
// Try to replace existing editors for resource
|
||||
const existingEditor = firstOrDefault(editorService.findEditors(resource, group));
|
||||
if (existingEditor && !fileEditorInput.matches(existingEditor)) {
|
||||
await editorService.replaceEditors([{
|
||||
editor: existingEditor,
|
||||
replacement: fileEditorInput,
|
||||
options: options ? EditorOptions.create(options) : undefined,
|
||||
}], group);
|
||||
}
|
||||
|
||||
return editorService.openEditor(fileEditorInput, textOptions, group);
|
||||
})()
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -233,6 +233,11 @@ export interface IEditorService {
|
|||
isOpen(editor: IResourceEditorInput): boolean;
|
||||
isOpen(editor: IEditorInput): boolean;
|
||||
|
||||
/**
|
||||
* Find the existing editors for a given resource.
|
||||
*/
|
||||
findEditors(resource: URI, group: IEditorGroup | GroupIdentifier): IEditorInput[];
|
||||
|
||||
/**
|
||||
* Get all available editor overrides for the editor input.
|
||||
*/
|
||||
|
|
|
@ -1094,4 +1094,52 @@ suite('EditorService', () => {
|
|||
|
||||
part.dispose();
|
||||
});
|
||||
|
||||
test('findEditors', async () => {
|
||||
const [part, service] = createEditorService();
|
||||
|
||||
const input = new TestFileEditorInput(URI.parse('my://resource-openEditors'), TEST_EDITOR_INPUT_ID);
|
||||
const otherInput = new TestFileEditorInput(URI.parse('my://resource2-openEditors'), TEST_EDITOR_INPUT_ID);
|
||||
|
||||
await part.whenRestored;
|
||||
|
||||
// Open editors
|
||||
await service.openEditors([{ editor: input }, { editor: otherInput }]);
|
||||
assert.equal(part.activeGroup.count, 2);
|
||||
|
||||
// Try using find editors for opened editors
|
||||
{
|
||||
const found = service.findEditors(input.resource, part.activeGroup);
|
||||
assert.equal(found.length, 1);
|
||||
assert.equal(found[0], input);
|
||||
}
|
||||
{
|
||||
const found = service.findEditors(otherInput.resource, part.activeGroup);
|
||||
assert.equal(found.length, 1);
|
||||
assert.equal(found[0], otherInput);
|
||||
}
|
||||
|
||||
// Make sure we don't find non-opened editors
|
||||
{
|
||||
const found = service.findEditors(URI.parse('my://no-such-resource'), part.activeGroup);
|
||||
assert.equal(found.length, 0);
|
||||
}
|
||||
|
||||
// Make sure we don't find editors across groups
|
||||
{
|
||||
const newEditor = await service.openEditor(new TestFileEditorInput(URI.parse('my://other-group-resource'), TEST_EDITOR_INPUT_ID), { pinned: true, preserveFocus: true }, SIDE_GROUP);
|
||||
|
||||
const found = service.findEditors(input.resource, newEditor!.group!.id);
|
||||
assert.equal(found.length, 0);
|
||||
}
|
||||
|
||||
// Check we don't find editors after closing them
|
||||
await part.activeGroup.closeAllEditors();
|
||||
{
|
||||
const found = service.findEditors(input.resource, part.activeGroup);
|
||||
assert.equal(found.length, 0);
|
||||
}
|
||||
|
||||
part.dispose();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -704,6 +704,7 @@ export class TestEditorService implements EditorServiceImpl {
|
|||
|
||||
constructor(private editorGroupService?: IEditorGroupsService) { }
|
||||
getEditors() { return []; }
|
||||
findEditors() { return []; }
|
||||
getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] { return []; }
|
||||
overrideOpenEditor(_handler: IOpenEditorOverrideHandler): IDisposable { return toDisposable(() => undefined); }
|
||||
registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable {
|
||||
|
|
Loading…
Reference in a new issue