Skip to content

Commit 8b94086

Browse files
committed
highlighter: Avoid reusing parse tree when adding an injection range
1 parent 19b9c8e commit 8b94086

File tree

1 file changed

+25
-3
lines changed

1 file changed

+25
-3
lines changed

highlighter/src/parse.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,31 @@ impl LayerData {
9595
parser
9696
.set_included_ranges(&self.ranges)
9797
.map_err(|_| Error::InvalidRanges)?;
98-
let tree = parser
99-
.parse(source, self.parse_tree.as_ref())
100-
.ok_or(Error::Timeout)?;
98+
99+
// HACK:
100+
// This is a workaround for a bug within the lexer (in the C library) or maybe within
101+
// tree-sitter-markdown which needs more debugging. When adding a new range to a combined
102+
// injection and passing the old tree, if the old tree doesn't already cover a wider range
103+
// than the newly added range, some assumptions are violated in the lexer and it tries to
104+
// access some invalid memory, resulting in a segfault. This workaround avoids that
105+
// situation by avoiding passing the old tree when the old tree's range doesn't cover the
106+
// total range of `self.ranges`.
107+
//
108+
// See <https://github.com/helix-editor/helix/pull/12972#issuecomment-2725410409>.
109+
let tree = self.parse_tree.as_ref().filter(|tree| {
110+
let included_ranges_range = self.ranges.first().map(|r| r.start_byte).unwrap_or(0)
111+
..self.ranges.last().map(|r| r.end_byte).unwrap_or(u32::MAX);
112+
// Allow re-parsing the root layer even though the range is larger. The root always
113+
// covers `0..u32::MAX`:
114+
if included_ranges_range == (0..u32::MAX) {
115+
return true;
116+
}
117+
let tree_range = tree.root_node().byte_range();
118+
tree_range.start <= included_ranges_range.start
119+
&& tree_range.end >= included_ranges_range.end
120+
});
121+
122+
let tree = parser.parse(source, tree).ok_or(Error::Timeout)?;
101123
self.parse_tree = Some(tree);
102124
Ok(())
103125
}

0 commit comments

Comments
 (0)