Skip to content

Commit 5e52faa

Browse files
committed
Use tree-sitter Tree.changed_ranges.
A first attempt at using changed_ranges to reduce the amount of redrawing triggered by completion of a tree-sitter incremental parse.
1 parent 9baa643 commit 5e52faa

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

src/textual/document/_syntax_aware_document.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,12 @@ def reparse(self, timeout_us: int, lines: list[str], syntax_tree=None) -> bool:
239239
# The only known cause is a timeout.
240240
return False
241241
else:
242-
self._syntax_tree = tree
243242
if self._syntax_tree_update_callback is not None:
244-
self._syntax_tree_update_callback()
243+
changed_ranges = self._syntax_tree.changed_ranges(tree)
244+
self._syntax_tree = tree
245+
self._syntax_tree_update_callback(changed_ranges)
246+
else:
247+
self._syntax_tree = tree
245248
return True
246249
finally:
247250
self._parser.timeout_micros = saved_timeout

src/textual/widgets/_text_area.py

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -707,14 +707,62 @@ def check_consume_key(self, key: str, character: str | None = None) -> bool:
707707
# Otherwise we capture all printable keys
708708
return character is not None and character.isprintable()
709709

710-
def _handle_syntax_tree_update(self) -> None:
710+
def _handle_syntax_tree_update(self, tree_ranges) -> None:
711711
"""Reflect changes to the syntax tree."""
712-
if self._highlight_query:
713-
self._highlights.reset()
712+
if not self._highlight_query:
713+
return
714+
715+
self._highlights.reset()
716+
717+
_, first_line_index = self.scroll_offset
718+
visible_line_range = range(
719+
first_line_index, first_line_index + self.size.height
720+
)
714721

715-
# TODO: This feels heavy handed.
716-
_, scroll_offset_y = self.scroll_offset
717-
self.refresh(Region(0, scroll_offset_y, self.size.width, self.size.height))
722+
visible_region = self.window_region
723+
regions = []
724+
full_width = self.size.width
725+
726+
for tree_range in tree_ranges:
727+
start_row = tree_range.start_point.row
728+
end_row = tree_range.end_point.row
729+
if not (start_row in visible_line_range or end_row in visible_line_range):
730+
continue
731+
732+
start_column = tree_range.start_point.column
733+
end_column = tree_range.end_point.column
734+
735+
if start_row == end_row:
736+
width = end_column = start_column
737+
tree_region = Region(start_column, start_row, width, 1)
738+
region = tree_region.intersection(visible_region)
739+
if region.area > 0:
740+
regions.append(region)
741+
742+
else:
743+
# Add region for the first changed line.
744+
width = full_width - start_column
745+
tree_region = Region(start_column, start_row, width, 1)
746+
region = tree_region.intersection(visible_region)
747+
if region.area > 0:
748+
regions.append(region)
749+
750+
# Add region for the last changed line.
751+
tree_region = Region(0, end_row, end_column, 1)
752+
region = tree_region.intersection(visible_region)
753+
if region.area > 0:
754+
regions.append(region)
755+
756+
# Add region for the other lines.
757+
height = end_row - start_row - 1
758+
if height > 0:
759+
tree_region = Region(0, start_row + 1, width, height)
760+
region = tree_region.intersection(visible_region)
761+
if region.area > 0:
762+
regions.append(region)
763+
764+
if regions:
765+
self.refresh(*regions)
718766

719767
def _handle_change_affecting_highlighting(
720768
self,

0 commit comments

Comments
 (0)