highlighter: Fix some editing operatons might result incorrect hightlight. (#1197)
This PR just removed the partial update highlight cache code. This logic will get incorrect offset. To make sure highlight correct first. Remove this will slow down performance (On editing), when is more than 5K lines, but if less than this the performance can accept.
This commit is contained in:
parent
34ec2564f8
commit
189bd6e9a3
1 changed files with 10 additions and 88 deletions
|
|
@ -216,6 +216,7 @@ impl SyntaxHighlighter {
|
|||
let changed_len = new_text.len() as isize - selected_range.len() as isize;
|
||||
|
||||
let new_tree = match &self.old_tree {
|
||||
// NOTE: 10K lines, about 4.5ms
|
||||
None => self.parser.parse(full_text.as_ref(), None),
|
||||
Some(old) => {
|
||||
let edit = InputEdit {
|
||||
|
|
@ -228,7 +229,6 @@ impl SyntaxHighlighter {
|
|||
};
|
||||
let mut old_cloned = old.clone();
|
||||
old_cloned.edit(&edit);
|
||||
// NOTE: 10K lines, about 4.5ms
|
||||
self.parser.parse(full_text.as_ref(), Some(&old_cloned))
|
||||
}
|
||||
};
|
||||
|
|
@ -237,27 +237,19 @@ impl SyntaxHighlighter {
|
|||
return;
|
||||
};
|
||||
|
||||
let mut changed_ranges = None;
|
||||
if let Some(old_tree) = &self.old_tree {
|
||||
changed_ranges = Some(new_tree.changed_ranges(old_tree));
|
||||
}
|
||||
|
||||
// Update state
|
||||
self.old_tree = Some(new_tree);
|
||||
self.text = full_text.clone();
|
||||
|
||||
// let measure = Measure::new("build_styles");
|
||||
self.build_styles(changed_ranges, changed_len, cx);
|
||||
// let measure = crate::Measure::new("build_styles");
|
||||
self.build_styles(cx);
|
||||
// measure.end();
|
||||
}
|
||||
|
||||
/// NOTE: 10K lines, about 180ms
|
||||
fn build_styles(
|
||||
&mut self,
|
||||
changed_ranges: Option<impl ExactSizeIterator<Item = tree_sitter::Range>>,
|
||||
changed_len: isize,
|
||||
cx: &mut App,
|
||||
) {
|
||||
/// FIXME: To improve the performance when there more than 5K lines, use partial update.
|
||||
/// Ref: https://github.com/longbridge/gpui-component/pull/1197
|
||||
fn build_styles(&mut self, cx: &mut App) {
|
||||
let Some(tree) = &self.old_tree else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -268,78 +260,10 @@ impl SyntaxHighlighter {
|
|||
|
||||
let source = self.text.as_bytes();
|
||||
let mut query_cursor = QueryCursor::new();
|
||||
let mut root_node = tree.root_node();
|
||||
|
||||
// Incremental parsing to only update changed ranges.
|
||||
if let Some(changed_ranges) = changed_ranges {
|
||||
let mut total_range = (None, None);
|
||||
for change_range in changed_ranges {
|
||||
// dbg!(change_range);
|
||||
if total_range.0.is_none() {
|
||||
total_range.0 = Some(change_range.start_byte);
|
||||
}
|
||||
if total_range.1.is_none() {
|
||||
total_range.1 = Some(change_range.end_byte);
|
||||
}
|
||||
}
|
||||
let total_range = total_range.0.unwrap_or(0)..total_range.1.unwrap_or(0);
|
||||
|
||||
// println!("------- total_range: {:?}", total_range);
|
||||
|
||||
if total_range.len() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(node) =
|
||||
root_node.descendant_for_byte_range(total_range.start, total_range.end)
|
||||
{
|
||||
root_node = node;
|
||||
}
|
||||
|
||||
let byte_range = root_node.byte_range();
|
||||
|
||||
// let measure = Measure::new("update cache to change range offset");
|
||||
|
||||
// FIXME: If we delete 1 char in a node, that node will not highlighted.
|
||||
|
||||
// Remove the cache entries that are range is intersecting with the byte_range.
|
||||
self.cache.retain(|_, (range, _)| {
|
||||
if range.start < byte_range.end && range.end > byte_range.start {
|
||||
// Remove the item if it is intersecting with the byte_range.
|
||||
false
|
||||
} else {
|
||||
// Keep the item if it is not intersecting with the byte_range.
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
// Apply changed_len to reorder the cache to move the range offset
|
||||
let mut old_cache: BTreeMap<usize, (Range<usize>, SharedString)> = BTreeMap::new();
|
||||
std::mem::swap(&mut self.cache, &mut old_cache);
|
||||
|
||||
// NOTE: 10K lines, about 35ms
|
||||
for (start, (old_range, highlight_name)) in old_cache.into_iter() {
|
||||
if old_range.end >= byte_range.start {
|
||||
let new_range = Range {
|
||||
start: (old_range.start as isize + changed_len).max(0) as usize,
|
||||
end: (old_range.end as isize + changed_len).max(0) as usize,
|
||||
};
|
||||
|
||||
if new_range.len() > 0 {
|
||||
self.cache
|
||||
.insert(new_range.start, (new_range, highlight_name));
|
||||
}
|
||||
} else {
|
||||
self.cache.insert(start, (old_range, highlight_name));
|
||||
}
|
||||
}
|
||||
// measure.end();
|
||||
} else {
|
||||
self.cache.clear();
|
||||
}
|
||||
let root_node = tree.root_node();
|
||||
self.cache.clear();
|
||||
|
||||
let mut matches = query_cursor.matches(&query, root_node, source);
|
||||
|
||||
while let Some(m) = matches.next() {
|
||||
// Ref:
|
||||
// https://github.com/tree-sitter/tree-sitter/blob/460118b4c82318b083b4d527c9c750426730f9c0/highlight/src/lib.rs#L556
|
||||
|
|
@ -348,10 +272,8 @@ impl SyntaxHighlighter {
|
|||
if let Some(content_node) = content_node {
|
||||
let styles = self.handle_injection(&language_name, content_node, source, cx);
|
||||
for (node_range, highlight_name) in styles {
|
||||
self.cache.insert(
|
||||
node_range.start,
|
||||
(node_range, highlight_name.to_string().into()),
|
||||
);
|
||||
self.cache
|
||||
.insert(node_range.start, (node_range, highlight_name.into()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue