Navigate to previous positions in editors when using navigation history
This commit is contained in:
parent
e43d33cdad
commit
11b7270f68
4 changed files with 107 additions and 66 deletions
|
@ -2399,6 +2399,8 @@ impl Editor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.push_to_navigation_history(cx);
|
||||||
|
|
||||||
let selection = Selection {
|
let selection = Selection {
|
||||||
id: post_inc(&mut self.next_selection_id),
|
id: post_inc(&mut self.next_selection_id),
|
||||||
start: 0,
|
start: 0,
|
||||||
|
@ -2421,6 +2423,8 @@ impl Editor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.push_to_navigation_history(cx);
|
||||||
|
|
||||||
let cursor = self.buffer.read(cx).read(cx).len();
|
let cursor = self.buffer.read(cx).read(cx).len();
|
||||||
let selection = Selection {
|
let selection = Selection {
|
||||||
id: post_inc(&mut self.next_selection_id),
|
id: post_inc(&mut self.next_selection_id),
|
||||||
|
@ -2432,6 +2436,15 @@ impl Editor {
|
||||||
self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
|
self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_to_navigation_history(&self, cx: &mut ViewContext<Self>) {
|
||||||
|
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<Self>) {
|
pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
|
||||||
let mut selection = self.local_selections::<usize>(cx).first().unwrap().clone();
|
let mut selection = self.local_selections::<usize>(cx).first().unwrap().clone();
|
||||||
selection.set_head(self.buffer.read(cx).read(cx).len());
|
selection.set_head(self.buffer.read(cx).read(cx).len());
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{Autoscroll, Editor, Event};
|
use crate::{Anchor, Autoscroll, Editor, Event, MultiBuffer, ToOffset, ToPoint as _};
|
||||||
use crate::{MultiBuffer, ToPoint as _};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*, AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, RenderContext,
|
elements::*, AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, RenderContext,
|
||||||
|
@ -106,6 +105,13 @@ impl ItemView for Editor {
|
||||||
BufferItemHandle(self.buffer.read(cx).as_singleton().unwrap())
|
BufferItemHandle(self.buffer.read(cx).as_singleton().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(anchor) = data.downcast_ref::<Anchor>() {
|
||||||
|
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 {
|
fn title(&self, cx: &AppContext) -> String {
|
||||||
let filename = self
|
let filename = self
|
||||||
.buffer()
|
.buffer()
|
||||||
|
@ -134,9 +140,7 @@ impl ItemView for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(navigation) = self.navigation.as_ref() {
|
self.push_to_navigation_history(cx);
|
||||||
navigation.push::<(), _>(None, cx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_dirty(&self, cx: &AppContext) -> bool {
|
fn is_dirty(&self, cx: &AppContext) -> bool {
|
||||||
|
|
|
@ -84,6 +84,7 @@ struct NavigationHistory {
|
||||||
paths_by_item: HashMap<usize, ProjectPath>,
|
paths_by_item: HashMap<usize, ProjectPath>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
enum NavigationHistoryMode {
|
enum NavigationHistoryMode {
|
||||||
Normal,
|
Normal,
|
||||||
GoingBack,
|
GoingBack,
|
||||||
|
@ -116,33 +117,56 @@ impl Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn go_back(workspace: &mut Workspace, _: &GoBack, cx: &mut ViewContext<Workspace>) {
|
pub fn go_back(workspace: &mut Workspace, _: &GoBack, cx: &mut ViewContext<Workspace>) {
|
||||||
let project_path = workspace.active_pane().update(cx, |pane, cx| {
|
Self::navigate_history(workspace, NavigationHistoryMode::GoingBack, cx);
|
||||||
let mut navigation = pane.navigation.0.borrow_mut();
|
}
|
||||||
if let Some(entry) = navigation.backward_stack.pop() {
|
|
||||||
|
pub fn go_forward(workspace: &mut Workspace, _: &GoForward, cx: &mut ViewContext<Workspace>) {
|
||||||
|
Self::navigate_history(workspace, NavigationHistoryMode::GoingForward, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn navigate_history(
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
mode: NavigationHistoryMode,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> 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)?;
|
||||||
|
|
||||||
|
// If the item is still present in this pane, then activate it.
|
||||||
if let Some(index) = entry
|
if let Some(index) = entry
|
||||||
.item_view
|
.item_view
|
||||||
.upgrade(cx)
|
.upgrade(cx)
|
||||||
.and_then(|v| pane.index_for_item_view(v.as_ref()))
|
.and_then(|v| pane.index_for_item_view(v.as_ref()))
|
||||||
{
|
{
|
||||||
if let Some(item_view) = pane.active_item() {
|
if let Some(item_view) = pane.active_item() {
|
||||||
pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::GoingBack;
|
pane.navigation.set_mode(mode);
|
||||||
item_view.deactivated(cx);
|
item_view.deactivated(cx);
|
||||||
pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::Normal;
|
pane.navigation.set_mode(NavigationHistoryMode::Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
pane.active_item_index = index;
|
pane.active_item_index = index;
|
||||||
drop(navigation);
|
|
||||||
pane.focus_active_item(cx);
|
pane.focus_active_item(cx);
|
||||||
|
if let Some(data) = entry.data {
|
||||||
|
pane.active_item()?.navigate(data, cx);
|
||||||
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
} else {
|
|
||||||
return navigation.paths_by_item.get(&entry.item_view.id()).cloned();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
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 let Some(project_path) = project_path {
|
// If the item was no longer present, then load it again from its previous path.
|
||||||
let task = workspace.load_path(project_path, cx);
|
let task = workspace.load_path(project_path, cx);
|
||||||
cx.spawn(|workspace, mut cx| {
|
cx.spawn(|workspace, mut cx| {
|
||||||
async move {
|
async move {
|
||||||
|
@ -150,9 +174,15 @@ impl Pane {
|
||||||
workspace.update(&mut cx, |workspace, cx| {
|
workspace.update(&mut cx, |workspace, cx| {
|
||||||
let pane = workspace.active_pane().clone();
|
let pane = workspace.active_pane().clone();
|
||||||
pane.update(cx, |pane, cx| {
|
pane.update(cx, |pane, cx| {
|
||||||
pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::GoingBack;
|
pane.navigation.set_mode(mode);
|
||||||
pane.open_item(item, workspace, cx);
|
let item_view = pane.open_item(item, workspace, cx);
|
||||||
pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::Normal;
|
pane.navigation.set_mode(NavigationHistoryMode::Normal);
|
||||||
|
|
||||||
|
if let Some(data) = entry.data {
|
||||||
|
item_view.navigate(data, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -160,33 +190,8 @@ impl Pane {
|
||||||
.log_err()
|
.log_err()
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn go_forward(&mut self, _: &GoForward, cx: &mut ViewContext<Self>) {
|
None
|
||||||
if self.navigation.0.borrow().forward_stack.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut navigation = self.navigation.0.borrow_mut();
|
|
||||||
if let Some(entry) = navigation.forward_stack.pop() {
|
|
||||||
if let Some(index) = entry
|
|
||||||
.item_view
|
|
||||||
.upgrade(cx)
|
|
||||||
.and_then(|v| self.index_for_item_view(v.as_ref()))
|
|
||||||
{
|
|
||||||
self.active_item_index = index;
|
|
||||||
drop(navigation);
|
|
||||||
self.focus_active_item(cx);
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_item<T>(
|
pub fn open_item<T>(
|
||||||
|
@ -511,6 +516,18 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Navigation {
|
impl Navigation {
|
||||||
|
fn pop(&self, mode: NavigationHistoryMode) -> Option<NavigationEntry> {
|
||||||
|
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<D: 'static + Any, T: ItemView>(&self, data: Option<D>, cx: &mut ViewContext<T>) {
|
pub fn push<D: 'static + Any, T: ItemView>(&self, data: Option<D>, cx: &mut ViewContext<T>) {
|
||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
match state.mode {
|
match state.mode {
|
||||||
|
|
|
@ -33,6 +33,7 @@ use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItem
|
||||||
use status_bar::StatusBar;
|
use status_bar::StatusBar;
|
||||||
pub use status_bar::StatusItemView;
|
pub use status_bar::StatusItemView;
|
||||||
use std::{
|
use std::{
|
||||||
|
any::Any,
|
||||||
future::Future,
|
future::Future,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -148,6 +149,7 @@ pub trait ItemView: View {
|
||||||
|
|
||||||
fn added_to_pane(&mut self, _: Rc<Navigation>, _: &mut ViewContext<Self>) {}
|
fn added_to_pane(&mut self, _: Rc<Navigation>, _: &mut ViewContext<Self>) {}
|
||||||
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
||||||
|
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {}
|
||||||
fn item_handle(&self, cx: &AppContext) -> Self::ItemHandle;
|
fn item_handle(&self, cx: &AppContext) -> Self::ItemHandle;
|
||||||
fn title(&self, cx: &AppContext) -> String;
|
fn title(&self, cx: &AppContext) -> String;
|
||||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
||||||
|
@ -211,6 +213,7 @@ pub trait ItemViewHandle {
|
||||||
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
|
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
|
||||||
fn added_to_pane(&mut self, cx: &mut ViewContext<Pane>);
|
fn added_to_pane(&mut self, cx: &mut ViewContext<Pane>);
|
||||||
fn deactivated(&self, cx: &mut MutableAppContext);
|
fn deactivated(&self, cx: &mut MutableAppContext);
|
||||||
|
fn navigate(&self, data: Box<dyn Any>, cx: &mut MutableAppContext);
|
||||||
fn id(&self) -> usize;
|
fn id(&self) -> usize;
|
||||||
fn to_any(&self) -> AnyViewHandle;
|
fn to_any(&self) -> AnyViewHandle;
|
||||||
fn is_dirty(&self, cx: &AppContext) -> bool;
|
fn is_dirty(&self, cx: &AppContext) -> bool;
|
||||||
|
@ -368,6 +371,10 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
||||||
self.update(cx, |this, cx| this.deactivated(cx));
|
self.update(cx, |this, cx| this.deactivated(cx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn navigate(&self, data: Box<dyn Any>, cx: &mut MutableAppContext) {
|
||||||
|
self.update(cx, |this, cx| this.navigate(data, cx));
|
||||||
|
}
|
||||||
|
|
||||||
fn save(&self, cx: &mut MutableAppContext) -> Result<Task<Result<()>>> {
|
fn save(&self, cx: &mut MutableAppContext) -> Result<Task<Result<()>>> {
|
||||||
self.update(cx, |item, cx| item.save(cx))
|
self.update(cx, |item, cx| item.save(cx))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue