Experiment with a more general way of pushing editor navigation entries

Antonio Scandurra created

Change summary

crates/editor/src/editor.rs  | 11 +++++++++++
crates/editor/src/items.rs   |  2 ++
crates/workspace/src/pane.rs | 22 +++++++++++++++++++++-
crates/zed/src/zed.rs        | 33 ++++++++++++++++++++++++++++++++-
4 files changed, 66 insertions(+), 2 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -3297,6 +3297,17 @@ impl Editor {
         }
         self.pause_cursor_blinking(cx);
 
+        let prev_newest_selection = self.selections.iter().max_by_key(|s| s.id);
+        let curr_newest_selection = selections.iter().max_by_key(|s| s.id);
+        if let Some((prev_selection, curr_selection)) =
+            prev_newest_selection.zip(curr_newest_selection)
+        {
+            if prev_selection.head().to_offset(&buffer) != curr_selection.head().to_offset(&buffer)
+            {
+                self.push_to_navigation_history(cx);
+            }
+        }
+
         self.set_selections(
             Arc::from_iter(selections.into_iter().map(|selection| {
                 let end_bias = if selection.end > selection.start {

crates/editor/src/items.rs 🔗

@@ -115,7 +115,9 @@ impl ItemView for Editor {
             };
 
             drop(buffer);
+            let navigation = self.navigation.take();
             self.select_ranges([offset..offset], Some(Autoscroll::Fit), cx);
+            self.navigation = navigation;
         }
     }
 

crates/workspace/src/pane.rs 🔗

@@ -11,7 +11,13 @@ use gpui::{
 };
 use postage::watch;
 use project::ProjectPath;
-use std::{any::Any, cell::RefCell, cmp, mem, rc::Rc};
+use std::{
+    any::Any,
+    cell::RefCell,
+    cmp, mem,
+    rc::Rc,
+    time::{Duration, Instant},
+};
 use util::ResultExt;
 
 action!(Split, SplitDirection);
@@ -104,6 +110,7 @@ impl Default for NavigationMode {
 struct NavigationEntry {
     item_view: Box<dyn WeakItemViewHandle>,
     data: Option<Box<dyn Any>>,
+    insertion_time: Option<Instant>,
 }
 
 impl Pane {
@@ -551,9 +558,20 @@ impl Navigation {
         let mut state = self.0.borrow_mut();
         match state.mode {
             NavigationMode::Normal => {
+                let mut insertion_time = Instant::now();
+                if let Some(prev_insertion_time) =
+                    state.backward_stack.last().and_then(|e| e.insertion_time)
+                {
+                    if insertion_time.duration_since(prev_insertion_time) <= Duration::from_secs(2)
+                    {
+                        state.backward_stack.pop();
+                        insertion_time = prev_insertion_time;
+                    }
+                }
                 state.backward_stack.push(NavigationEntry {
                     item_view: Box::new(cx.weak_handle()),
                     data: data.map(|data| Box::new(data) as Box<dyn Any>),
+                    insertion_time: Some(insertion_time),
                 });
                 state.forward_stack.clear();
             }
@@ -561,12 +579,14 @@ impl Navigation {
                 state.forward_stack.push(NavigationEntry {
                     item_view: Box::new(cx.weak_handle()),
                     data: data.map(|data| Box::new(data) as Box<dyn Any>),
+                    insertion_time: None,
                 });
             }
             NavigationMode::GoingForward => {
                 state.backward_stack.push(NavigationEntry {
                     item_view: Box::new(cx.weak_handle()),
                     data: data.map(|data| Box::new(data) as Box<dyn Any>),
+                    insertion_time: None,
                 });
             }
         }

crates/zed/src/zed.rs 🔗

@@ -755,6 +755,14 @@ mod tests {
             (file3.clone(), DisplayPoint::new(0, 2))
         );
 
+        workspace
+            .update(&mut cx, |w, cx| Pane::go_back(w, cx))
+            .await;
+        assert_eq!(
+            active_location(&workspace, &mut cx),
+            (file3.clone(), DisplayPoint::new(0, 0))
+        );
+
         workspace
             .update(&mut cx, |w, cx| Pane::go_back(w, cx))
             .await;
@@ -771,10 +779,26 @@ mod tests {
             (file1.clone(), DisplayPoint::new(0, 1))
         );
 
+        workspace
+            .update(&mut cx, |w, cx| Pane::go_back(w, cx))
+            .await;
+        assert_eq!(
+            active_location(&workspace, &mut cx),
+            (file1.clone(), DisplayPoint::new(0, 0))
+        );
+
         // Go back one more time and ensure we don't navigate past the first item in the history.
         workspace
             .update(&mut cx, |w, cx| Pane::go_back(w, cx))
             .await;
+        assert_eq!(
+            active_location(&workspace, &mut cx),
+            (file1.clone(), DisplayPoint::new(0, 0))
+        );
+
+        workspace
+            .update(&mut cx, |w, cx| Pane::go_forward(w, cx))
+            .await;
         assert_eq!(
             active_location(&workspace, &mut cx),
             (file1.clone(), DisplayPoint::new(0, 1))
@@ -801,7 +825,7 @@ mod tests {
             .await;
         assert_eq!(
             active_location(&workspace, &mut cx),
-            (file3.clone(), DisplayPoint::new(0, 2))
+            (file3.clone(), DisplayPoint::new(0, 0))
         );
 
         // Go back to an item that has been closed and removed from disk, ensuring it gets skipped.
@@ -822,6 +846,13 @@ mod tests {
             active_location(&workspace, &mut cx),
             (file1.clone(), DisplayPoint::new(0, 1))
         );
+        workspace
+            .update(&mut cx, |w, cx| Pane::go_forward(w, cx))
+            .await;
+        assert_eq!(
+            active_location(&workspace, &mut cx),
+            (file3.clone(), DisplayPoint::new(0, 0))
+        );
 
         fn active_location(
             workspace: &ViewHandle<Workspace>,