Fix deserialization for remote projects

This commit is contained in:
Conrad Irwin 2024-06-07 16:35:55 -06:00
parent 0a9ab49ffb
commit 7c2993de75
6 changed files with 139 additions and 121 deletions

View file

@ -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(),

View file

@ -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<Path>;
/// 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<Result<String>>;
@ -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())
}

View file

@ -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;

View file

@ -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<Worktree>, 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,

View file

@ -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<Project>,
serialized_workspace: Option<SerializedWorkspace>,
project_paths: Vec<(PathBuf, Option<ProjectPath>)>,
app_state: Arc<AppState>,
requesting_window: Option<WindowHandle<Workspace>>,
cx: &mut AsyncAppContext,
) -> anyhow::Result<(
WindowHandle<Workspace>,
Vec<Option<Result<Box<dyn ItemHandle>, 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> {
self.weak_self.clone()
}
@ -4984,36 +5004,19 @@ pub fn join_dev_server_project(
cx.clone(),
)
.await?;
let serialized_workspace: Option<SerializedWorkspace> =
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| {

View file

@ -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<Result<String>> {
let worktree = self.worktree.read(cx).as_local().unwrap();
let abs_path = worktree.absolutize(&self.path);