Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class EditorResolverService extends Disposable implements IEditorResolver
let resource = EditorResourceAccessor.getCanonicalUri(untypedEditor, { supportSideBySide: SideBySideEditor.PRIMARY });

// If it was resolved before we await for the extensions to activate and then proceed with resolution or else the backing extensions won't be registered
if (this.cache && resource && this.resourceMatchesCache(resource)) {
if (this.cache && resource && (this.resourceMatchesCache(resource) || this.resourceMatchesUserAssociation(resource))) {
await this.extensionService.whenInstalledExtensionsRegistered();
}

Expand Down Expand Up @@ -825,6 +825,22 @@ export class EditorResolverService extends Disposable implements IEditorResolver
this.storageService.store(EditorResolverService.cacheStorageID, JSON.stringify(Array.from(cacheStorage)), StorageScope.PROFILE, StorageTarget.MACHINE);
}

/**
* Checks if a resource matches any user-configured editor association that
* points to a non-default editor. This ensures that on first startup (when
* the cache is empty), we still wait for extensions to register before
* resolving the editor, so that user-configured custom editors are available.
*/
private resourceMatchesUserAssociation(resource: URI): boolean {
const userAssociations = this.getAllUserAssociations();
for (const association of userAssociations) {
if (association.filenamePattern && association.viewType !== DEFAULT_EDITOR_ASSOCIATION.id && globMatchesResource(association.filenamePattern, resource)) {
return true;
}
}
return false;
}

private resourceMatchesCache(resource: URI): boolean {
if (!this.cache) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { EditorPart } from '../../../../browser/parts/editor/editorPart.js';
import { DiffEditorInput } from '../../../../common/editor/diffEditorInput.js';
import { EditorResolverService } from '../../browser/editorResolverService.js';
import { IEditorGroupsService } from '../../common/editorGroupsService.js';
import { IEditorResolverService, ResolvedStatus, RegisteredEditorPriority } from '../../common/editorResolverService.js';
import { IEditorResolverService, ResolvedStatus, RegisteredEditorPriority, editorsAssociationsSettingId } from '../../common/editorResolverService.js';
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
import { createEditorPart, ITestInstantiationService, TestFileEditorInput, TestServiceAccessor, workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';

suite('EditorResolverService', () => {
Expand Down Expand Up @@ -455,4 +456,69 @@ suite('EditorResolverService', () => {

registeredSingleEditor.dispose();
});

test('User-configured editor association resolves on first startup with empty cache #244597', async () => {
const CUSTOM_EDITOR_INPUT_ID = 'testCustomEditorInput';

// Set up a configuration with a user-configured editor association
const instantiationService = workbenchInstantiationService({
configurationService: () => new TestConfigurationService({
[editorsAssociationsSettingId]: {
'*.md': 'CUSTOM_MD_EDITOR'
}
})
}, disposables);

const part = await createEditorPart(instantiationService, disposables);
instantiationService.stub(IEditorGroupsService, part);

const editorResolverService = instantiationService.createInstance(EditorResolverService);
disposables.add(editorResolverService);

// Register both the default text editor and the custom markdown editor with 'option' priority
// (matching how markdown preview is registered in package.json)
const defaultEditor = editorResolverService.registerEditor('*',
{
id: 'default',
label: 'Default Editor',
detail: 'Default',
priority: RegisteredEditorPriority.default
},
{},
{
createEditorInput: ({ resource }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) })
}
);

const customEditor = editorResolverService.registerEditor('*.md',
{
id: 'CUSTOM_MD_EDITOR',
label: 'Markdown Preview',
detail: 'Markdown Preview Details',
priority: RegisteredEditorPriority.option
},
{},
{
createEditorInput: ({ resource }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), CUSTOM_EDITOR_INPUT_ID) })
}
);

// Resolve a .md file - should use the custom editor due to user association
const resultingResolution = await editorResolverService.resolveEditor(
{ resource: URI.file('test.md') },
part.activeGroup
);
assert.ok(resultingResolution);
assert.notStrictEqual(typeof resultingResolution, 'number');
if (resultingResolution !== ResolvedStatus.ABORT && resultingResolution !== ResolvedStatus.NONE) {
assert.strictEqual(resultingResolution.editor.typeId, CUSTOM_EDITOR_INPUT_ID,
'Should resolve to custom editor when user has configured editor association');
resultingResolution.editor.dispose();
} else {
assert.fail('Expected editor to resolve successfully');
}

defaultEditor.dispose();
customEditor.dispose();
});
});
Loading