indent guides: Cache active indent range (#12204)

Bennet Bo Fenner created

Caching the active indent range allows us to not re-compute the indent
range when it is not necessary

Release Notes:

- N/A

Change summary

crates/editor/src/indent_guides.rs | 57 +++++++++++++++++++++++++++++--
1 file changed, 52 insertions(+), 5 deletions(-)

Detailed changes

crates/editor/src/indent_guides.rs 🔗

@@ -25,8 +25,8 @@ pub struct ActiveIndentGuidesState {
 }
 
 impl ActiveIndentGuidesState {
-    pub fn should_refresh(&self, cursor_row: MultiBufferRow) -> bool {
-        self.pending_refresh.is_none() && (self.cursor_row != cursor_row || self.dirty)
+    pub fn should_refresh(&self) -> bool {
+        self.pending_refresh.is_none() && self.dirty
     }
 }
 
@@ -56,12 +56,27 @@ impl Editor {
         let cursor_row = MultiBufferRow(selection.head().row);
 
         let state = &mut self.active_indent_guides_state;
-        if state.cursor_row != cursor_row {
-            state.cursor_row = cursor_row;
+
+        if state
+            .active_indent_range
+            .as_ref()
+            .map(|active_indent_range| {
+                should_recalculate_indented_range(
+                    state.cursor_row,
+                    cursor_row,
+                    active_indent_range,
+                    snapshot,
+                )
+            })
+            .unwrap_or(true)
+        {
             state.dirty = true;
+        } else {
+            state.cursor_row = cursor_row;
         }
 
-        if state.should_refresh(cursor_row) {
+        if state.should_refresh() {
+            state.cursor_row = cursor_row;
             let snapshot = snapshot.clone();
             state.dirty = false;
 
@@ -159,3 +174,35 @@ async fn resolve_indented_range(
             buffer_id,
         })
 }
+
+fn should_recalculate_indented_range(
+    prev_row: MultiBufferRow,
+    new_row: MultiBufferRow,
+    current_indent_range: &ActiveIndentedRange,
+    snapshot: &DisplaySnapshot,
+) -> bool {
+    if prev_row.0 == new_row.0 {
+        return false;
+    }
+    if let Some((_, _, snapshot)) = snapshot.buffer_snapshot.as_singleton() {
+        if !current_indent_range.row_range.contains(&new_row.0) {
+            return true;
+        }
+
+        let (old_indent, old_is_blank) = snapshot.line_indent_for_row(prev_row.0);
+        let (new_indent, new_is_blank) = snapshot.line_indent_for_row(new_row.0);
+
+        if old_is_blank
+            || new_is_blank
+            || old_indent != new_indent
+            || snapshot.max_point().row == new_row.0
+        {
+            return true;
+        }
+
+        let (next_line_indent, next_line_is_blank) = snapshot.line_indent_for_row(new_row.0 + 1);
+        next_line_is_blank || next_line_indent != old_indent
+    } else {
+        true
+    }
+}