diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 0781fb1e0e..eab88c24d0 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2399,6 +2399,8 @@ impl Editor { return; } + self.push_to_navigation_history(cx); + let selection = Selection { id: post_inc(&mut self.next_selection_id), start: 0, @@ -2421,6 +2423,8 @@ impl Editor { return; } + self.push_to_navigation_history(cx); + let cursor = self.buffer.read(cx).read(cx).len(); let selection = Selection { id: post_inc(&mut self.next_selection_id), @@ -2432,6 +2436,15 @@ impl Editor { self.update_selections(vec![selection], Some(Autoscroll::Fit), cx); } + fn push_to_navigation_history(&self, cx: &mut ViewContext) { + if let Some(navigation) = &self.navigation { + if let Some(last_selection) = self.selections.iter().max_by_key(|s| s.id) { + let cursor = last_selection.head(); + navigation.push(Some(cursor), cx); + } + } + } + pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext) { let mut selection = self.local_selections::(cx).first().unwrap().clone(); selection.set_head(self.buffer.read(cx).read(cx).len()); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 015b4b84b6..5ff90201d5 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1,5 +1,4 @@ -use crate::{Autoscroll, Editor, Event}; -use crate::{MultiBuffer, ToPoint as _}; +use crate::{Anchor, Autoscroll, Editor, Event, MultiBuffer, ToOffset, ToPoint as _}; use anyhow::Result; use gpui::{ elements::*, AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, RenderContext, @@ -106,6 +105,13 @@ impl ItemView for Editor { BufferItemHandle(self.buffer.read(cx).as_singleton().unwrap()) } + fn navigate(&mut self, data: Box, cx: &mut ViewContext) { + if let Some(anchor) = data.downcast_ref::() { + let offset = anchor.to_offset(&self.buffer.read(cx).read(cx)); + self.select_ranges([offset..offset], Some(Autoscroll::Fit), cx); + } + } + fn title(&self, cx: &AppContext) -> String { let filename = self .buffer() @@ -134,9 +140,7 @@ impl ItemView for Editor { } fn deactivated(&mut self, cx: &mut ViewContext) { - if let Some(navigation) = self.navigation.as_ref() { - navigation.push::<(), _>(None, cx); - } + self.push_to_navigation_history(cx); } fn is_dirty(&self, cx: &AppContext) -> bool { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 6fb35ba786..f745298f6b 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -84,6 +84,7 @@ struct NavigationHistory { paths_by_item: HashMap, } +#[derive(Copy, Clone)] enum NavigationHistoryMode { Normal, GoingBack, @@ -116,77 +117,81 @@ impl Pane { } pub fn go_back(workspace: &mut Workspace, _: &GoBack, cx: &mut ViewContext) { - let project_path = workspace.active_pane().update(cx, |pane, cx| { - let mut navigation = pane.navigation.0.borrow_mut(); - if let Some(entry) = navigation.backward_stack.pop() { - if let Some(index) = entry - .item_view - .upgrade(cx) - .and_then(|v| pane.index_for_item_view(v.as_ref())) - { - if let Some(item_view) = pane.active_item() { - pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::GoingBack; - item_view.deactivated(cx); - pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::Normal; - } - - pane.active_item_index = index; - drop(navigation); - pane.focus_active_item(cx); - cx.notify(); - } else { - return navigation.paths_by_item.get(&entry.item_view.id()).cloned(); - } - } - - None - }); - - if let Some(project_path) = project_path { - let task = workspace.load_path(project_path, cx); - cx.spawn(|workspace, mut cx| { - async move { - let item = task.await?; - workspace.update(&mut cx, |workspace, cx| { - let pane = workspace.active_pane().clone(); - pane.update(cx, |pane, cx| { - pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::GoingBack; - pane.open_item(item, workspace, cx); - pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::Normal; - }); - }); - Ok(()) - } - .log_err() - }) - .detach(); - } + Self::navigate_history(workspace, NavigationHistoryMode::GoingBack, cx); } - pub fn go_forward(&mut self, _: &GoForward, cx: &mut ViewContext) { - if self.navigation.0.borrow().forward_stack.is_empty() { - return; - } + pub fn go_forward(workspace: &mut Workspace, _: &GoForward, cx: &mut ViewContext) { + Self::navigate_history(workspace, NavigationHistoryMode::GoingForward, cx); + } - if let Some(item_view) = self.active_item() { - self.navigation.0.borrow_mut().mode = NavigationHistoryMode::GoingForward; - item_view.deactivated(cx); - self.navigation.0.borrow_mut().mode = NavigationHistoryMode::Normal; - } + fn navigate_history( + workspace: &mut Workspace, + mode: NavigationHistoryMode, + cx: &mut ViewContext, + ) -> Option<()> { + let (project_path, entry) = workspace.active_pane().update(cx, |pane, cx| { + // Retrieve the weak item handle from the history. + let entry = pane.navigation.pop(mode)?; - let mut navigation = self.navigation.0.borrow_mut(); - if let Some(entry) = navigation.forward_stack.pop() { + // If the item is still present in this pane, then activate it. if let Some(index) = entry .item_view .upgrade(cx) - .and_then(|v| self.index_for_item_view(v.as_ref())) + .and_then(|v| pane.index_for_item_view(v.as_ref())) { - self.active_item_index = index; - drop(navigation); - self.focus_active_item(cx); + if let Some(item_view) = pane.active_item() { + pane.navigation.set_mode(mode); + item_view.deactivated(cx); + pane.navigation.set_mode(NavigationHistoryMode::Normal); + } + + pane.active_item_index = index; + pane.focus_active_item(cx); + if let Some(data) = entry.data { + pane.active_item()?.navigate(data, cx); + } cx.notify(); + None } - } + // If the item is no longer present in this pane, then retrieve its + // project path in order to reopen it. + else { + pane.navigation + .0 + .borrow_mut() + .paths_by_item + .get(&entry.item_view.id()) + .cloned() + .map(|project_path| (project_path, entry)) + } + })?; + + // If the item was no longer present, then load it again from its previous path. + let task = workspace.load_path(project_path, cx); + cx.spawn(|workspace, mut cx| { + async move { + let item = task.await?; + workspace.update(&mut cx, |workspace, cx| { + let pane = workspace.active_pane().clone(); + pane.update(cx, |pane, cx| { + pane.navigation.set_mode(mode); + let item_view = pane.open_item(item, workspace, cx); + pane.navigation.set_mode(NavigationHistoryMode::Normal); + + if let Some(data) = entry.data { + item_view.navigate(data, cx); + } + + cx.notify(); + }); + }); + Ok(()) + } + .log_err() + }) + .detach(); + + None } pub fn open_item( @@ -511,6 +516,18 @@ impl View for Pane { } impl Navigation { + fn pop(&self, mode: NavigationHistoryMode) -> Option { + match mode { + NavigationHistoryMode::Normal => None, + NavigationHistoryMode::GoingBack => self.0.borrow_mut().backward_stack.pop(), + NavigationHistoryMode::GoingForward => self.0.borrow_mut().forward_stack.pop(), + } + } + + fn set_mode(&self, mode: NavigationHistoryMode) { + self.0.borrow_mut().mode = mode; + } + pub fn push(&self, data: Option, cx: &mut ViewContext) { let mut state = self.0.borrow_mut(); match state.mode { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 99ef8764e8..e7504db451 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -33,6 +33,7 @@ use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItem use status_bar::StatusBar; pub use status_bar::StatusItemView; use std::{ + any::Any, future::Future, hash::{Hash, Hasher}, path::{Path, PathBuf}, @@ -148,6 +149,7 @@ pub trait ItemView: View { fn added_to_pane(&mut self, _: Rc, _: &mut ViewContext) {} fn deactivated(&mut self, _: &mut ViewContext) {} + fn navigate(&mut self, _: Box, _: &mut ViewContext) {} fn item_handle(&self, cx: &AppContext) -> Self::ItemHandle; fn title(&self, cx: &AppContext) -> String; fn project_path(&self, cx: &AppContext) -> Option; @@ -211,6 +213,7 @@ pub trait ItemViewHandle { fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option>; fn added_to_pane(&mut self, cx: &mut ViewContext); fn deactivated(&self, cx: &mut MutableAppContext); + fn navigate(&self, data: Box, cx: &mut MutableAppContext); fn id(&self) -> usize; fn to_any(&self) -> AnyViewHandle; fn is_dirty(&self, cx: &AppContext) -> bool; @@ -368,6 +371,10 @@ impl ItemViewHandle for ViewHandle { self.update(cx, |this, cx| this.deactivated(cx)); } + fn navigate(&self, data: Box, cx: &mut MutableAppContext) { + self.update(cx, |this, cx| this.navigate(data, cx)); + } + fn save(&self, cx: &mut MutableAppContext) -> Result>> { self.update(cx, |item, cx| item.save(cx)) }