Start work on making save and save_as code paths more similar

Max Brunsfeld created

Change summary

crates/editor/src/items.rs        | 35 +++++++++-----------------------
crates/editor/src/multi_buffer.rs | 15 --------------
crates/project/src/project.rs     | 33 ++++++++++++++++++++++++++++++
3 files changed, 42 insertions(+), 41 deletions(-)

Detailed changes

crates/editor/src/items.rs 🔗

@@ -2,12 +2,10 @@ use crate::{
     display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
     movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
     Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
-    FORMAT_TIMEOUT,
 };
 use anyhow::{anyhow, Context, Result};
 use collections::HashSet;
 use futures::future::try_join_all;
-use futures::FutureExt;
 use gpui::{
     elements::*, geometry::vector::vec2f, AppContext, Entity, ModelHandle, MutableAppContext,
     RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
@@ -16,7 +14,7 @@ use language::{
     proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
     SelectionGoal,
 };
-use project::{FormatTrigger, Item as _, Project, ProjectPath};
+use project::{Item as _, Project, ProjectPath};
 use rpc::proto::{self, update_view};
 use settings::Settings;
 use smallvec::SmallVec;
@@ -613,30 +611,17 @@ impl Item for Editor {
 
         let buffer = self.buffer().clone();
         let buffers = buffer.read(cx).all_buffers();
-        let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
-        let format = project.update(cx, |project, cx| {
-            project.format(buffers, true, FormatTrigger::Save, cx)
-        });
+        let save = project.update(cx, |project, cx| project.save_buffers(buffers, cx));
         cx.spawn(|_, mut cx| async move {
-            let transaction = futures::select_biased! {
-                _ = timeout => {
-                    log::warn!("timed out waiting for formatting");
-                    None
-                }
-                transaction = format.log_err().fuse() => transaction,
-            };
-
-            buffer
-                .update(&mut cx, |buffer, cx| {
-                    if let Some(transaction) = transaction {
-                        if !buffer.is_singleton() {
-                            buffer.push_transaction(&transaction.0);
-                        }
+            let (format_transaction, save) = save.await;
+            buffer.update(&mut cx, |buffer, _| {
+                if let Some(transaction) = format_transaction {
+                    if !buffer.is_singleton() {
+                        buffer.push_transaction(&transaction.0);
                     }
-
-                    buffer.save(cx)
-                })
-                .await?;
+                }
+            });
+            save.await?;
             Ok(())
         })
     }

crates/editor/src/multi_buffer.rs 🔗

@@ -1,7 +1,6 @@
 mod anchor;
 
 pub use anchor::{Anchor, AnchorRangeExt};
-use anyhow::Result;
 use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet};
 use futures::{channel::mpsc, SinkExt};
@@ -1279,20 +1278,6 @@ impl MultiBuffer {
             .map(|state| state.buffer.clone())
     }
 
-    pub fn save(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
-        let mut save_tasks = Vec::new();
-        for BufferState { buffer, .. } in self.buffers.borrow().values() {
-            save_tasks.push(buffer.update(cx, |buffer, cx| buffer.save(cx)));
-        }
-
-        cx.spawn(|_, _| async move {
-            for save in save_tasks {
-                save.await?;
-            }
-            Ok(())
-        })
-    }
-
     pub fn is_completion_trigger<T>(&self, position: T, text: &str, cx: &AppContext) -> bool
     where
         T: ToOffset,

crates/project/src/project.rs 🔗

@@ -12,7 +12,7 @@ use clock::ReplicaId;
 use collections::{hash_map, BTreeMap, HashMap, HashSet};
 use futures::{
     channel::{mpsc, oneshot},
-    future::Shared,
+    future::{try_join_all, Shared},
     AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt,
 };
 use gpui::{
@@ -1428,6 +1428,37 @@ impl Project {
         }
     }
 
+    pub fn save_buffers(
+        &mut self,
+        buffers: HashSet<ModelHandle<Buffer>>,
+        cx: &mut ModelContext<Self>,
+    ) -> Task<(Option<ProjectTransaction>, Task<Result<()>>)> {
+        const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
+
+        let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
+        let format = self.format(buffers.clone(), true, FormatTrigger::Save, cx);
+        cx.spawn(|_, cx| async move {
+            let transaction = futures::select_biased! {
+                _ = timeout => {
+                    log::warn!("timed out waiting for formatting");
+                    None
+                }
+                transaction = format.log_err().fuse() => transaction,
+            };
+
+            (
+                transaction,
+                cx.spawn(|mut cx| async move {
+                    let save_tasks = buffers
+                        .iter()
+                        .map(|buffer| buffer.update(&mut cx, |buffer, cx| buffer.save(cx)));
+                    try_join_all(save_tasks).await?;
+                    Ok(())
+                }),
+            )
+        })
+    }
+
     pub fn save_buffer_as(
         &mut self,
         buffer: ModelHandle<Buffer>,