Detailed changes
@@ -67,6 +67,7 @@
"ctrl-o": "pane::GoBack",
"ctrl-i": "pane::GoForward",
"ctrl-]": "editor::GoToDefinition",
+ "ctrl-t": "pane::GoToOlderTag",
"escape": "vim::SwitchToNormalMode",
"ctrl-[": "vim::SwitchToNormalMode",
"v": "vim::ToggleVisual",
@@ -472,7 +472,7 @@ impl Item for AgentDiffPane {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -48,7 +48,7 @@ use settings::{
update_settings_file,
};
use std::{
- any::TypeId,
+ any::{Any, TypeId},
cmp,
ops::Range,
path::{Path, PathBuf},
@@ -2894,7 +2894,7 @@ impl Item for TextThreadEditor {
fn navigate(
&mut self,
- data: Box<dyn std::any::Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -6759,6 +6759,13 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
cx.run_until_parked();
let right_pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
+ right_pane.update(cx, |pane, cx| {
+ // Nav history is now cloned in an pane split, but that's inconvenient
+ // for this test, which uses the presence of a backwards history item as
+ // an indication that a preview item was successfully opened
+ pane.nav_history_mut().clear(cx);
+ });
+
pane.update(cx, |pane, cx| {
assert_eq!(pane.items_len(), 1);
assert_eq!(get_path(pane, 0, cx), path_1.clone());
@@ -517,7 +517,7 @@ impl Item for ChannelView {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -1,4 +1,7 @@
-use std::any::{Any, TypeId};
+use std::{
+ any::{Any, TypeId},
+ sync::Arc,
+};
use collections::HashMap;
use dap::StackFrameId;
@@ -333,7 +336,7 @@ impl Item for StackTraceView {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -757,7 +757,7 @@ impl Item for BufferDiagnosticsEditor {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -728,7 +728,7 @@ impl Item for ProjectDiagnosticsEditor {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -203,9 +203,9 @@ use ui::{
};
use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
use workspace::{
- CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
- RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection, TabBarSettings, Toast,
- ViewId, Workspace, WorkspaceId, WorkspaceSettings,
+ CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
+ OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
+ TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
searchable::{CollapseDirection, SearchEvent},
@@ -1778,7 +1778,7 @@ enum SelectSyntaxNodeScrollBehavior {
CursorBottom,
}
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
pub(crate) struct NavigationData {
cursor_anchor: Anchor,
cursor_position: Point,
@@ -14913,6 +14913,29 @@ impl Editor {
);
}
+ fn navigation_data(&self, cursor_anchor: Anchor, cx: &App) -> NavigationData {
+ let buffer = self.buffer.read(cx).read(cx);
+ let cursor_position = cursor_anchor.to_point(&buffer);
+ let scroll_anchor = self.scroll_manager.anchor();
+ let scroll_top_row = scroll_anchor.top_row(&buffer);
+ drop(buffer);
+
+ NavigationData {
+ cursor_anchor,
+ cursor_position,
+ scroll_anchor,
+ scroll_top_row,
+ }
+ }
+
+ fn navigation_entry(&self, cursor_anchor: Anchor, cx: &App) -> Option<NavigationEntry> {
+ let Some(history) = self.nav_history.clone() else {
+ return None;
+ };
+ let data = self.navigation_data(cursor_anchor, cx);
+ Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
+ }
+
fn push_to_nav_history(
&mut self,
cursor_anchor: Anchor,
@@ -14921,29 +14944,16 @@ impl Editor {
always: bool,
cx: &mut Context<Self>,
) {
+ let data = self.navigation_data(cursor_anchor, cx);
if let Some(nav_history) = self.nav_history.as_mut() {
- let buffer = self.buffer.read(cx).read(cx);
- let cursor_position = cursor_anchor.to_point(&buffer);
- let scroll_state = self.scroll_manager.anchor();
- let scroll_top_row = scroll_state.top_row(&buffer);
- drop(buffer);
-
if let Some(new_position) = new_position {
- let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
+ let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
return;
}
}
- nav_history.push(
- Some(NavigationData {
- cursor_anchor,
- cursor_position,
- scroll_anchor: scroll_state,
- scroll_top_row,
- }),
- cx,
- );
+ nav_history.push(Some(data), cx);
cx.emit(EditorEvent::PushedToNavHistory {
anchor: cursor_anchor,
is_deactivate,
@@ -17556,6 +17566,8 @@ impl Editor {
return Task::ready(Ok(Navigated::No));
};
+ let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
+
cx.spawn_in(window, async move |editor, cx| {
let Some(definitions) = definitions.await? else {
return Ok(Navigated::No);
@@ -17571,6 +17583,7 @@ impl Editor {
})
.map(HoverLink::Text)
.collect::<Vec<_>>(),
+ nav_entry,
split,
window,
cx,
@@ -17663,6 +17676,7 @@ impl Editor {
&mut self,
kind: Option<GotoDefinitionKind>,
definitions: Vec<HoverLink>,
+ origin: Option<NavigationEntry>,
split: bool,
window: &mut Window,
cx: &mut Context<Editor>,
@@ -17752,16 +17766,34 @@ impl Editor {
.update_in(cx, |workspace, window, cx| {
let allow_preview = PreviewTabsSettings::get_global(cx)
.enable_preview_multibuffer_from_code_navigation;
- Self::open_locations_in_multibuffer(
- workspace,
- locations,
- title,
- split,
- allow_preview,
- MultibufferSelectionMode::First,
- window,
- cx,
- )
+ if let Some((target_editor, target_pane)) =
+ Self::open_locations_in_multibuffer(
+ workspace,
+ locations,
+ title,
+ split,
+ allow_preview,
+ MultibufferSelectionMode::First,
+ window,
+ cx,
+ )
+ {
+ // We create our own nav history instead of using
+ // `target_editor.nav_history` because `nav_history`
+ // seems to be populated asynchronously when an item
+ // is added to a pane
+ let mut nav_history = target_pane
+ .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
+ target_editor.update(cx, |editor, cx| {
+ let nav_data = editor
+ .navigation_data(editor.selections.newest_anchor().head(), cx);
+ let target =
+ Some(nav_history.navigation_entry(Some(
+ Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
+ )));
+ nav_history.push_tag(origin, target);
+ })
+ }
})
.is_ok();
@@ -17801,21 +17833,26 @@ impl Editor {
let target_range = target_ranges.first().unwrap().clone();
editor.update_in(cx, |editor, window, cx| {
- let range = target_range.to_point(target_buffer.read(cx));
- let range = editor.range_for_match(&range);
+ let range = editor.range_for_match(&target_range);
let range = collapse_multiline_range(range);
if !split
&& Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
{
editor.go_to_singleton_buffer_range(range, window, cx);
+
+ let target =
+ editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
+ if let Some(mut nav_history) = editor.nav_history.clone() {
+ nav_history.push_tag(origin, target);
+ }
} else {
let Some(workspace) = workspace else {
return Navigated::No;
};
let pane = workspace.read(cx).active_pane().clone();
window.defer(cx, move |window, cx| {
- let target_editor: Entity<Self> =
+ let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
workspace.update(cx, |workspace, cx| {
let pane = if split {
workspace.adjacent_pane(window, cx)
@@ -17829,8 +17866,8 @@ impl Editor {
let allow_new_preview = preview_tabs_settings
.enable_preview_file_from_code_navigation;
- workspace.open_project_item(
- pane,
+ let editor = workspace.open_project_item(
+ pane.clone(),
target_buffer.clone(),
true,
true,
@@ -17838,13 +17875,30 @@ impl Editor {
allow_new_preview,
window,
cx,
- )
+ );
+ (editor, pane)
});
+ // We create our own nav history instead of using
+ // `target_editor.nav_history` because `nav_history`
+ // seems to be populated asynchronously when an item
+ // is added to a pane
+ let mut nav_history = target_pane
+ .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
target_editor.update(cx, |target_editor, cx| {
// When selecting a definition in a different buffer, disable the nav history
// to avoid creating a history entry at the previous cursor location.
pane.update(cx, |pane, _| pane.disable_history());
target_editor.go_to_singleton_buffer_range(range, window, cx);
+
+ let nav_data = target_editor.navigation_data(
+ target_editor.selections.newest_anchor().head(),
+ cx,
+ );
+ let target =
+ Some(nav_history.navigation_entry(Some(
+ Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
+ )));
+ nav_history.push_tag(origin, target);
pane.update(cx, |pane, _| pane.enable_history());
});
});
@@ -18203,10 +18257,10 @@ impl Editor {
multibuffer_selection_mode: MultibufferSelectionMode,
window: &mut Window,
cx: &mut Context<Workspace>,
- ) {
+ ) -> Option<(Entity<Editor>, Entity<Pane>)> {
if locations.is_empty() {
log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
- return;
+ return None;
}
let capability = workspace.project().read(cx).capability();
@@ -18287,7 +18341,7 @@ impl Editor {
}
});
- let item = Box::new(editor);
+ let item = Box::new(editor.clone());
let pane = if split {
workspace.adjacent_pane(window, cx)
@@ -18306,6 +18360,8 @@ impl Editor {
}
pane.add_item(item, activate_pane, true, destination_index, window, cx);
});
+
+ Some((editor, pane))
}
pub fn rename(
@@ -954,7 +954,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
let invalid_point = Point::new(9999, 0);
editor.navigate(
- Box::new(NavigationData {
+ Arc::new(NavigationData {
cursor_anchor: invalid_anchor,
cursor_position: invalid_point,
scroll_anchor: ScrollAnchor {
@@ -232,6 +232,13 @@ impl Editor {
else {
return Task::ready(Ok(Navigated::No));
};
+ let Some(mb_anchor) = self
+ .buffer()
+ .read(cx)
+ .buffer_anchor_to_anchor(&buffer, anchor, cx)
+ else {
+ return Task::ready(Ok(Navigated::No));
+ };
let links = hovered_link_state
.links
.into_iter()
@@ -243,8 +250,10 @@ impl Editor {
}
})
.collect();
+ let nav_entry = self.navigation_entry(mb_anchor, cx);
let split = Self::is_alt_pressed(&modifiers, cx);
- let navigate_task = self.navigate_to_hover_links(None, links, split, window, cx);
+ let navigate_task =
+ self.navigate_to_hover_links(None, links, nav_entry, split, window, cx);
self.select(SelectPhase::End, window, cx);
return navigate_task;
}
@@ -29,7 +29,7 @@ use project::{
use rpc::proto::{self, update_view};
use settings::Settings;
use std::{
- any::TypeId,
+ any::{Any, TypeId},
borrow::Cow,
cmp::{self, Ordering},
iter,
@@ -593,11 +593,11 @@ impl Item for Editor {
fn navigate(
&mut self,
- data: Box<dyn std::any::Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
- if let Ok(data) = data.downcast::<NavigationData>() {
+ if let Some(data) = data.downcast_ref::<NavigationData>() {
let newest_selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
let buffer = self.buffer.read(cx).read(cx);
let offset = if buffer.can_resolve(&data.cursor_anchor) {
@@ -76,6 +76,8 @@ pub fn go_to_parent_module(
return;
};
+ let nav_entry = editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
+
let project = project.clone();
let lsp_store = project.read(cx).lsp_store();
let upstream_client = lsp_store.read(cx).upstream_client();
@@ -123,6 +125,7 @@ pub fn go_to_parent_module(
editor.navigate_to_hover_links(
Some(GotoDefinitionKind::Declaration),
location_links.into_iter().map(HoverLink::Text).collect(),
+ nav_entry,
false,
window,
cx,
@@ -1005,7 +1005,7 @@ impl Item for CommitView {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -311,7 +311,7 @@ impl Item for FileDiffView {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -12,6 +12,7 @@ use project::{
git_store::{GitStore, Repository},
};
use std::any::{Any, TypeId};
+use std::sync::Arc;
use time::OffsetDateTime;
use ui::{Avatar, Chip, Divider, ListItem, WithScrollbar, prelude::*};
@@ -574,7 +575,12 @@ impl Item for FileHistoryView {
Task::ready(None)
}
- fn navigate(&mut self, _: Box<dyn Any>, _window: &mut Window, _: &mut Context<Self>) -> bool {
+ fn navigate(
+ &mut self,
+ _: Arc<dyn Any + Send>,
+ _window: &mut Window,
+ _: &mut Context<Self>,
+ ) -> bool {
false
}
@@ -840,7 +840,7 @@ impl Item for ProjectDiff {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -369,7 +369,7 @@ impl Item for TextDiffView {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -642,7 +642,7 @@ impl Item for ProjectSearchView {
fn navigate(
&mut self,
- data: Box<dyn Any>,
+ data: Arc<dyn Any + Send>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
@@ -219,7 +219,12 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
fn discarded(&self, _project: Entity<Project>, _window: &mut Window, _cx: &mut Context<Self>) {}
fn on_removed(&self, _cx: &App) {}
fn workspace_deactivated(&mut self, _window: &mut Window, _: &mut Context<Self>) {}
- fn navigate(&mut self, _: Box<dyn Any>, _window: &mut Window, _: &mut Context<Self>) -> bool {
+ fn navigate(
+ &mut self,
+ _: Arc<dyn Any + Send>,
+ _window: &mut Window,
+ _: &mut Context<Self>,
+ ) -> bool {
false
}
@@ -480,7 +485,7 @@ pub trait ItemHandle: 'static + Send {
fn deactivated(&self, window: &mut Window, cx: &mut App);
fn on_removed(&self, cx: &App);
fn workspace_deactivated(&self, window: &mut Window, cx: &mut App);
- fn navigate(&self, data: Box<dyn Any>, window: &mut Window, cx: &mut App) -> bool;
+ fn navigate(&self, data: Arc<dyn Any + Send>, window: &mut Window, cx: &mut App) -> bool;
fn item_id(&self) -> EntityId;
fn to_any_view(&self) -> AnyView;
fn is_dirty(&self, cx: &App) -> bool;
@@ -944,7 +949,7 @@ impl<T: Item> ItemHandle for Entity<T> {
self.update(cx, |this, cx| this.workspace_deactivated(window, cx));
}
- fn navigate(&self, data: Box<dyn Any>, window: &mut Window, cx: &mut App) -> bool {
+ fn navigate(&self, data: Arc<dyn Any + Send>, window: &mut Window, cx: &mut App) -> bool {
self.update(cx, |this, cx| this.navigate(data, window, cx))
}
@@ -1331,7 +1336,7 @@ pub mod test {
InteractiveElement, IntoElement, Render, SharedString, Task, WeakEntity, Window,
};
use project::{Project, ProjectEntryId, ProjectPath, WorktreeId};
- use std::{any::Any, cell::Cell};
+ use std::{any::Any, cell::Cell, sync::Arc};
use util::rel_path::rel_path;
pub struct TestProjectItem {
@@ -1564,14 +1569,18 @@ pub mod test {
fn navigate(
&mut self,
- state: Box<dyn Any>,
+ state: Arc<dyn Any + Send>,
_window: &mut Window,
_: &mut Context<Self>,
) -> bool {
- let state = *state.downcast::<String>().unwrap_or_default();
- if state != self.state {
- self.state = state;
- true
+ if let Some(state) = state.downcast_ref::<Box<String>>() {
+ let state = *state.clone();
+ if state != self.state {
+ false
+ } else {
+ self.state = state;
+ true
+ }
} else {
false
}
@@ -247,6 +247,10 @@ actions!(
GoBack,
/// Navigates forward in history.
GoForward,
+ /// Navigates back in the tag stack.
+ GoToOlderTag,
+ /// Navigates forward in the tag stack.
+ GoToNewerTag,
/// Joins this pane into the next pane.
JoinIntoNext,
/// Joins all panes into one.
@@ -429,6 +433,7 @@ pub struct ActivationHistoryEntry {
pub timestamp: usize,
}
+#[derive(Clone)]
pub struct ItemNavHistory {
history: NavHistory,
item: Arc<dyn WeakItemHandle>,
@@ -438,11 +443,14 @@ pub struct ItemNavHistory {
#[derive(Clone)]
pub struct NavHistory(Arc<Mutex<NavHistoryState>>);
+#[derive(Clone)]
struct NavHistoryState {
mode: NavigationMode,
backward_stack: VecDeque<NavigationEntry>,
forward_stack: VecDeque<NavigationEntry>,
closed_stack: VecDeque<NavigationEntry>,
+ tag_stack: VecDeque<TagStackEntry>,
+ tag_stack_pos: usize,
paths_by_item: HashMap<EntityId, (ProjectPath, Option<PathBuf>)>,
pane: WeakEntity<Pane>,
next_timestamp: Arc<AtomicUsize>,
@@ -459,13 +467,27 @@ pub enum NavigationMode {
Disabled,
}
+#[derive(Debug, Default, Copy, Clone)]
+pub enum TagNavigationMode {
+ #[default]
+ Older,
+ Newer,
+}
+
+#[derive(Clone)]
pub struct NavigationEntry {
- pub item: Arc<dyn WeakItemHandle>,
- pub data: Option<Box<dyn Any + Send>>,
+ pub item: Arc<dyn WeakItemHandle + Send + Sync>,
+ pub data: Option<Arc<dyn Any + Send + Sync>>,
pub timestamp: usize,
pub is_preview: bool,
}
+#[derive(Clone)]
+pub struct TagStackEntry {
+ pub origin: NavigationEntry,
+ pub target: NavigationEntry,
+}
+
#[derive(Clone)]
pub struct DraggedTab {
pub pane: Entity<Pane>,
@@ -534,6 +556,8 @@ impl Pane {
backward_stack: Default::default(),
forward_stack: Default::default(),
closed_stack: Default::default(),
+ tag_stack: Default::default(),
+ tag_stack_pos: Default::default(),
paths_by_item: Default::default(),
pane: handle,
next_timestamp,
@@ -839,6 +863,16 @@ impl Pane {
&mut self.nav_history
}
+ pub fn fork_nav_history(&self) -> NavHistory {
+ let history = self.nav_history.0.lock().clone();
+ NavHistory(Arc::new(Mutex::new(history)))
+ }
+
+ pub fn set_nav_history(&mut self, history: NavHistory, cx: &Context<Self>) {
+ self.nav_history = history;
+ self.nav_history().0.lock().pane = cx.entity().downgrade();
+ }
+
pub fn disable_history(&mut self) {
self.nav_history.disable();
}
@@ -879,6 +913,42 @@ impl Pane {
}
}
+ pub fn go_to_older_tag(
+ &mut self,
+ _: &GoToOlderTag,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(workspace) = self.workspace.upgrade() {
+ let pane = cx.entity().downgrade();
+ window.defer(cx, move |window, cx| {
+ workspace.update(cx, |workspace, cx| {
+ workspace
+ .navigate_tag_history(pane, TagNavigationMode::Older, window, cx)
+ .detach_and_log_err(cx)
+ })
+ })
+ }
+ }
+
+ pub fn go_to_newer_tag(
+ &mut self,
+ _: &GoToNewerTag,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(workspace) = self.workspace.upgrade() {
+ let pane = cx.entity().downgrade();
+ window.defer(cx, move |window, cx| {
+ workspace.update(cx, |workspace, cx| {
+ workspace
+ .navigate_tag_history(pane, TagNavigationMode::Newer, window, cx)
+ .detach_and_log_err(cx)
+ })
+ })
+ }
+ }
+
fn history_updated(&mut self, cx: &mut Context<Self>) {
self.toolbar.update(cx, |_, cx| cx.notify());
}
@@ -4159,6 +4229,8 @@ impl Render for Pane {
.on_action(cx.listener(Pane::zoom_out))
.on_action(cx.listener(Self::navigate_backward))
.on_action(cx.listener(Self::navigate_forward))
+ .on_action(cx.listener(Self::go_to_older_tag))
+ .on_action(cx.listener(Self::go_to_newer_tag))
.on_action(
cx.listener(|pane: &mut Pane, action: &ActivateItem, window, cx| {
pane.activate_item(
@@ -4391,7 +4463,7 @@ impl Render for Pane {
}
impl ItemNavHistory {
- pub fn push<D: 'static + Send + Any>(&mut self, data: Option<D>, cx: &mut App) {
+ pub fn push<D: 'static + Any + Send + Sync>(&mut self, data: Option<D>, cx: &mut App) {
if self
.item
.upgrade()
@@ -4402,6 +4474,21 @@ impl ItemNavHistory {
}
}
+ pub fn navigation_entry(&self, data: Option<Arc<dyn Any + Send + Sync>>) -> NavigationEntry {
+ NavigationEntry {
+ item: self.item.clone(),
+ data: data,
+ timestamp: 0, // not used
+ is_preview: self.is_preview,
+ }
+ }
+
+ pub fn push_tag(&mut self, origin: Option<NavigationEntry>, target: Option<NavigationEntry>) {
+ if let (Some(origin_entry), Some(target_entry)) = (origin, target) {
+ self.history.push_tag(origin_entry, target_entry);
+ }
+ }
+
pub fn pop_backward(&mut self, cx: &mut App) -> Option<NavigationEntry> {
self.history.pop(NavigationMode::GoingBack, cx)
}
@@ -4459,6 +4546,7 @@ impl NavHistory {
&& state.forward_stack.is_empty()
&& state.closed_stack.is_empty()
&& state.paths_by_item.is_empty()
+ && state.tag_stack.is_empty()
{
return;
}
@@ -4468,6 +4556,8 @@ impl NavHistory {
state.forward_stack.clear();
state.closed_stack.clear();
state.paths_by_item.clear();
+ state.tag_stack.clear();
+ state.tag_stack_pos = 0;
state.did_update(cx);
}
@@ -4488,10 +4578,10 @@ impl NavHistory {
entry
}
- pub fn push<D: 'static + Send + Any>(
+ pub fn push<D: 'static + Any + Send + Sync>(
&mut self,
data: Option<D>,
- item: Arc<dyn WeakItemHandle>,
+ item: Arc<dyn WeakItemHandle + Send + Sync>,
is_preview: bool,
cx: &mut App,
) {
@@ -4504,7 +4594,7 @@ impl NavHistory {
}
state.backward_stack.push_back(NavigationEntry {
item,
- data: data.map(|data| Box::new(data) as Box<dyn Any + Send>),
+ data: data.map(|data| Arc::new(data) as Arc<dyn Any + Send + Sync>),
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
is_preview,
});
@@ -4516,7 +4606,7 @@ impl NavHistory {
}
state.forward_stack.push_back(NavigationEntry {
item,
- data: data.map(|data| Box::new(data) as Box<dyn Any + Send>),
+ data: data.map(|data| Arc::new(data) as Arc<dyn Any + Send + Sync>),
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
is_preview,
});
@@ -4527,7 +4617,7 @@ impl NavHistory {
}
state.backward_stack.push_back(NavigationEntry {
item,
- data: data.map(|data| Box::new(data) as Box<dyn Any + Send>),
+ data: data.map(|data| Arc::new(data) as Arc<dyn Any + Send + Sync>),
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
is_preview,
});
@@ -4539,7 +4629,7 @@ impl NavHistory {
}
state.closed_stack.push_back(NavigationEntry {
item,
- data: data.map(|data| Box::new(data) as Box<dyn Any + Send>),
+ data: data.map(|data| Arc::new(data) as Arc<dyn Any + Send + Sync>),
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
is_preview,
});
@@ -4560,6 +4650,9 @@ impl NavHistory {
state
.closed_stack
.retain(|entry| entry.item.id() != item_id);
+ state
+ .tag_stack
+ .retain(|entry| entry.origin.item.id() != item_id && entry.target.item.id() != item_id);
}
pub fn rename_item(
@@ -4579,6 +4672,41 @@ impl NavHistory {
pub fn path_for_item(&self, item_id: EntityId) -> Option<(ProjectPath, Option<PathBuf>)> {
self.0.lock().paths_by_item.get(&item_id).cloned()
}
+
+ pub fn push_tag(&mut self, origin: NavigationEntry, target: NavigationEntry) {
+ let mut state = self.0.lock();
+ let truncate_to = state.tag_stack_pos;
+ state.tag_stack.truncate(truncate_to);
+ state.tag_stack.push_back(TagStackEntry { origin, target });
+ state.tag_stack_pos = state.tag_stack.len();
+ }
+
+ pub fn pop_tag(&mut self, mode: TagNavigationMode) -> Option<NavigationEntry> {
+ let mut state = self.0.lock();
+ match mode {
+ TagNavigationMode::Older => {
+ if state.tag_stack_pos > 0 {
+ state.tag_stack_pos -= 1;
+ state
+ .tag_stack
+ .get(state.tag_stack_pos)
+ .map(|e| e.origin.clone())
+ } else {
+ None
+ }
+ }
+ TagNavigationMode::Newer => {
+ let entry = state
+ .tag_stack
+ .get(state.tag_stack_pos)
+ .map(|e| e.target.clone());
+ if state.tag_stack_pos < state.tag_stack.len() {
+ state.tag_stack_pos += 1;
+ }
+ entry
+ }
+ }
+ }
}
impl NavHistoryState {
@@ -2080,13 +2080,40 @@ impl Workspace {
mode: NavigationMode,
window: &mut Window,
cx: &mut Context<Workspace>,
+ ) -> Task<Result<()>> {
+ self.navigate_history_impl(pane, mode, window, |history, cx| history.pop(mode, cx), cx)
+ }
+
+ fn navigate_tag_history(
+ &mut self,
+ pane: WeakEntity<Pane>,
+ mode: TagNavigationMode,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+ ) -> Task<Result<()>> {
+ self.navigate_history_impl(
+ pane,
+ NavigationMode::Normal,
+ window,
+ |history, _cx| history.pop_tag(mode),
+ cx,
+ )
+ }
+
+ fn navigate_history_impl(
+ &mut self,
+ pane: WeakEntity<Pane>,
+ mode: NavigationMode,
+ window: &mut Window,
+ mut cb: impl FnMut(&mut NavHistory, &mut App) -> Option<NavigationEntry>,
+ cx: &mut Context<Workspace>,
) -> Task<Result<()>> {
let to_load = if let Some(pane) = pane.upgrade() {
pane.update(cx, |pane, cx| {
window.focus(&pane.focus_handle(cx), cx);
loop {
// Retrieve the weak item handle from the history.
- let entry = pane.nav_history_mut().pop(mode, cx)?;
+ let entry = cb(pane.nav_history_mut(), cx)?;
// If the item is still present in this pane, then activate it.
if let Some(index) = entry
@@ -4553,7 +4580,9 @@ impl Workspace {
if let Some(clone) = task.await {
this.update_in(cx, |this, window, cx| {
let new_pane = this.add_pane(window, cx);
+ let nav_history = pane.read(cx).fork_nav_history();
new_pane.update(cx, |pane, cx| {
+ pane.set_nav_history(nav_history, cx);
pane.add_item(clone, true, true, None, window, cx)
});
this.center.split(&pane, &new_pane, direction, cx).unwrap();