Restore editor state on reopen (#27672)
Closes https://github.com/zed-industries/zed/issues/11626 Part of https://github.com/zed-industries/zed/issues/12853 `"restore_on_file_reopen": true` in workspace settings can now be used to enable and disable editor data between file reopens in the same pane: https://github.com/user-attachments/assets/8d938ee1-d854-42a8-bbc3-2a4e4d7d5933 The settings are generic and panes' data store can be extended for further entities, beyond editors. --------------- Impl details: Currently, the project entry IDs seem to be stable across file reopens, unlike BufferIds, so those were used. Originally, the DB data was considered over in-memory one as editors serialize their state anyway, but managing and exposing PaneIds out of the DB is quite tedious and joining the DB data otherwise is not possible. Release Notes: - Started to restore editor state on reopen
This commit is contained in:
parent
bbd1e628f0
commit
e11e7df724
13 changed files with 611 additions and 48 deletions
|
@ -1031,11 +1031,19 @@ impl<T: Item> WeakItemHandle for WeakEntity<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ProjectItemKind(pub &'static str);
|
||||
|
||||
pub trait ProjectItem: Item {
|
||||
type Item: project::ProjectItem;
|
||||
|
||||
fn project_item_kind() -> Option<ProjectItemKind> {
|
||||
None
|
||||
}
|
||||
|
||||
fn for_project_item(
|
||||
project: Entity<Project>,
|
||||
pane: &Pane,
|
||||
item: Entity<Self::Item>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
item::{
|
||||
ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
|
||||
ShowCloseButton, ShowDiagnostics, TabContentParams, TabTooltipContent, WeakItemHandle,
|
||||
ProjectItemKind, ShowCloseButton, ShowDiagnostics, TabContentParams, TabTooltipContent,
|
||||
WeakItemHandle,
|
||||
},
|
||||
move_item,
|
||||
notifications::NotifyResultExt,
|
||||
|
@ -9,6 +10,7 @@ use crate::{
|
|||
workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings},
|
||||
CloseWindow, NewFile, NewTerminal, OpenInTerminal, OpenOptions, OpenTerminal, OpenVisible,
|
||||
SplitDirection, ToggleFileFinder, ToggleProjectSymbols, ToggleZoom, Workspace,
|
||||
WorkspaceItemBuilder,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::{BTreeSet, HashMap, HashSet, VecDeque};
|
||||
|
@ -321,6 +323,8 @@ pub struct Pane {
|
|||
pinned_tab_count: usize,
|
||||
diagnostics: HashMap<ProjectPath, DiagnosticSeverity>,
|
||||
zoom_out_on_close: bool,
|
||||
/// If a certain project item wants to get recreated with specific data, it can persist its data before the recreation here.
|
||||
pub project_item_restoration_data: HashMap<ProjectItemKind, Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
pub struct ActivationHistoryEntry {
|
||||
|
@ -526,6 +530,7 @@ impl Pane {
|
|||
pinned_tab_count: 0,
|
||||
diagnostics: Default::default(),
|
||||
zoom_out_on_close: true,
|
||||
project_item_restoration_data: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -859,7 +864,7 @@ impl Pane {
|
|||
suggested_position: Option<usize>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
build_item: impl FnOnce(&mut Window, &mut Context<Pane>) -> Box<dyn ItemHandle>,
|
||||
build_item: WorkspaceItemBuilder,
|
||||
) -> Box<dyn ItemHandle> {
|
||||
let mut existing_item = None;
|
||||
if let Some(project_entry_id) = project_entry_id {
|
||||
|
@ -896,7 +901,7 @@ impl Pane {
|
|||
suggested_position
|
||||
};
|
||||
|
||||
let new_item = build_item(window, cx);
|
||||
let new_item = build_item(self, window, cx);
|
||||
|
||||
if allow_preview {
|
||||
self.set_preview_item_id(Some(new_item.item_id()), cx);
|
||||
|
|
|
@ -457,7 +457,8 @@ type ProjectItemOpener = fn(
|
|||
)
|
||||
-> Option<Task<Result<(Option<ProjectEntryId>, WorkspaceItemBuilder)>>>;
|
||||
|
||||
type WorkspaceItemBuilder = Box<dyn FnOnce(&mut Window, &mut Context<Pane>) -> Box<dyn ItemHandle>>;
|
||||
type WorkspaceItemBuilder =
|
||||
Box<dyn FnOnce(&mut Pane, &mut Window, &mut Context<Pane>) -> Box<dyn ItemHandle>>;
|
||||
|
||||
impl Global for ProjectItemOpeners {}
|
||||
|
||||
|
@ -473,10 +474,13 @@ pub fn register_project_item<I: ProjectItem>(cx: &mut App) {
|
|||
let project_item = project_item.await?;
|
||||
let project_entry_id: Option<ProjectEntryId> =
|
||||
project_item.read_with(cx, project::ProjectItem::entry_id)?;
|
||||
let build_workspace_item = Box::new(|window: &mut Window, cx: &mut Context<Pane>| {
|
||||
Box::new(cx.new(|cx| I::for_project_item(project, project_item, window, cx)))
|
||||
as Box<dyn ItemHandle>
|
||||
}) as Box<_>;
|
||||
let build_workspace_item = Box::new(
|
||||
|pane: &mut Pane, window: &mut Window, cx: &mut Context<Pane>| {
|
||||
Box::new(
|
||||
cx.new(|cx| I::for_project_item(project, pane, project_item, window, cx)),
|
||||
) as Box<dyn ItemHandle>
|
||||
},
|
||||
) as Box<_>;
|
||||
Ok((project_entry_id, build_workspace_item))
|
||||
}))
|
||||
});
|
||||
|
@ -3060,8 +3064,9 @@ impl Workspace {
|
|||
return item;
|
||||
}
|
||||
|
||||
let item =
|
||||
cx.new(|cx| T::for_project_item(self.project().clone(), project_item, window, cx));
|
||||
let item = pane.update(cx, |pane, cx| {
|
||||
cx.new(|cx| T::for_project_item(self.project().clone(), pane, project_item, window, cx))
|
||||
});
|
||||
let item_id = item.item_id();
|
||||
let mut destination_index = None;
|
||||
pane.update(cx, |pane, cx| {
|
||||
|
@ -8720,6 +8725,7 @@ mod tests {
|
|||
|
||||
fn for_project_item(
|
||||
_project: Entity<Project>,
|
||||
_pane: &Pane,
|
||||
_item: Entity<Self::Item>,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
|
@ -8791,6 +8797,7 @@ mod tests {
|
|||
|
||||
fn for_project_item(
|
||||
_project: Entity<Project>,
|
||||
_pane: &Pane,
|
||||
_item: Entity<Self::Item>,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
|
@ -8834,6 +8841,7 @@ mod tests {
|
|||
|
||||
fn for_project_item(
|
||||
_project: Entity<Project>,
|
||||
_pane: &Pane,
|
||||
_item: Entity<Self::Item>,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
|
|
|
@ -17,6 +17,7 @@ pub struct WorkspaceSettings {
|
|||
pub show_call_status_icon: bool,
|
||||
pub autosave: AutosaveSetting,
|
||||
pub restore_on_startup: RestoreOnStartupBehavior,
|
||||
pub restore_on_file_reopen: bool,
|
||||
pub drop_target_size: f32,
|
||||
pub use_system_path_prompts: bool,
|
||||
pub use_system_prompts: bool,
|
||||
|
@ -134,6 +135,15 @@ pub struct WorkspaceSettingsContent {
|
|||
/// Values: none, last_workspace, last_session
|
||||
/// Default: last_session
|
||||
pub restore_on_startup: Option<RestoreOnStartupBehavior>,
|
||||
/// Whether to attempt to restore previous file's state when opening it again.
|
||||
/// The state is stored per pane.
|
||||
/// When disabled, defaults are applied instead of the state restoration.
|
||||
///
|
||||
/// E.g. for editors, selections, folds and scroll positions are restored, if the same file is closed and, later, opened again in the same pane.
|
||||
/// When disabled, a single selection in the very beginning of the file, zero scroll position and no folds state is used as a default.
|
||||
///
|
||||
/// Default: true
|
||||
pub restore_on_file_reopen: Option<bool>,
|
||||
/// The size of the workspace split drop targets on the outer edges.
|
||||
/// Given as a fraction that will be multiplied by the smaller dimension of the workspace.
|
||||
///
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue