Improve handling of injection.combined
injections in SyntaxSnapshot::layers_for_range
(#32145)
Closes #27596 The problem in this case was incorrect identification of which language (layer) contains the selection. Language layer selection incorrectly assumed that the deepest `SyntaxLayer` containing a range was the most specific. This worked for Markdown (base document + injected subtrees) but failed for PHP, where `injection.combined` injections are used to make HTML logically function as the base layer, despite being at a greater depth in the layer stack. This caused HTML to be incorrectly identified as the most specific language for PHP ranges. The solution is to track included sub-ranges for syntax layers and filter out layers that don't contain a sub-range covering the desired range. The top-level layer is never filtered to ensure gaps between sibling nodes always have a fallback language, as the top-level layer is likely more correct than the default language settings. Release Notes: - Fixed an issue in PHP where PHP language settings would be occasionally overridden by HTML language settings
This commit is contained in:
parent
2e883be4b5
commit
a40ee74a1f
2 changed files with 49 additions and 5 deletions
|
@ -1387,9 +1387,30 @@ impl Buffer {
|
|||
/// Returns the [`Language`] at the given location.
|
||||
pub fn language_at<D: ToOffset>(&self, position: D) -> Option<Arc<Language>> {
|
||||
let offset = position.to_offset(self);
|
||||
let mut is_first = true;
|
||||
let start_anchor = self.anchor_before(offset);
|
||||
let end_anchor = self.anchor_after(offset);
|
||||
self.syntax_map
|
||||
.lock()
|
||||
.layers_for_range(offset..offset, &self.text, false)
|
||||
.filter(|layer| {
|
||||
if is_first {
|
||||
is_first = false;
|
||||
return true;
|
||||
}
|
||||
let any_sub_ranges_contain_range = layer
|
||||
.included_sub_ranges
|
||||
.map(|sub_ranges| {
|
||||
sub_ranges.iter().any(|sub_range| {
|
||||
let is_before_start = sub_range.end.cmp(&start_anchor, self).is_lt();
|
||||
let is_after_end = sub_range.start.cmp(&end_anchor, self).is_gt();
|
||||
!is_before_start && !is_after_end
|
||||
})
|
||||
})
|
||||
.unwrap_or(true);
|
||||
let result = any_sub_ranges_contain_range;
|
||||
return result;
|
||||
})
|
||||
.last()
|
||||
.map(|info| info.language.clone())
|
||||
.or_else(|| self.language.clone())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue