zed: Add automatic language detection for pasted code - #52718
Closed
florian-trehaut wants to merge 1 commit into
Closed
zed: Add automatic language detection for pasted code#52718florian-trehaut wants to merge 1 commit into
florian-trehaut wants to merge 1 commit into
Conversation
florian-trehaut
force-pushed
the
feat/4868-auto-detect-pasted-language
branch
6 times, most recently
from
March 30, 2026 11:55
866519d to
68c72af
Compare
florian-trehaut
marked this pull request as ready for review
March 30, 2026 11:56
zed-codeowner-coordinator
Bot
requested review from
a team,
MrSubidubi and
SomeoneToIgnore
and removed request for
a team
March 30, 2026 11:56
florian-trehaut
force-pushed
the
feat/4868-auto-detect-pasted-language
branch
from
March 30, 2026 12:15
68c72af to
f59dc16
Compare
florian-trehaut
marked this pull request as draft
March 30, 2026 13:12
florian-trehaut
force-pushed
the
feat/4868-auto-detect-pasted-language
branch
2 times, most recently
from
March 30, 2026 14:29
0acf7b3 to
03934c3
Compare
Add a language_detection module that detects the programming language when pasting code into a Plain Text buffer or receiving content via stdin. Uses tree-sitter grammar scoring: parses content against each native grammar and picks the best-scoring language based on the ratio of valid AST nodes to total nodes. 12 of 20 native grammars participate in scoring. 8 meta-grammars (diff, markdown, regex, jsdoc, gomod, gowork, gitcommit) are excluded because they parse almost any input successfully.
florian-trehaut
force-pushed
the
feat/4868-auto-detect-pasted-language
branch
from
March 30, 2026 15:07
03934c3 to
6669e49
Compare
florian-trehaut
marked this pull request as ready for review
March 30, 2026 15:16
Contributor
|
Thank you, I do not think 500ms "debounce" makes any sense given Zed's alignment to be fast. We should dig down to that and find ways to speed things up before doing anything else, as the last comment in the PR you've linked to explicitly states: #43057 (comment) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Self-Review Checklist:
Closes #4868
Transparency
First OSS contribution in Rust. First time working on a desktop app codebase (my background is data/backend). The code was generated with Claude Code under close supervision. Flagging so reviewers pay extra attention to Rust idioms, GPUI patterns, and desktop conventions.
Summary
Adds automatic language detection for code pasted into Plain Text buffers or received via stdin. Parses pasted content against each native tree-sitter grammar and picks the best-scoring language based on the ratio of valid AST nodes to total nodes.
New
language_detectionmodule withinit()called at startup. ObservesEditorEvent::Editedon Plain Text buffers with 500ms debounce. Grammar list is built dynamically fromLanguageRegistryviaavailable_language_for_modeline_name()— no hardcoded language mapping. Hidden languages and a blacklist of meta-grammars that cause false positives are filtered out. Scoring runs on a background thread viacx.background_executor().spawn().Shows an auto-dismissing toast with an Undo action on detection. New
auto_detect_languageeditor setting (default: true), independent ofdisable_ai. Also handles stdin content detection on editor creation.Adds
grammarsandtree-sitteras direct dependencies to thezedcrate (both already in the workspace, no new external dependencies).Architecture
LanguageDetectoris a GPUIGlobalthat tracks user overrides and which buffer is currently being updated by detection.LanguageDetectionAddonis a per-editorAddonholdingDebouncedDelaystate.build_detection_grammars(registry)queries theLanguageRegistryto dynamically map grammar names toLanguageName, filtering hidden languages and aMETA_GRAMMAR_BLACKLIST.detect_language(content, grammars)scores each grammar using a single reusedParserand returns the best match above threshold.count_error_nodes()does an iterative tree traversal withTreeCursorinstead of recursion.Follows the
edit_prediction_registrypattern (cx.observe_new) andconflict_viewpattern (Editor Addon).Grammar selection
12 of 20 native grammars participate in scoring. 8 are excluded:
AvailableLanguage::hidden()):jsdoc,regex,markdown-inlinediff,markdown,gomod,gowork,gitcommitWhy tree-sitter scoring
PR #43057 attempted this feature with Magika (Google's ML content type detection). It was closed due to latency and architecture concerns. Tree-sitter scoring was suggested by @SomeoneToIgnore on that PR:
No new external dependencies (tree-sitter and grammars already in workspace), no binary size increase (no ONNX Runtime), and it uses the same grammars Zed already uses for syntax highlighting.
Performance
Measured in two ways: unit tests (isolated scoring) and runtime logs (temporary
log::info!withInstant::now()deltas around the full pipeline).Unit tests (debug build,
cargo test): Rust ~1KB in ~24ms, Python ~950B in ~24ms, both for 12 grammars.Runtime logs (debug build):
build_detection_grammars()takes ~70-84µs,detect_language()scoring takes ~15-17ms for 12 grammars. Total end-to-end ~17ms.All measurements on debug build only. I did not measure release builds. The methodology is basic — reviewers should flag if something more rigorous is needed.
Design decisions open for discussion
Scoring algorithm:
(descendant_count - error_count) / descendant_count. Simple ratio.Minimum score threshold (0.5): In my tests, correct languages score >0.9 and incorrect ones <0.3. The 0.5 threshold is arbitrary but sits between the two clusters.
Meta-grammar blacklist: 5 non-hidden grammars excluded statically, identified by manual testing — they parse almost any input as valid.
Minimum content threshold (16 bytes): Arbitrary floor before attempting detection.
Debounce duration (500ms): Tested manually. No data on what the right value is.
JS/TS ambiguity: JavaScript uses the
tsxgrammar in Zed (crates/grammars/src/javascript/config.toml), so there is no separatejavascriptnative grammar. JS code will be detected as TSX.Buffer text cloning: The full buffer text is cloned after debounce fires (inside
fire_newclosure), not on every keystroke. For very large buffers this could be expensive — I don't know if partial content would give reliable results with tree-sitter scoring.Test Plan
count_error_nodesiterative correctness (valid and invalid inputs)auto_detect_language: falsedisables detectionRelease Notes: