Allow custom editors and editor defaults to target language IDs
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
## Summary
Allow custom editors to target language IDs in addition to filename globs, and allow users to configure a language-wide default editor from **Reopen Editor With...** and the editor-type dropdown in breadcrumbs.
Today, `contributes.customEditors[].selector` only supports `filenamePattern`. This forces custom editors to duplicate every filename association of a language and means they do not automatically apply to extensions or filename patterns added through `files.associations`.
For example, Markdown recognizes many extensions and filename patterns, while the built-in Markdown custom editors currently have to target specific globs such as `*.md`. A file associated with the `markdown` language under a different extension does not offer those custom editors.
Related prior discussion: #129553 established that associating a language with a custom editor is not currently possible, but it was closed as a question and does not track the feature or language-wide default behavior. The selector shape was originally designed for future expansion similar to document selectors: #94384.
## Proposed custom editor selector
Support a `language` field in custom editor selectors:
```json
{
"contributes": {
"customEditors": [
{
"viewType": "example.markdownEditor",
"displayName": "Markdown Editor",
"priority": "option",
"selector": [
{
"language": "markdown"
}
]
}
]
}
}
```
Existing `filenamePattern` selectors must remain supported. If both fields are specified in one selector, they should be combined as an AND condition; entries in the selector array remain OR conditions. This would allow selectors such as:
```json
"selector": [
{
"language": "markdown",
"filenamePattern": "docs/**"
}
]
```
Language matching should use the resource's effective language ID:
- On initial open, infer it from registered language contributions and `files.associations`.
- If a text model already exists, use its current language ID.
- Re-evaluate editor availability when the model language or language associations change.
This avoids requiring a custom editor to copy a language's extension, filename, and filename-pattern lists.
## Reopen With and breadcrumbs
When a language selector matches, the custom editor should appear consistently in:
- **Reopen Editor With...**
- the editor-type dropdown shown in breadcrumbs
- the **Reopen Editor With** submenu in the editor title overflow menu
- normal editor resolution when the contribution has default priority
- diff editor resolution when the contribution opts into diffs
All these surfaces should use the same resolver match result so they cannot disagree about editor availability.
## Language-wide default editor associations
When an editor is available because of a language selector, choosing **Set Default** should create a language-wide default instead of deriving a glob from the current file's extension.
For example, the UI could show:
> Set Default for 'Markdown'
A possible settings representation is:
```json
"workbench.editorLanguageAssociations": {
"markdown": "example.markdownEditor"
},
"workbench.diffEditorLanguageAssociations": {
"markdown": "example.markdownEditor"
}
```
The exact storage shape is an implementation detail, but it should support independent normal-editor and diff-editor defaults, matching the existing `workbench.editorAssociations` and `workbench.diffEditorAssociations` behavior.
Suggested precedence:
1. A matching explicit filename-pattern association
2. A matching explicit language association
3. The natural priority of matching editor contributions
4. The default text editor fallback
Diff-specific associations should take precedence over normal-editor associations in diff views.
This preserves precise filename overrides while allowing one default to cover all resources associated with a language, including custom extensions configured through `files.associations`.
## Expected behavior
Given:
```json
"files.associations": {
"*.notes": "markdown"
},
"workbench.editorLanguageAssociations": {
"markdown": "example.markdownEditor"
}
```
and a custom editor with `{ "language": "markdown" }`:
1. Opening `README.md` uses `example.markdownEditor`.
2. Opening `meeting.notes` also uses `example.markdownEditor`.
3. **Reopen Editor With...** and the breadcrumbs editor-type dropdown offer the same editor choices for both files.
4. A more-specific filename association can override the language-wide default for either file.
5. Existing extensions using only `filenamePattern` continue to behave unchanged.
Contributor guide
Research direction
Start by tracing custom editor selector resolution and the editor association handling behind Reopen Editor With..., breadcrumbs, editor-title overflow, and diff resolution. Compare matching for filenamePattern and effective language IDs, then verify that language-wide normal and diff defaults preserve filename overrides and produce the expected behavior for README.md and a file.associated with markdown.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript, vscode
- Domain
- desktop, developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100