Skip to content

Commit 04c7b98

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 15758e1 commit 04c7b98

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
@@ -708,14 +708,62 @@ def check_consume_key(self, key: str, character: str | None = None) -> bool:
708708
# Otherwise we capture all printable keys
709709
return character is not None and character.isprintable()
710710

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

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

720768
def _handle_change_affecting_highlighting(
721769
self,

0 commit comments

Comments
 (0)