From 7c2993de755f57b2ddc6ee1acdd4f20df78dffbf Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 7 Jun 2024 16:35:55 -0600 Subject: [PATCH] Fix deserialization for remote projects --- crates/editor/src/items.rs | 12 +- crates/language/src/buffer.rs | 10 +- crates/project/src/prettier_support.rs | 2 +- crates/project/src/project.rs | 17 ++- crates/workspace/src/workspace.rs | 201 +++++++++++++------------ crates/worktree/src/worktree.rs | 18 +-- 6 files changed, 139 insertions(+), 121 deletions(-) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 2d5ea9be7f5..0082e025aeb 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -859,15 +859,11 @@ impl Item for Editor { item_id: ItemId, cx: &mut AppContext, ) { - if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) { - let path = file.abs_path(cx); + if let Some(file) = buffer.read(cx).file() { + let path = file.abs_path(cx).to_path_buf(); cx.background_executor() - .spawn(async move { - DB.save_path(item_id, workspace_id, path.clone()) - .await - .log_err() - }) + .spawn(async move { DB.save_path(item_id, workspace_id, path).await.log_err() }) .detach(); } } @@ -943,7 +939,7 @@ impl Item for Editor { .context("No path stored for this editor")?; let (worktree, path) = project - .find_local_worktree(&path, cx) + .find_worktree(&path, cx) .with_context(|| format!("No worktree for path: {path:?}"))?; let project_path = ProjectPath { worktree_id: worktree.read(cx).id(), diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 68dd36201c5..96175958bce 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -343,6 +343,9 @@ pub trait File: Send + Sync { /// Returns the path of this file relative to the worktree's root directory. fn path(&self) -> &Arc; + /// Returns the path of this file relative to the worktree's root directory. + fn abs_path(&self, cx: &AppContext) -> PathBuf; + /// Returns the path of this file relative to the worktree's parent directory (this means it /// includes the name of the worktree's root folder). fn full_path(&self, cx: &AppContext) -> PathBuf; @@ -376,9 +379,6 @@ pub trait File: Send + Sync { /// The file associated with a buffer, in the case where the file is on the local disk. pub trait LocalFile: File { - /// Returns the absolute path of this file. - fn abs_path(&self, cx: &AppContext) -> PathBuf; - /// Loads the file's contents from disk. fn load(&self, cx: &AppContext) -> Task>; @@ -3906,6 +3906,10 @@ impl File for TestFile { &self.path } + fn abs_path(&self, cx: &AppContext) -> PathBuf { + panic!("unimplemented") + } + fn full_path(&self, _: &gpui::AppContext) -> PathBuf { PathBuf::from(&self.root_name).join(self.path.as_ref()) } diff --git a/crates/project/src/prettier_support.rs b/crates/project/src/prettier_support.rs index 706c4372bb9..0112a07ee65 100644 --- a/crates/project/src/prettier_support.rs +++ b/crates/project/src/prettier_support.rs @@ -14,7 +14,7 @@ use futures::{ use gpui::{AsyncAppContext, Model, ModelContext, Task, WeakModel}; use language::{ language_settings::{Formatter, LanguageSettings}, - Buffer, LanguageServerName, LocalFile, + Buffer, File as _, LanguageServerName, }; use lsp::{LanguageServer, LanguageServerId}; use node_runtime::NodeRuntime; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b0fa3dbffae..9c5fb8e8350 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -48,7 +48,7 @@ use language::{ }, range_from_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability, CodeLabel, ContextProvider, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Documentation, - Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, + Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LspAdapterDelegate, Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped, }; @@ -7425,6 +7425,21 @@ impl Project { } } + pub fn find_worktree( + &self, + abs_path: &Path, + cx: &AppContext, + ) -> Option<(Model, PathBuf)> { + for tree in &self.worktrees { + if let Some(tree) = tree.upgrade() { + if let Some(relative_path) = abs_path.strip_prefix(&tree.read(cx).abs_path()).ok() { + return Some((tree.clone(), relative_path.into())); + } + } + } + None + } + pub fn find_local_worktree( &self, abs_path: &Path, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 60868be0fa7..7f053265f0b 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -950,81 +950,101 @@ impl Workspace { } } - let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() { - serialized_workspace.id - } else { - DB.next_id().await.unwrap_or_else(|_| Default::default()) - }; - - let window = if let Some(window) = requesting_window { - cx.update_window(window.into(), |_, cx| { - cx.replace_root_view(|cx| { - Workspace::new( - Some(workspace_id), - project_handle.clone(), - app_state.clone(), - cx, - ) - }); - })?; - window - } else { - let window_bounds_override = window_bounds_env_override(); - - let (window_bounds, display) = if let Some(bounds) = window_bounds_override { - (Some(WindowBounds::Windowed(bounds)), None) - } else { - let restorable_bounds = serialized_workspace - .as_ref() - .and_then(|workspace| Some((workspace.display?, workspace.window_bounds?))) - .or_else(|| { - let (display, window_bounds) = DB.last_window().log_err()?; - Some((display?, window_bounds?)) - }); - - if let Some((serialized_display, serialized_status)) = restorable_bounds { - (Some(serialized_status.0), Some(serialized_display)) - } else { - (None, None) - } - }; - - // Use the serialized workspace to construct the new window - let mut options = cx.update(|cx| (app_state.build_window_options)(display, cx))?; - options.window_bounds = window_bounds; - let centered_layout = serialized_workspace - .as_ref() - .map(|w| w.centered_layout) - .unwrap_or(false); - cx.open_window(options, { - let app_state = app_state.clone(); - let project_handle = project_handle.clone(); - move |cx| { - cx.new_view(|cx| { - let mut workspace = - Workspace::new(Some(workspace_id), project_handle, app_state, cx); - workspace.centered_layout = centered_layout; - workspace - }) - } - })? - }; - - notify_if_database_failed(window, &mut cx); - let opened_items = window - .update(&mut cx, |_workspace, cx| { - open_items(serialized_workspace, project_paths, app_state, cx) - })? - .await - .unwrap_or_default(); - - window - .update(&mut cx, |_, cx| cx.activate_window()) - .log_err(); - Ok((window, opened_items)) + Workspace::reopen_serialized( + project_handle, + serialized_workspace, + project_paths, + app_state, + requesting_window, + &mut cx, + ) + .await }) } + async fn reopen_serialized( + project_handle: Model, + serialized_workspace: Option, + project_paths: Vec<(PathBuf, Option)>, + app_state: Arc, + requesting_window: Option>, + cx: &mut AsyncAppContext, + ) -> anyhow::Result<( + WindowHandle, + Vec, anyhow::Error>>>, + )> { + let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() { + serialized_workspace.id + } else { + DB.next_id().await.unwrap_or_else(|_| Default::default()) + }; + + let window = if let Some(window) = requesting_window { + cx.update_window(window.into(), |_, cx| { + cx.replace_root_view(|cx| { + Workspace::new( + Some(workspace_id), + project_handle.clone(), + app_state.clone(), + cx, + ) + }); + })?; + window + } else { + let window_bounds_override = window_bounds_env_override(); + + let (window_bounds, display) = if let Some(bounds) = window_bounds_override { + (Some(WindowBounds::Windowed(bounds)), None) + } else { + let restorable_bounds = serialized_workspace + .as_ref() + .and_then(|workspace| Some((workspace.display?, workspace.window_bounds?))) + .or_else(|| { + let (display, window_bounds) = DB.last_window().log_err()?; + Some((display?, window_bounds?)) + }); + + if let Some((serialized_display, serialized_status)) = restorable_bounds { + (Some(serialized_status.0), Some(serialized_display)) + } else { + (None, None) + } + }; + + // Use the serialized workspace to construct the new window + let mut options = cx.update(|cx| (app_state.build_window_options)(display, cx))?; + options.window_bounds = window_bounds; + let centered_layout = serialized_workspace + .as_ref() + .map(|w| w.centered_layout) + .unwrap_or(false); + cx.open_window(options, { + let app_state = app_state.clone(); + let project_handle = project_handle.clone(); + move |cx| { + cx.new_view(|cx| { + let mut workspace = + Workspace::new(Some(workspace_id), project_handle, app_state, cx); + workspace.centered_layout = centered_layout; + workspace + }) + } + })? + }; + + notify_if_database_failed(window, cx); + let opened_items = window + .update(cx, |_workspace, cx| { + open_items(serialized_workspace, project_paths, app_state, cx) + })? + .await + .unwrap_or_default(); + + window.update(cx, |_, cx| cx.activate_window()).log_err(); + Ok((window, opened_items)) + } + pub fn weak_handle(&self) -> WeakView { self.weak_self.clone() } @@ -4984,36 +5004,19 @@ pub fn join_dev_server_project( cx.clone(), ) .await?; - let serialized_workspace: Option = persistence::DB.workspace_for_dev_server_project(dev_server_project_id); - let workspace_id = if let Some(serialized_workspace) = serialized_workspace { - serialized_workspace.id - } else { - persistence::DB.next_id().await? - }; - - if let Some(window_to_replace) = window_to_replace { - cx.update_window(window_to_replace.into(), |_, cx| { - cx.replace_root_view(|cx| { - Workspace::new(Some(workspace_id), project, app_state.clone(), cx) - }); - })?; - window_to_replace - } else { - let window_bounds_override = window_bounds_env_override(); - cx.update(|cx| { - let mut options = (app_state.build_window_options)(None, cx); - options.window_bounds = - window_bounds_override.map(|bounds| WindowBounds::Windowed(bounds)); - cx.open_window(options, |cx| { - cx.new_view(|cx| { - Workspace::new(Some(workspace_id), project, app_state.clone(), cx) - }) - }) - })? - } + Workspace::reopen_serialized( + project, + serialized_workspace, + vec![], + app_state, + window_to_replace, + &mut cx, + ) + .await? + .0 }; workspace.update(&mut cx, |_, cx| { diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 11527dbd998..af738cb6f7c 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -2947,6 +2947,15 @@ impl language::File for File { &self.path } + fn abs_path(&self, cx: &AppContext) -> PathBuf { + let worktree_path = &self.worktree.read(cx).abs_path; + if self.path.as_ref() == Path::new("") { + worktree_path.to_path_buf() + } else { + worktree_path.join(&self.path) + } + } + fn full_path(&self, cx: &AppContext) -> PathBuf { let mut full_path = PathBuf::new(); let worktree = self.worktree.read(cx); @@ -3007,15 +3016,6 @@ impl language::File for File { } impl language::LocalFile for File { - fn abs_path(&self, cx: &AppContext) -> PathBuf { - let worktree_path = &self.worktree.read(cx).as_local().unwrap().abs_path; - if self.path.as_ref() == Path::new("") { - worktree_path.to_path_buf() - } else { - worktree_path.join(&self.path) - } - } - fn load(&self, cx: &AppContext) -> Task> { let worktree = self.worktree.read(cx).as_local().unwrap(); let abs_path = worktree.absolutize(&self.path);