Detailed changes
@@ -572,8 +572,12 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
true
}
- fn save(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
- self.editor.save(cx)
+ fn save(
+ &mut self,
+ project: ModelHandle<Project>,
+ cx: &mut ViewContext<Self>,
+ ) -> Task<Result<()>> {
+ self.editor.save(project, cx)
}
fn can_save_as(&self, _: &AppContext) -> bool {
@@ -220,11 +220,15 @@ impl ItemView for Editor {
!self.buffer().read(cx).is_singleton() || self.project_path(cx).is_some()
}
- fn save(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
+ fn save(
+ &mut self,
+ project: ModelHandle<Project>,
+ cx: &mut ViewContext<Self>,
+ ) -> Task<Result<()>> {
let buffer = self.buffer().clone();
cx.spawn(|editor, mut cx| async move {
buffer
- .update(&mut cx, |buffer, cx| buffer.format(cx).log_err())
+ .update(&mut cx, |buffer, cx| buffer.format(project, cx).log_err())
.await;
editor.update(&mut cx, |editor, cx| {
editor.request_autoscroll(Autoscroll::Fit, cx)
@@ -10,6 +10,7 @@ use language::{
Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Outline,
OutlineItem, Selection, ToOffset as _, ToPoint as _, ToPointUtf16 as _, TransactionId,
};
+use project::Project;
use std::{
cell::{Ref, RefCell},
cmp, fmt, io,
@@ -930,16 +931,25 @@ impl MultiBuffer {
cx.emit(event.clone());
}
- pub fn format(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
- let mut format_tasks = Vec::new();
- for BufferState { buffer, .. } in self.buffers.borrow().values() {
- format_tasks.push(buffer.update(cx, |buffer, cx| buffer.format(cx)));
- }
-
- cx.spawn(|_, _| async move {
- for format in format_tasks {
- format.await?;
- }
+ pub fn format(
+ &mut self,
+ project: ModelHandle<Project>,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<()>> {
+ let buffers = self
+ .buffers
+ .borrow()
+ .values()
+ .map(|state| state.buffer.clone())
+ .collect();
+ let transaction = project.update(cx, |project, cx| project.format(buffers, true, cx));
+ cx.spawn(|this, mut cx| async move {
+ let transaction = transaction.await?;
+ this.update(&mut cx, |this, _| {
+ if !this.singleton {
+ this.push_transaction(&transaction.0);
+ }
+ });
Ok(())
})
}
@@ -201,9 +201,6 @@ pub trait File {
cx: &mut MutableAppContext,
) -> Task<Result<(clock::Global, SystemTime)>>;
- fn format_remote(&self, buffer_id: u64, cx: &mut MutableAppContext)
- -> Option<Task<Result<()>>>;
-
fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext);
fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext);
@@ -278,10 +275,6 @@ impl File for FakeFile {
cx.spawn(|_| async move { Ok((Default::default(), SystemTime::UNIX_EPOCH)) })
}
- fn format_remote(&self, _: u64, _: &mut MutableAppContext) -> Option<Task<Result<()>>> {
- None
- }
-
fn buffer_updated(&self, _: u64, _: Operation, _: &mut MutableAppContext) {}
fn buffer_removed(&self, _: u64, _: &mut MutableAppContext) {}
@@ -540,52 +533,6 @@ impl Buffer {
self.file.as_deref()
}
- pub fn format(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
- let file = if let Some(file) = self.file.as_ref() {
- file
- } else {
- return Task::ready(Err(anyhow!("buffer has no file")));
- };
-
- if let Some(LanguageServerState { server, .. }) = self.language_server.as_ref() {
- let server = server.clone();
- let abs_path = file.as_local().unwrap().abs_path(cx);
- let version = self.version();
- cx.spawn(|this, mut cx| async move {
- let edits = server
- .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
- text_document: lsp::TextDocumentIdentifier::new(
- lsp::Url::from_file_path(&abs_path).unwrap(),
- ),
- options: Default::default(),
- work_done_progress_params: Default::default(),
- })
- .await?;
-
- if let Some(edits) = edits {
- this.update(&mut cx, |this, cx| {
- if this.version == version {
- this.apply_lsp_edits(edits, None, cx)?;
- Ok(())
- } else {
- Err(anyhow!("buffer edited since starting to format"))
- }
- })
- } else {
- Ok(())
- }
- })
- } else {
- let format = file.format_remote(self.remote_id(), cx.as_mut());
- cx.spawn(|_, _| async move {
- if let Some(format) = format {
- format.await?;
- }
- Ok(())
- })
- }
- }
-
pub fn save(
&mut self,
cx: &mut ModelContext<Self>,
@@ -348,7 +348,7 @@ impl Project {
client.subscribe_to_entity(remote_id, cx, Self::handle_update_buffer),
client.subscribe_to_entity(remote_id, cx, Self::handle_save_buffer),
client.subscribe_to_entity(remote_id, cx, Self::handle_buffer_saved),
- client.subscribe_to_entity(remote_id, cx, Self::handle_format_buffer),
+ client.subscribe_to_entity(remote_id, cx, Self::handle_format_buffers),
client.subscribe_to_entity(remote_id, cx, Self::handle_get_completions),
client.subscribe_to_entity(
remote_id,
@@ -613,9 +613,7 @@ impl Project {
})
.await?;
let buffer = response.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
- this.update(&mut cx, |this, cx| {
- this.deserialize_remote_buffer(buffer, cx)
- })
+ this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
})
}
@@ -1045,6 +1043,112 @@ impl Project {
Ok(())
}
+ pub fn format(
+ &self,
+ buffers: HashSet<ModelHandle<Buffer>>,
+ push_to_history: bool,
+ cx: &mut ModelContext<Project>,
+ ) -> Task<Result<ProjectTransaction>> {
+ let mut local_buffers = Vec::new();
+ let mut remote_buffers = None;
+ for buffer_handle in buffers {
+ let buffer = buffer_handle.read(cx);
+ let worktree;
+ if let Some(file) = File::from_dyn(buffer.file()) {
+ worktree = file.worktree.clone();
+ if let Some(buffer_abs_path) = file.as_local().map(|f| f.abs_path(cx)) {
+ let lang_server;
+ if let Some(lang) = buffer.language() {
+ if let Some(server) = self
+ .language_servers
+ .get(&(worktree.read(cx).id(), lang.name().to_string()))
+ {
+ lang_server = server.clone();
+ } else {
+ return Task::ready(Err(anyhow!(
+ "buffer {} does not have a language server",
+ buffer.remote_id()
+ )));
+ };
+ } else {
+ return Task::ready(Err(anyhow!("buffer does not have a language")));
+ }
+
+ local_buffers.push((buffer_handle, buffer_abs_path, lang_server));
+ } else {
+ remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
+ }
+ } else {
+ return Task::ready(Err(anyhow!(
+ "buffer {} does not belong to any worktree",
+ buffer.remote_id()
+ )));
+ }
+ }
+
+ let remote_buffers = self.remote_id().zip(remote_buffers);
+ let client = self.client.clone();
+
+ cx.spawn(|this, mut cx| async move {
+ let mut project_transaction = ProjectTransaction::default();
+
+ if let Some((project_id, remote_buffers)) = remote_buffers {
+ let response = client
+ .request(proto::FormatBuffers {
+ project_id,
+ buffer_ids: remote_buffers
+ .iter()
+ .map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
+ .collect(),
+ })
+ .await?
+ .transaction
+ .ok_or_else(|| anyhow!("missing transaction"))?;
+ project_transaction = this
+ .update(&mut cx, |this, cx| {
+ this.deserialize_project_transaction(response, push_to_history, cx)
+ })
+ .await?;
+ }
+
+ for (buffer, buffer_abs_path, lang_server) in local_buffers {
+ let lsp_edits = lang_server
+ .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
+ text_document: lsp::TextDocumentIdentifier::new(
+ lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
+ ),
+ options: Default::default(),
+ work_done_progress_params: Default::default(),
+ })
+ .await?;
+
+ if let Some(lsp_edits) = lsp_edits {
+ let edits = buffer
+ .update(&mut cx, |buffer, cx| {
+ buffer.edits_from_lsp(lsp_edits, None, cx)
+ })
+ .await?;
+ buffer.update(&mut cx, |buffer, cx| {
+ buffer.finalize_last_transaction();
+ buffer.start_transaction();
+ for (range, text) in edits {
+ buffer.edit([range], text, cx);
+ }
+ if buffer.end_transaction(cx).is_some() {
+ let transaction = buffer.finalize_last_transaction().unwrap().clone();
+ if !push_to_history {
+ buffer.forget_transaction(transaction.id);
+ }
+ project_transaction.0.insert(cx.handle(), transaction);
+ }
+ });
+ }
+ }
+
+ Ok(project_transaction)
+ })
+ }
+
pub fn definition<T: ToPointUtf16>(
&self,
source_buffer_handle: &ModelHandle<Buffer>,
@@ -1156,7 +1260,7 @@ impl Project {
this.update(&mut cx, |this, cx| {
let mut definitions = Vec::new();
for definition in response.definitions {
- let target_buffer = this.deserialize_remote_buffer(
+ let target_buffer = this.deserialize_buffer(
definition.buffer.ok_or_else(|| anyhow!("missing buffer"))?,
cx,
)?;
@@ -1637,29 +1741,10 @@ impl Project {
.await?
.transaction
.ok_or_else(|| anyhow!("missing transaction"))?;
- let mut project_transaction = ProjectTransaction::default();
- for (buffer, transaction) in response.buffers.into_iter().zip(response.transactions)
- {
- let buffer = this.update(&mut cx, |this, cx| {
- this.deserialize_remote_buffer(buffer, cx)
- })?;
- let transaction = language::proto::deserialize_transaction(transaction)?;
-
- buffer
- .update(&mut cx, |buffer, _| {
- buffer.wait_for_edits(transaction.edit_ids.iter().copied())
- })
- .await;
-
- if push_to_history {
- buffer.update(&mut cx, |buffer, _| {
- buffer.push_transaction(transaction.clone(), Instant::now());
- });
- }
-
- project_transaction.0.insert(buffer, transaction);
- }
- Ok(project_transaction)
+ this.update(&mut cx, |this, cx| {
+ this.deserialize_project_transaction(response, push_to_history, cx)
+ })
+ .await
})
} else {
Task::ready(Err(anyhow!("project does not have a remote id")))
@@ -2163,26 +2248,51 @@ impl Project {
Ok(())
}
- pub fn handle_format_buffer(
+ pub fn handle_format_buffers(
&mut self,
- envelope: TypedEnvelope<proto::FormatBuffer>,
+ envelope: TypedEnvelope<proto::FormatBuffers>,
rpc: Arc<Client>,
cx: &mut ModelContext<Self>,
) -> Result<()> {
let receipt = envelope.receipt();
let sender_id = envelope.original_sender_id()?;
- let buffer = self
+ let shared_buffers = self
.shared_buffers
.get(&sender_id)
- .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
- .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
- cx.spawn(|_, mut cx| async move {
- let format = buffer.update(&mut cx, |buffer, cx| buffer.format(cx)).await;
- // We spawn here in order to enqueue the sending of `Ack` *after* transmission of edits
- // associated with formatting.
+ .ok_or_else(|| anyhow!("peer has no buffers"))?;
+ let mut buffers = HashSet::default();
+ for buffer_id in envelope.payload.buffer_ids {
+ buffers.insert(
+ shared_buffers
+ .get(&buffer_id)
+ .cloned()
+ .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
+ );
+ }
+ cx.spawn(|this, mut cx| async move {
+ dbg!("here!");
+ let project_transaction = this
+ .update(&mut cx, |this, cx| this.format(buffers, false, cx))
+ .await
+ .map(|project_transaction| {
+ this.update(&mut cx, |this, cx| {
+ this.serialize_project_transaction_for_peer(
+ project_transaction,
+ sender_id,
+ cx,
+ )
+ })
+ });
+ // We spawn here in order to enqueue the sending of the response *after* transmission of
+ // edits associated with formatting.
cx.spawn(|_| async move {
- match format {
- Ok(()) => rpc.respond(receipt, proto::Ack {})?,
+ match project_transaction {
+ Ok(transaction) => rpc.respond(
+ receipt,
+ proto::FormatBuffersResponse {
+ transaction: Some(transaction),
+ },
+ )?,
Err(error) => rpc.respond_with_error(
receipt,
proto::Error {
@@ -2358,18 +2468,11 @@ impl Project {
cx.spawn(|this, mut cx| async move {
match apply_code_action.await {
Ok(project_transaction) => this.update(&mut cx, |this, cx| {
- let mut serialized_transaction = proto::ProjectTransaction {
- buffers: Default::default(),
- transactions: Default::default(),
- };
- for (buffer, transaction) in project_transaction.0 {
- serialized_transaction
- .buffers
- .push(this.serialize_buffer_for_peer(&buffer, sender_id, cx));
- serialized_transaction
- .transactions
- .push(language::proto::serialize_transaction(&transaction));
- }
+ let serialized_transaction = this.serialize_project_transaction_for_peer(
+ project_transaction,
+ sender_id,
+ cx,
+ );
rpc.respond(
receipt,
proto::ApplyCodeActionResponse {
@@ -2471,6 +2574,58 @@ impl Project {
Ok(())
}
+ fn serialize_project_transaction_for_peer(
+ &mut self,
+ project_transaction: ProjectTransaction,
+ peer_id: PeerId,
+ cx: &AppContext,
+ ) -> proto::ProjectTransaction {
+ let mut serialized_transaction = proto::ProjectTransaction {
+ buffers: Default::default(),
+ transactions: Default::default(),
+ };
+ for (buffer, transaction) in project_transaction.0 {
+ serialized_transaction
+ .buffers
+ .push(self.serialize_buffer_for_peer(&buffer, peer_id, cx));
+ serialized_transaction
+ .transactions
+ .push(language::proto::serialize_transaction(&transaction));
+ }
+ serialized_transaction
+ }
+
+ fn deserialize_project_transaction(
+ &self,
+ message: proto::ProjectTransaction,
+ push_to_history: bool,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<ProjectTransaction>> {
+ cx.spawn(|this, mut cx| async move {
+ let mut project_transaction = ProjectTransaction::default();
+ for (buffer, transaction) in message.buffers.into_iter().zip(message.transactions) {
+ let buffer =
+ this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))?;
+ let transaction = language::proto::deserialize_transaction(transaction)?;
+
+ buffer
+ .update(&mut cx, |buffer, _| {
+ buffer.wait_for_edits(transaction.edit_ids.iter().copied())
+ })
+ .await;
+
+ if push_to_history {
+ buffer.update(&mut cx, |buffer, _| {
+ buffer.push_transaction(transaction.clone(), Instant::now());
+ });
+ }
+
+ project_transaction.0.insert(buffer, transaction);
+ }
+ Ok(project_transaction)
+ })
+ }
+
fn serialize_buffer_for_peer(
&mut self,
buffer: &ModelHandle<Buffer>,
@@ -2492,7 +2647,7 @@ impl Project {
}
}
- fn deserialize_remote_buffer(
+ fn deserialize_buffer(
&mut self,
buffer: proto::Buffer,
cx: &mut ModelContext<Self>,
@@ -1385,25 +1385,6 @@ impl language::File for File {
})
}
- fn format_remote(
- &self,
- buffer_id: u64,
- cx: &mut MutableAppContext,
- ) -> Option<Task<Result<()>>> {
- let worktree = self.worktree.read(cx);
- let worktree = worktree.as_remote()?;
- let rpc = worktree.client.clone();
- let project_id = worktree.project_id;
- Some(cx.foreground().spawn(async move {
- rpc.request(proto::FormatBuffer {
- project_id,
- buffer_id,
- })
- .await?;
- Ok(())
- }))
- }
-
fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext) {
self.worktree.update(cx, |worktree, cx| {
worktree.send_buffer_update(buffer_id, operation, cx);
@@ -39,31 +39,32 @@ message Envelope {
SaveBuffer save_buffer = 31;
BufferSaved buffer_saved = 32;
BufferReloaded buffer_reloaded = 33;
- FormatBuffer format_buffer = 34;
- GetCompletions get_completions = 35;
- GetCompletionsResponse get_completions_response = 36;
- ApplyCompletionAdditionalEdits apply_completion_additional_edits = 37;
- ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 38;
- GetCodeActions get_code_actions = 39;
- GetCodeActionsResponse get_code_actions_response = 40;
- ApplyCodeAction apply_code_action = 41;
- ApplyCodeActionResponse apply_code_action_response = 42;
-
- GetChannels get_channels = 43;
- GetChannelsResponse get_channels_response = 44;
- JoinChannel join_channel = 45;
- JoinChannelResponse join_channel_response = 46;
- LeaveChannel leave_channel = 47;
- SendChannelMessage send_channel_message = 48;
- SendChannelMessageResponse send_channel_message_response = 49;
- ChannelMessageSent channel_message_sent = 50;
- GetChannelMessages get_channel_messages = 51;
- GetChannelMessagesResponse get_channel_messages_response = 52;
-
- UpdateContacts update_contacts = 53;
-
- GetUsers get_users = 54;
- GetUsersResponse get_users_response = 55;
+ FormatBuffers format_buffers = 34;
+ FormatBuffersResponse format_buffers_response = 35;
+ GetCompletions get_completions = 36;
+ GetCompletionsResponse get_completions_response = 37;
+ ApplyCompletionAdditionalEdits apply_completion_additional_edits = 38;
+ ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 39;
+ GetCodeActions get_code_actions = 40;
+ GetCodeActionsResponse get_code_actions_response = 41;
+ ApplyCodeAction apply_code_action = 42;
+ ApplyCodeActionResponse apply_code_action_response = 43;
+
+ GetChannels get_channels = 44;
+ GetChannelsResponse get_channels_response = 45;
+ JoinChannel join_channel = 46;
+ JoinChannelResponse join_channel_response = 47;
+ LeaveChannel leave_channel = 48;
+ SendChannelMessage send_channel_message = 49;
+ SendChannelMessageResponse send_channel_message_response = 50;
+ ChannelMessageSent channel_message_sent = 51;
+ GetChannelMessages get_channel_messages = 52;
+ GetChannelMessagesResponse get_channel_messages_response = 53;
+
+ UpdateContacts update_contacts = 54;
+
+ GetUsers get_users = 55;
+ GetUsersResponse get_users_response = 56;
}
}
@@ -206,9 +207,13 @@ message BufferReloaded {
Timestamp mtime = 4;
}
-message FormatBuffer {
+message FormatBuffers {
uint64 project_id = 1;
- uint64 buffer_id = 2;
+ repeated uint64 buffer_ids = 2;
+}
+
+message FormatBuffersResponse {
+ ProjectTransaction transaction = 1;
}
message GetCompletions {
@@ -133,7 +133,8 @@ messages!(
DiskBasedDiagnosticsUpdated,
DiskBasedDiagnosticsUpdating,
Error,
- FormatBuffer,
+ FormatBuffers,
+ FormatBuffersResponse,
GetChannelMessages,
GetChannelMessagesResponse,
GetChannels,
@@ -180,7 +181,7 @@ request_messages!(
ApplyCompletionAdditionalEdits,
ApplyCompletionAdditionalEditsResponse
),
- (FormatBuffer, Ack),
+ (FormatBuffers, FormatBuffersResponse),
(GetChannelMessages, GetChannelMessagesResponse),
(GetChannels, GetChannelsResponse),
(GetCodeActions, GetCodeActionsResponse),
@@ -210,7 +211,7 @@ entity_messages!(
CloseBuffer,
DiskBasedDiagnosticsUpdated,
DiskBasedDiagnosticsUpdating,
- FormatBuffer,
+ FormatBuffers,
GetCodeActions,
GetCompletions,
GetDefinition,
@@ -82,7 +82,7 @@ impl Server {
.add_handler(Server::buffer_reloaded)
.add_handler(Server::buffer_saved)
.add_handler(Server::save_buffer)
- .add_handler(Server::format_buffer)
+ .add_handler(Server::format_buffers)
.add_handler(Server::get_completions)
.add_handler(Server::apply_additional_edits_for_completion)
.add_handler(Server::get_code_actions)
@@ -669,9 +669,9 @@ impl Server {
Ok(())
}
- async fn format_buffer(
+ async fn format_buffers(
self: Arc<Server>,
- request: TypedEnvelope<proto::FormatBuffer>,
+ request: TypedEnvelope<proto::FormatBuffers>,
) -> tide::Result<()> {
let host;
{
@@ -2607,7 +2607,9 @@ mod tests {
.await
.unwrap();
- let format = buffer_b.update(&mut cx_b, |buffer, cx| buffer.format(cx));
+ let format = project_b.update(&mut cx_b, |project, cx| {
+ project.format(HashSet::from_iter([buffer_b.clone()]), true, cx)
+ });
let (request_id, _) = fake_language_server
.receive_request::<lsp::request::Formatting>()
.await;
@@ -168,7 +168,11 @@ pub trait ItemView: View {
false
}
fn can_save(&self, cx: &AppContext) -> bool;
- fn save(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>>;
+ fn save(
+ &mut self,
+ project: ModelHandle<Project>,
+ cx: &mut ViewContext<Self>,
+ ) -> Task<Result<()>>;
fn can_save_as(&self, cx: &AppContext) -> bool;
fn save_as(
&mut self,
@@ -234,7 +238,7 @@ pub trait ItemViewHandle: 'static {
fn has_conflict(&self, cx: &AppContext) -> bool;
fn can_save(&self, cx: &AppContext) -> bool;
fn can_save_as(&self, cx: &AppContext) -> bool;
- fn save(&self, cx: &mut MutableAppContext) -> Task<Result<()>>;
+ fn save(&self, project: ModelHandle<Project>, cx: &mut MutableAppContext) -> Task<Result<()>>;
fn save_as(
&self,
project: ModelHandle<Project>,
@@ -402,8 +406,8 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
self.update(cx, |this, cx| this.navigate(data, cx));
}
- fn save(&self, cx: &mut MutableAppContext) -> Task<Result<()>> {
- self.update(cx, |item, cx| item.save(cx))
+ fn save(&self, project: ModelHandle<Project>, cx: &mut MutableAppContext) -> Task<Result<()>> {
+ self.update(cx, |item, cx| item.save(project, cx))
}
fn save_as(
@@ -820,6 +824,7 @@ impl Workspace {
}
pub fn save_active_item(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
+ let project = self.project.clone();
if let Some(item) = self.active_item(cx) {
if item.can_save(cx) {
if item.has_conflict(cx.as_ref()) {
@@ -833,12 +838,12 @@ impl Workspace {
cx.spawn(|_, mut cx| async move {
let answer = answer.recv().await;
if answer == Some(0) {
- cx.update(|cx| item.save(cx)).await?;
+ cx.update(|cx| item.save(project, cx)).await?;
}
Ok(())
})
} else {
- item.save(cx)
+ item.save(project, cx)
}
} else if item.can_save_as(cx) {
let worktree = self.worktrees(cx).next();
@@ -847,9 +852,8 @@ impl Workspace {
.map_or(Path::new(""), |w| w.abs_path())
.to_path_buf();
let mut abs_path = cx.prompt_for_new_path(&start_abs_path);
- cx.spawn(|this, mut cx| async move {
+ cx.spawn(|_, mut cx| async move {
if let Some(abs_path) = abs_path.recv().await.flatten() {
- let project = this.read_with(&cx, |this, _| this.project().clone());
cx.update(|cx| item.save_as(project, abs_path, cx)).await?;
}
Ok(())