Offload `text::Buffer` construction to background worker

Julia and Antonio Scandurra created

Co-Authored-By: Antonio Scandurra <me@as-cii.com>

Change summary

crates/language/src/buffer.rs  | 20 +++++---------------
crates/project/src/project.rs  |  6 +++++-
crates/project/src/worktree.rs |  7 ++++++-
3 files changed, 16 insertions(+), 17 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -357,20 +357,6 @@ impl Buffer {
         )
     }
 
-    pub fn from_file<T: Into<String>>(
-        replica_id: ReplicaId,
-        base_text: T,
-        diff_base: Option<T>,
-        file: Arc<dyn File>,
-        cx: &mut ModelContext<Self>,
-    ) -> Self {
-        Self::build(
-            TextBuffer::new(replica_id, cx.model_id() as u64, base_text.into()),
-            diff_base.map(|h| h.into().into_boxed_str().into()),
-            Some(file),
-        )
-    }
-
     pub fn from_proto(
         replica_id: ReplicaId,
         message: proto::BufferState,
@@ -460,7 +446,11 @@ impl Buffer {
         self
     }
 
-    fn build(buffer: TextBuffer, diff_base: Option<String>, file: Option<Arc<dyn File>>) -> Self {
+    pub fn build(
+        buffer: TextBuffer,
+        diff_base: Option<String>,
+        file: Option<Arc<dyn File>>,
+    ) -> Self {
         let saved_mtime = if let Some(file) = file.as_ref() {
             file.mtime()
         } else {

crates/project/src/project.rs 🔗

@@ -109,6 +109,7 @@ pub struct Project {
     collaborators: HashMap<proto::PeerId, Collaborator>,
     client_subscriptions: Vec<client::Subscription>,
     _subscriptions: Vec<gpui::Subscription>,
+    next_buffer_id: u64,
     opened_buffer: (watch::Sender<()>, watch::Receiver<()>),
     shared_buffers: HashMap<proto::PeerId, HashSet<u64>>,
     #[allow(clippy::type_complexity)]
@@ -441,6 +442,7 @@ impl Project {
                 worktrees: Default::default(),
                 buffer_ordered_messages_tx: tx,
                 collaborators: Default::default(),
+                next_buffer_id: 0,
                 opened_buffers: Default::default(),
                 shared_buffers: Default::default(),
                 incomplete_remote_buffers: Default::default(),
@@ -509,6 +511,7 @@ impl Project {
                 worktrees: Vec::new(),
                 buffer_ordered_messages_tx: tx,
                 loading_buffers_by_path: Default::default(),
+                next_buffer_id: 0,
                 opened_buffer: watch::channel(),
                 shared_buffers: Default::default(),
                 incomplete_remote_buffers: Default::default(),
@@ -1401,9 +1404,10 @@ impl Project {
         worktree: &ModelHandle<Worktree>,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<ModelHandle<Buffer>>> {
+        let buffer_id = post_inc(&mut self.next_buffer_id);
         let load_buffer = worktree.update(cx, |worktree, cx| {
             let worktree = worktree.as_local_mut().unwrap();
-            worktree.load_buffer(path, cx)
+            worktree.load_buffer(buffer_id, path, cx)
         });
         cx.spawn(|this, mut cx| async move {
             let buffer = load_buffer.await?;

crates/project/src/worktree.rs 🔗

@@ -506,6 +506,7 @@ impl LocalWorktree {
 
     pub(crate) fn load_buffer(
         &mut self,
+        id: u64,
         path: &Path,
         cx: &mut ModelContext<Worktree>,
     ) -> Task<Result<ModelHandle<Buffer>>> {
@@ -514,8 +515,12 @@ impl LocalWorktree {
             let (file, contents, diff_base) = this
                 .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))
                 .await?;
+            let text_buffer = cx
+                .background()
+                .spawn(async move { text::Buffer::new(0, id, contents) })
+                .await;
             Ok(cx.add_model(|cx| {
-                let mut buffer = Buffer::from_file(0, contents, diff_base, Arc::new(file), cx);
+                let mut buffer = Buffer::build(text_buffer, diff_base, Some(Arc::new(file)));
                 buffer.git_diff_recalc(cx);
                 buffer
             }))