From ae284c2d8abc19d41367922ae842c19288306c50 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 19 Jan 2022 18:44:48 +0100 Subject: [PATCH] Route `save_as` via the `Project` Co-Authored-By: Max Brunsfeld --- crates/diagnostics/src/diagnostics.rs | 6 +-- crates/editor/src/items.rs | 46 +++-------------- crates/project/src/project.rs | 47 ++++++++++++++++- crates/project/src/worktree.rs | 48 ++++++++++++++--- crates/workspace/src/workspace.rs | 74 +++++---------------------- 5 files changed, 108 insertions(+), 113 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 85b40acd8d6fce445e084a403f949efdf0bd440a..ed49a3226b3da86668dc931dcc2e82c480ec3c9d 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -15,7 +15,7 @@ use gpui::{ use language::{Bias, Buffer, Diagnostic, DiagnosticEntry, Point, Selection, SelectionGoal}; use postage::watch; use project::{Project, ProjectPath, WorktreeId}; -use std::{cmp::Ordering, mem, ops::Range, rc::Rc, sync::Arc}; +use std::{cmp::Ordering, mem, ops::Range, path::PathBuf, rc::Rc, sync::Arc}; use util::TryFutureExt; use workspace::{NavHistory, Workspace}; @@ -570,8 +570,8 @@ impl workspace::ItemView for ProjectDiagnosticsEditor { fn save_as( &mut self, - _: ModelHandle, - _: &std::path::Path, + _: ModelHandle, + _: PathBuf, _: &mut ViewContext, ) -> Task> { unreachable!() diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 8158c5f6c4b7df63d48a2407aae340b876216adb..3ba61b8dca3bda6c4edd395589b5cf7277e0767a 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -4,12 +4,12 @@ use gpui::{ elements::*, AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakModelHandle, }; -use language::{Bias, Buffer, Diagnostic, File as _}; +use language::{Bias, Buffer, Diagnostic}; use postage::watch; -use project::{File, ProjectEntry, ProjectPath, Worktree}; -use std::fmt::Write; -use std::path::Path; +use project::worktree::File; +use project::{Project, ProjectEntry, ProjectPath, Worktree}; use std::rc::Rc; +use std::{fmt::Write, path::PathBuf}; use text::{Point, Selection}; use util::TryFutureExt; use workspace::{ @@ -182,8 +182,8 @@ impl ItemView for Editor { fn save_as( &mut self, - worktree: ModelHandle, - path: &Path, + project: ModelHandle, + abs_path: PathBuf, cx: &mut ViewContext, ) -> Task> { let buffer = self @@ -193,38 +193,8 @@ impl ItemView for Editor { .expect("cannot call save_as on an excerpt list") .clone(); - buffer.update(cx, |buffer, cx| { - let handle = cx.handle(); - let text = buffer.as_rope().clone(); - let version = buffer.version(); - - let save_as = worktree.update(cx, |worktree, cx| { - worktree - .as_local_mut() - .unwrap() - .save_buffer_as(handle, path, text, cx) - }); - - cx.spawn(|buffer, mut cx| async move { - save_as.await.map(|new_file| { - let (language, language_server) = worktree.update(&mut cx, |worktree, cx| { - let worktree = worktree.as_local_mut().unwrap(); - let language = worktree - .language_registry() - .select_language(new_file.full_path()) - .cloned(); - let language_server = language - .as_ref() - .and_then(|language| worktree.register_language(language, cx)); - (language, language_server.clone()) - }); - - buffer.update(&mut cx, |buffer, cx| { - buffer.did_save(version, new_file.mtime, Some(Box::new(new_file)), cx); - buffer.set_language(language, language_server, cx); - }); - }) - }) + project.update(cx, |project, cx| { + project.save_buffer_as(buffer, &abs_path, cx) }) } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 310274239f886b5bed06e7f3a3327ee78b6aae15..ba79a2fdfc72a6931931f38c550fc1086ef18d34 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -15,7 +15,7 @@ use language::{Buffer, DiagnosticEntry, LanguageRegistry}; use lsp::DiagnosticSeverity; use postage::{prelude::Stream, watch}; use std::{ - path::Path, + path::{Path, PathBuf}, sync::{atomic::AtomicBool, Arc}, }; use util::TryFutureExt as _; @@ -468,6 +468,49 @@ impl Project { } } + pub fn save_buffer_as( + &self, + buffer: ModelHandle, + abs_path: &Path, + cx: &mut ModelContext, + ) -> Task> { + let result = self.worktree_for_abs_path(abs_path, cx); + cx.spawn(|_, mut cx| async move { + let (worktree, path) = result.await?; + worktree + .update(&mut cx, |worktree, cx| { + worktree + .as_local() + .unwrap() + .save_buffer_as(buffer.clone(), path, cx) + }) + .await?; + Ok(()) + }) + } + + pub fn worktree_for_abs_path( + &self, + abs_path: &Path, + cx: &mut ModelContext, + ) -> Task, PathBuf)>> { + for tree in &self.worktrees { + if let Some(relative_path) = tree + .read(cx) + .as_local() + .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok()) + { + return Task::ready(Ok((tree.clone(), relative_path.into()))); + } + } + + let worktree = self.add_local_worktree(abs_path, cx); + cx.background().spawn(async move { + let worktree = worktree.await?; + Ok((worktree, PathBuf::new())) + }) + } + pub fn is_shared(&self) -> bool { match &self.client_state { ProjectClientState::Local { is_shared, .. } => *is_shared, @@ -476,7 +519,7 @@ impl Project { } pub fn add_local_worktree( - &mut self, + &self, abs_path: impl AsRef, cx: &mut ModelContext, ) -> Task>> { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index cacdd4b1b2d5343864c02f86f4a9ba15705b0671..1603e0fede5d7c6378ad42597ba5d49d11d27559 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1002,6 +1002,7 @@ pub struct LocalWorktree { client: Arc, user_store: ModelHandle, fs: Arc, + languages: Vec>, language_servers: HashMap>, } @@ -1109,6 +1110,7 @@ impl LocalWorktree { client, user_store, fs, + languages: Default::default(), language_servers: Default::default(), }; @@ -1153,11 +1155,19 @@ impl LocalWorktree { &self.language_registry } + pub fn languages(&self) -> &[Arc] { + &self.languages + } + pub fn register_language( &mut self, language: &Arc, cx: &mut ModelContext, ) -> Option> { + if !self.languages.iter().any(|l| Arc::ptr_eq(l, language)) { + self.languages.push(language.clone()); + } + if let Some(server) = self.language_servers.get(language.name()) { return Some(server.clone()); } @@ -1498,26 +1508,48 @@ impl LocalWorktree { pub fn save_buffer_as( &self, - buffer: ModelHandle, + buffer_handle: ModelHandle, path: impl Into>, - text: Rope, cx: &mut ModelContext, - ) -> Task> { + ) -> Task> { + let buffer = buffer_handle.read(cx); + let text = buffer.as_rope().clone(); + let version = buffer.version(); let save = self.save(path, text, cx); cx.spawn(|this, mut cx| async move { let entry = save.await?; - this.update(&mut cx, |this, cx| { + let file = this.update(&mut cx, |this, cx| { let this = this.as_local_mut().unwrap(); - this.open_buffers.insert(buffer.id(), buffer.downgrade()); - Ok(File { + this.open_buffers + .insert(buffer_handle.id(), buffer_handle.downgrade()); + File { entry_id: Some(entry.id), worktree: cx.handle(), worktree_path: this.abs_path.clone(), path: entry.path, mtime: entry.mtime, is_local: true, - }) - }) + } + }); + + let (language, language_server) = this.update(&mut cx, |worktree, cx| { + let worktree = worktree.as_local_mut().unwrap(); + let language = worktree + .language_registry() + .select_language(file.full_path()) + .cloned(); + let language_server = language + .as_ref() + .and_then(|language| worktree.register_language(language, cx)); + (language, language_server.clone()) + }); + + buffer_handle.update(&mut cx, |buffer, cx| { + buffer.did_save(version, file.mtime, Some(Box::new(file)), cx); + buffer.set_language(language, language_server, cx); + }); + + Ok(()) }) } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 949c0193eaa5ff6c748d811ee861a2076704ca93..da7c40d5f6402c1aec897dbceea19c0a403cec0f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -168,8 +168,8 @@ pub trait ItemView: View { fn can_save_as(&self, cx: &AppContext) -> bool; fn save_as( &mut self, - worktree: ModelHandle, - path: &Path, + project: ModelHandle, + abs_path: PathBuf, cx: &mut ViewContext, ) -> Task>; fn should_activate_item_on_event(_: &Self::Event) -> bool { @@ -221,8 +221,8 @@ pub trait ItemViewHandle { fn save(&self, cx: &mut MutableAppContext) -> Result>>; fn save_as( &self, - worktree: ModelHandle, - path: &Path, + project: ModelHandle, + abs_path: PathBuf, cx: &mut MutableAppContext, ) -> Task>; } @@ -379,11 +379,11 @@ impl ItemViewHandle for ViewHandle { fn save_as( &self, - worktree: ModelHandle, - path: &Path, + project: ModelHandle, + abs_path: PathBuf, cx: &mut MutableAppContext, ) -> Task> { - self.update(cx, |item, cx| item.save_as(worktree, path, cx)) + self.update(cx, |item, cx| item.save_as(project, abs_path, cx)) } fn is_dirty(&self, cx: &AppContext) -> bool { @@ -674,44 +674,14 @@ impl Workspace { }) } - fn worktree_for_abs_path( - &self, - abs_path: &Path, - cx: &mut ViewContext, - ) -> Task, PathBuf)>> { - let abs_path: Arc = Arc::from(abs_path); - cx.spawn(|this, mut cx| async move { - let mut entry_id = None; - this.read_with(&cx, |this, cx| { - for tree in this.worktrees(cx) { - if let Some(relative_path) = tree - .read(cx) - .as_local() - .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok()) - { - entry_id = Some((tree.clone(), relative_path.into())); - break; - } - } - }); - - if let Some(entry_id) = entry_id { - Ok(entry_id) - } else { - let worktree = this - .update(&mut cx, |this, cx| this.add_worktree(&abs_path, cx)) - .await?; - Ok((worktree, PathBuf::new())) - } - }) - } - fn project_path_for_path( &self, abs_path: &Path, cx: &mut ViewContext, ) -> Task> { - let entry = self.worktree_for_abs_path(abs_path, cx); + let entry = self.project().update(cx, |project, cx| { + project.worktree_for_abs_path(abs_path, cx) + }); cx.spawn(|_, cx| async move { let (worktree, path) = entry.await?; Ok(ProjectPath { @@ -880,28 +850,8 @@ impl Workspace { .to_path_buf(); cx.prompt_for_new_path(&start_abs_path, move |abs_path, cx| { if let Some(abs_path) = abs_path { - cx.spawn(|mut cx| async move { - let result = match handle - .update(&mut cx, |this, cx| { - this.worktree_for_abs_path(&abs_path, cx) - }) - .await - { - Ok((worktree, path)) => { - handle - .update(&mut cx, |_, cx| { - item.save_as(worktree, &path, cx.as_mut()) - }) - .await - } - Err(error) => Err(error), - }; - - if let Err(error) = result { - error!("failed to save item: {:?}, ", error); - } - }) - .detach() + let project = handle.read(cx).project().clone(); + cx.update(|cx| item.save_as(project, abs_path, cx).detach_and_log_err(cx)); } }); }