Separate autoclose pair stack from autocomplete stack

Antonio Scandurra created

Change summary

crates/editor/src/editor.rs | 235 ++++++++++++++++++++------------------
1 file changed, 126 insertions(+), 109 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -48,7 +48,7 @@ use std::{
     cmp::{self, Ordering, Reverse},
     iter::{self, FromIterator},
     mem,
-    ops::{Deref, Range, RangeInclusive, Sub},
+    ops::{Deref, DerefMut, Range, RangeInclusive, Sub},
     sync::Arc,
     time::{Duration, Instant},
 };
@@ -312,6 +312,10 @@ trait SelectionExt {
         -> Range<u32>;
 }
 
+trait InvalidationRegion {
+    fn ranges(&self) -> &[Range<Anchor>];
+}
+
 #[derive(Clone, Debug)]
 pub enum SelectPhase {
     Begin {
@@ -385,7 +389,8 @@ pub struct Editor {
     select_next_state: Option<SelectNextState>,
     selection_history:
         HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
-    action_region_stack: Vec<ActionRegionState>,
+    autoclose_stack: InvalidationStack<BracketPairState>,
+    snippet_stack: InvalidationStack<SnippetState>,
     select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
     active_diagnostics: Option<ActiveDiagnosticGroup>,
     scroll_position: Vector2F,
@@ -431,30 +436,18 @@ struct SelectNextState {
     done: bool,
 }
 
-#[derive(Debug)]
-enum ActionRegionState {
-    BracketPair {
-        ranges: Vec<Range<Anchor>>,
-        pair: BracketPair,
-    },
-    Snippet {
-        ranges: Vec<Vec<Range<Anchor>>>,
-        active_index: usize,
-    },
+struct BracketPairState {
+    ranges: Vec<Range<Anchor>>,
+    pair: BracketPair,
 }
 
-impl ActionRegionState {
-    fn ranges(&self) -> &[Range<Anchor>] {
-        match self {
-            ActionRegionState::BracketPair { ranges, .. } => ranges.as_slice(),
-            ActionRegionState::Snippet {
-                ranges,
-                active_index,
-            } => ranges[*active_index].as_slice(),
-        }
-    }
+struct SnippetState {
+    ranges: Vec<Vec<Range<Anchor>>>,
+    active_index: usize,
 }
 
+struct InvalidationStack<T>(Vec<T>);
+
 struct CompletionState {
     initial_position: Anchor,
     completions: Arc<[Completion<Anchor>]>,
@@ -597,7 +590,8 @@ impl Editor {
             add_selections_state: None,
             select_next_state: None,
             selection_history: Default::default(),
-            action_region_stack: Vec::new(),
+            autoclose_stack: Default::default(),
+            snippet_stack: Default::default(),
             select_larger_syntax_node_stack: Vec::new(),
             active_diagnostics: None,
             build_settings,
@@ -1528,7 +1522,7 @@ impl Editor {
 
                 if pair.end.len() == 1 {
                     let mut delta = 0;
-                    bracket_pair_state = Some(ActionRegionState::BracketPair {
+                    bracket_pair_state = Some(BracketPairState {
                         ranges: selections
                             .iter()
                             .map(move |selection| {
@@ -1547,33 +1541,27 @@ impl Editor {
             self.update_selections(new_selections, None, cx);
         }
         if let Some(bracket_pair_state) = bracket_pair_state {
-            self.action_region_stack.push(bracket_pair_state);
+            self.autoclose_stack.push(bracket_pair_state);
         }
     }
 
     fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
         let old_selections = self.local_selections::<usize>(cx);
-        let mut autoclose_ranges = None;
-        if let Some(region_state) = self.action_region_stack.last() {
-            if let ActionRegionState::BracketPair { ranges, pair } = region_state {
-                if pair.end == text {
-                    autoclose_ranges = Some(ranges.as_slice());
-                }
-            }
-        }
-
-        let autoclose_ranges = if let Some(ranges) = autoclose_ranges {
-            ranges
+        let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
+            autoclose_pair
         } else {
             return false;
         };
+        if text != autoclose_pair.pair.end {
+            return false;
+        }
 
-        debug_assert_eq!(old_selections.len(), autoclose_ranges.len());
+        debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
 
         let buffer = self.buffer.read(cx).snapshot(cx);
         if old_selections
             .iter()
-            .zip(autoclose_ranges.iter().map(|r| r.to_offset(&buffer)))
+            .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
             .all(|(selection, autoclose_range)| {
                 let autoclose_range_end = autoclose_range.end.to_offset(&buffer);
                 selection.is_empty() && selection.start == autoclose_range_end
@@ -1592,7 +1580,7 @@ impl Editor {
                     }
                 })
                 .collect();
-            self.action_region_stack.pop();
+            self.autoclose_stack.pop();
             self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
             true
         } else {
@@ -1790,7 +1778,7 @@ impl Editor {
 
         if let Some(tabstop) = tabstops.first() {
             self.select_ranges(tabstop.iter().cloned(), Some(Autoscroll::Fit), cx);
-            self.action_region_stack.push(ActionRegionState::Snippet {
+            self.snippet_stack.push(SnippetState {
                 active_index: 0,
                 ranges: tabstops,
             });
@@ -1811,61 +1799,48 @@ impl Editor {
         let buffer = self.buffer.read(cx).snapshot(cx);
         let old_selections = self.local_selections::<usize>(cx);
 
-        for (ix, region_state) in self.action_region_stack.iter().enumerate().rev() {
-            if let ActionRegionState::Snippet { .. } = region_state {
-                self.action_region_stack.truncate(ix + 1);
-                break;
-            }
-        }
-
-        if let Some(region_state) = self.action_region_stack.last_mut() {
-            if let ActionRegionState::Snippet {
-                ranges,
-                active_index,
-            } = region_state
-            {
-                match bias {
-                    Bias::Left => {
-                        if *active_index > 0 {
-                            *active_index -= 1;
-                        } else {
-                            return false;
-                        }
-                    }
-                    Bias::Right => {
-                        if *active_index + 1 < ranges.len() {
-                            *active_index += 1;
-                        } else {
-                            return false;
-                        }
+        if let Some(snippet) = self.snippet_stack.last_mut() {
+            match bias {
+                Bias::Left => {
+                    if snippet.active_index > 0 {
+                        snippet.active_index -= 1;
+                    } else {
+                        return false;
                     }
                 }
-                if let Some(current_ranges) = ranges.get(*active_index) {
-                    let new_selections = old_selections
-                        .into_iter()
-                        .zip(current_ranges.iter())
-                        .map(|(selection, new_range)| {
-                            let new_range = new_range.to_offset(&buffer);
-                            Selection {
-                                id: selection.id,
-                                start: new_range.start,
-                                end: new_range.end,
-                                reversed: false,
-                                goal: SelectionGoal::None,
-                            }
-                        })
-                        .collect();
-
-                    // Remove the snippet state when moving to the last tabstop.
-                    if *active_index + 1 == ranges.len() {
-                        self.action_region_stack.pop();
+                Bias::Right => {
+                    if snippet.active_index + 1 < snippet.ranges.len() {
+                        snippet.active_index += 1;
+                    } else {
+                        return false;
                     }
+                }
+            }
+            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
+                let new_selections = old_selections
+                    .into_iter()
+                    .zip(current_ranges.iter())
+                    .map(|(selection, new_range)| {
+                        let new_range = new_range.to_offset(&buffer);
+                        Selection {
+                            id: selection.id,
+                            start: new_range.start,
+                            end: new_range.end,
+                            reversed: false,
+                            goal: SelectionGoal::None,
+                        }
+                    })
+                    .collect();
 
-                    self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
-                    return true;
+                // Remove the snippet state when moving to the last tabstop.
+                if snippet.active_index + 1 == snippet.ranges.len() {
+                    self.snippet_stack.pop();
                 }
-                self.action_region_stack.pop();
+
+                self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
+                return true;
             }
+            self.snippet_stack.pop();
         }
 
         false
@@ -3811,26 +3786,8 @@ impl Editor {
         self.add_selections_state = None;
         self.select_next_state = None;
         self.select_larger_syntax_node_stack.clear();
-        while let Some(region_state) = self.action_region_stack.last() {
-            let region_ranges = region_state.ranges();
-            let all_selections_inside_action_ranges = if selections.len() == region_ranges.len() {
-                selections
-                    .iter()
-                    .zip(region_ranges.iter().map(|r| r.to_point(&buffer)))
-                    .all(|(selection, action_range)| {
-                        let head = selection.head().to_point(&buffer);
-                        action_range.start <= head && action_range.end >= head
-                    })
-            } else {
-                false
-            };
-
-            if all_selections_inside_action_ranges {
-                break;
-            } else {
-                self.action_region_stack.pop();
-            }
-        }
+        self.autoclose_stack.invalidate(&selections, &buffer);
+        self.snippet_stack.invalidate(&selections, &buffer);
 
         let new_cursor_position = selections.iter().max_by_key(|s| s.id).map(|s| s.head());
         if let Some(old_cursor_position) = old_cursor_position {
@@ -4560,6 +4517,66 @@ impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
     }
 }
 
+impl<T: InvalidationRegion> InvalidationStack<T> {
+    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
+    where
+        S: Clone + ToOffset,
+    {
+        while let Some(region) = self.last() {
+            let all_selections_inside_invalidation_ranges =
+                if selections.len() == region.ranges().len() {
+                    selections
+                        .iter()
+                        .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
+                        .all(|(selection, invalidation_range)| {
+                            let head = selection.head().to_offset(&buffer);
+                            invalidation_range.start <= head && invalidation_range.end >= head
+                        })
+                } else {
+                    false
+                };
+
+            if all_selections_inside_invalidation_ranges {
+                break;
+            } else {
+                self.pop();
+            }
+        }
+    }
+}
+
+impl<T> Default for InvalidationStack<T> {
+    fn default() -> Self {
+        Self(Default::default())
+    }
+}
+
+impl<T> Deref for InvalidationStack<T> {
+    type Target = Vec<T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for InvalidationStack<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl InvalidationRegion for BracketPairState {
+    fn ranges(&self) -> &[Range<Anchor>] {
+        &self.ranges
+    }
+}
+
+impl InvalidationRegion for SnippetState {
+    fn ranges(&self) -> &[Range<Anchor>] {
+        &self.ranges[self.active_index]
+    }
+}
+
 pub fn diagnostic_block_renderer(
     diagnostic: Diagnostic,
     is_valid: bool,