Detailed changes
@@ -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<project::Worktree>,
- _: &std::path::Path,
+ _: ModelHandle<Project>,
+ _: PathBuf,
_: &mut ViewContext<Self>,
) -> Task<Result<()>> {
unreachable!()
@@ -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<Worktree>,
- path: &Path,
+ project: ModelHandle<Project>,
+ abs_path: PathBuf,
cx: &mut ViewContext<Self>,
) -> Task<Result<()>> {
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)
})
}
@@ -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<Buffer>,
+ abs_path: &Path,
+ cx: &mut ModelContext<Project>,
+ ) -> Task<Result<()>> {
+ 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<Self>,
+ ) -> Task<Result<(ModelHandle<Worktree>, 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<Path>,
cx: &mut ModelContext<Self>,
) -> Task<Result<ModelHandle<Worktree>>> {
@@ -1002,6 +1002,7 @@ pub struct LocalWorktree {
client: Arc<Client>,
user_store: ModelHandle<UserStore>,
fs: Arc<dyn Fs>,
+ languages: Vec<Arc<Language>>,
language_servers: HashMap<String, Arc<LanguageServer>>,
}
@@ -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<Language>] {
+ &self.languages
+ }
+
pub fn register_language(
&mut self,
language: &Arc<Language>,
cx: &mut ModelContext<Worktree>,
) -> Option<Arc<LanguageServer>> {
+ 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>,
+ buffer_handle: ModelHandle<Buffer>,
path: impl Into<Arc<Path>>,
- text: Rope,
cx: &mut ModelContext<Worktree>,
- ) -> Task<Result<File>> {
+ ) -> Task<Result<()>> {
+ 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(())
})
}
@@ -168,8 +168,8 @@ pub trait ItemView: View {
fn can_save_as(&self, cx: &AppContext) -> bool;
fn save_as(
&mut self,
- worktree: ModelHandle<Worktree>,
- path: &Path,
+ project: ModelHandle<Project>,
+ abs_path: PathBuf,
cx: &mut ViewContext<Self>,
) -> Task<anyhow::Result<()>>;
fn should_activate_item_on_event(_: &Self::Event) -> bool {
@@ -221,8 +221,8 @@ pub trait ItemViewHandle {
fn save(&self, cx: &mut MutableAppContext) -> Result<Task<Result<()>>>;
fn save_as(
&self,
- worktree: ModelHandle<Worktree>,
- path: &Path,
+ project: ModelHandle<Project>,
+ abs_path: PathBuf,
cx: &mut MutableAppContext,
) -> Task<anyhow::Result<()>>;
}
@@ -379,11 +379,11 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
fn save_as(
&self,
- worktree: ModelHandle<Worktree>,
- path: &Path,
+ project: ModelHandle<Project>,
+ abs_path: PathBuf,
cx: &mut MutableAppContext,
) -> Task<anyhow::Result<()>> {
- 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<Self>,
- ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
- let abs_path: Arc<Path> = 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<Self>,
) -> Task<Result<ProjectPath>> {
- 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));
}
});
}