-
Couldn't load subscription status.
- Fork 5.7k
language: Fix language detection for injected syntax layers #41111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
language: Fix language detection for injected syntax layers #41111
Conversation
Previously, `syntax_layer_at` incorrectly reported injected languages (e.g., Ruby in ERB files) even when the cursor was in the base language content (HTML). This broke actions like "wrap selections in tag" that depend on language-specific configuration. The issue had two parts: 1. Missing start boundary check: The filter only checked if a layer's end was after the cursor (`end_byte() > offset`), not if it started before, causing layers outside the cursor position to be included. 2. Wrong boundary reference for injections: For injected layers with `included_sub_ranges` (like Ruby code blocks in ERB), checking the root node boundaries returned the entire file range instead of the actual injection ranges. This fix: - Adds proper containment check using half-open range semantics [start, end) for root node boundaries - Checks `included_sub_ranges` for injected layers to determine if the cursor is actually within an injection - Falls back to root node boundaries for base layers without sub-ranges Fixes template language support (ERB, Vue, JSX, etc.) where actions should be available based on the cursor's actual language context.
| @@ -2633,7 +2627,7 @@ fn test_language_scope_at_with_combined_injections(cx: &mut App) { | |||
| buffer.set_language_registry(language_registry.clone()); | |||
| buffer.set_language( | |||
| language_registry | |||
| .language_for_name("ERB") | |||
| .language_for_name("HTML+ERB") | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| @@ -3655,7 +3680,7 @@ fn html_lang() -> Language { | |||
| fn erb_lang() -> Language { | |||
| Language::new( | |||
| LanguageConfig { | |||
| name: "ERB".into(), | |||
| name: "HTML+ERB".into(), | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| (code) @content | ||
| (#set! "language" "ruby") | ||
| (#set! "combined") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| (content) @content | ||
| (#set! "language" "html") | ||
| (#set! "combined") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let included_sub_ranges: Option<Vec<Range<Anchor>>> = if is_combined { | ||
| Some( | ||
| included_ranges | ||
| .into_iter() | ||
| .filter(|r| r.start_byte < r.end_byte) | ||
| .map(|r| { | ||
| text.anchor_before(r.start_byte + step_start_byte) | ||
| ..text.anchor_after(r.end_byte + step_start_byte) | ||
| }) | ||
| .collect(), | ||
| ); | ||
| ) | ||
| } else { | ||
| None | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure we always provide accurate metadata for combined injections.
|
Always happy to see your PRs, @vitallium! We'll take a look! |
Closes #40632
TL;DR: The
wrap selections in tagaction was unavailable in ERB files, even when the cursor was positioned in HTML content (outside of Ruby code blocks). This happened becausesyntax_layer_at()incorrectly returned the Ruby language for positions that were actually in HTML. NOTE: I am not familiar with that part of Zed so it could be that the fix here is completely incorrect.Previously,
syntax_layer_atincorrectly reported injected languages (e.g., Ruby in ERB files) even when the cursor was in the base language content (HTML). This broke actions likewrap selections in tagthat depend on language-specific configuration.The issue had two parts:
end_byte() > offset), not if it started before, causing layers outside the cursor position to be included. See theBEFOREvideo: when I click on the HTML part it reportsRubylanguage instead ofHTML.included_sub_ranges(like Ruby code blocks in ERB), checking the root node boundaries returned the entire file range instead of the actual injection ranges.This fix:
Adds the containment check using half-open range semantics [start, end) for root node boundaries. That ensures proper reporting of the detected language when a cursor (
|) is located right after the injection:Checks
included_sub_rangesfor injected layers to determine if the cursor is actually within an injectionFalls back to root node boundaries for base layers without sub-ranges. This is the original behavior.
Fixes ERB language support where actions should be available based on the cursor's actual language context. I think that also applies to some other template languages like HEEX (Phoenix) and
*.pug. On short videos below you can see how I navigate through the ERB template and the terminal on the right outputs the detected language if you apply the following patch:Before:
before.mp4
After:
after.mp4
Here is the ERB template:
Release Notes: