Add a unit test for navigation behavior at the editor level

Max Brunsfeld created

Change summary

crates/editor/src/editor.rs  | 57 ++++++++++++++++++++++++++++++++++++++
crates/workspace/src/pane.rs | 18 ++++++++---
2 files changed, 70 insertions(+), 5 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -4156,6 +4156,63 @@ mod tests {
         });
     }
 
+    #[gpui::test]
+    fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
+        cx.add_window(Default::default(), |cx| {
+            use workspace::ItemView;
+            let navigation = Rc::new(workspace::Navigation::default());
+            let settings = EditorSettings::test(&cx);
+            let buffer = MultiBuffer::build_simple(&sample_text(30, 5, 'a'), cx);
+            let mut editor = build_editor(buffer.clone(), settings, cx);
+            editor.navigation = Some(navigation.clone());
+
+            // Move the cursor a small distance.
+            // Nothing is added to the navigation history.
+            editor.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
+            editor.select_display_ranges(&[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)], cx);
+            assert!(navigation.pop_backward().is_none());
+
+            // Move the cursor a large distance.
+            // The history can jump back to the previous position.
+            editor.select_display_ranges(&[DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)], cx);
+            let nav_entry = navigation.pop_backward().unwrap();
+            editor.navigate(nav_entry.data.unwrap(), cx);
+            assert_eq!(nav_entry.item_view.id(), cx.view_id());
+            assert_eq!(
+                editor.selected_display_ranges(cx),
+                &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
+            );
+
+            // Move the cursor a small distance via the mouse.
+            // Nothing is added to the navigation history.
+            editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
+            editor.end_selection(cx);
+            assert_eq!(
+                editor.selected_display_ranges(cx),
+                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
+            );
+            assert!(navigation.pop_backward().is_none());
+
+            // Move the cursor a large distance via the mouse.
+            // The history can jump back to the previous position.
+            editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
+            editor.end_selection(cx);
+            assert_eq!(
+                editor.selected_display_ranges(cx),
+                &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
+            );
+            let nav_entry = navigation.pop_backward().unwrap();
+            editor.navigate(nav_entry.data.unwrap(), cx);
+            assert_eq!(nav_entry.item_view.id(), cx.view_id());
+            assert_eq!(
+                editor.selected_display_ranges(cx),
+                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
+            );
+
+            editor
+        });
+    }
+
     #[gpui::test]
     fn test_cancel(cx: &mut gpui::MutableAppContext) {
         let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);

crates/workspace/src/pane.rs 🔗

@@ -101,9 +101,9 @@ impl Default for NavigationMode {
     }
 }
 
-struct NavigationEntry {
-    item_view: Box<dyn WeakItemViewHandle>,
-    data: Option<Box<dyn Any>>,
+pub struct NavigationEntry {
+    pub item_view: Box<dyn WeakItemViewHandle>,
+    pub data: Option<Box<dyn Any>>,
 }
 
 impl Pane {
@@ -535,11 +535,19 @@ impl View for Pane {
 }
 
 impl Navigation {
+    pub fn pop_backward(&self) -> Option<NavigationEntry> {
+        self.0.borrow_mut().backward_stack.pop()
+    }
+
+    pub fn pop_forward(&self) -> Option<NavigationEntry> {
+        self.0.borrow_mut().forward_stack.pop()
+    }
+
     fn pop(&self, mode: NavigationMode) -> Option<NavigationEntry> {
         match mode {
             NavigationMode::Normal => None,
-            NavigationMode::GoingBack => self.0.borrow_mut().backward_stack.pop(),
-            NavigationMode::GoingForward => self.0.borrow_mut().forward_stack.pop(),
+            NavigationMode::GoingBack => self.pop_backward(),
+            NavigationMode::GoingForward => self.pop_forward(),
         }
     }