Use `Buffer` handles instead of `MultiBuffer` as editor workspace items

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/editor/src/editor.rs       |  5 ++---
crates/editor/src/items.rs        | 26 +++++++++++---------------
crates/workspace/src/workspace.rs |  8 ++++----
3 files changed, 17 insertions(+), 22 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -46,7 +46,7 @@ use sum_tree::Bias;
 use text::rope::TextDimension;
 use theme::{DiagnosticStyle, EditorStyle};
 use util::post_inc;
-use workspace::{EntryOpener, Workspace};
+use workspace::{PathOpener, Workspace};
 
 const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
 const MAX_LINE_LEN: usize = 1024;
@@ -111,7 +111,7 @@ action!(FoldSelectedRanges);
 action!(Scroll, Vector2F);
 action!(Select, SelectPhase);
 
-pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn EntryOpener>>) {
+pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpener>>) {
     path_openers.push(Box::new(items::BufferOpener));
     cx.add_bindings(vec![
         Binding::new("escape", Cancel, Some("Editor")),
@@ -524,7 +524,6 @@ impl Editor {
         let buffer = cx.add_model(|cx| {
             Buffer::new(0, "", cx).with_language(Some(language::PLAIN_TEXT.clone()), None, cx)
         });
-        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
         workspace.open_item(BufferItemHandle(buffer), cx);
     }
 

crates/editor/src/items.rs 🔗

@@ -5,25 +5,25 @@ use gpui::{
     elements::*, AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, RenderContext,
     Subscription, Task, View, ViewContext, ViewHandle, WeakModelHandle,
 };
-use language::{Diagnostic, File as _};
+use language::{Buffer, Diagnostic, File as _};
 use postage::watch;
 use project::{File, ProjectPath, Worktree};
 use std::fmt::Write;
 use std::path::Path;
 use text::{Point, Selection};
 use workspace::{
-    EntryOpener, ItemHandle, ItemView, ItemViewHandle, Settings, StatusItemView, WeakItemHandle,
+    ItemHandle, ItemView, ItemViewHandle, PathOpener, Settings, StatusItemView, WeakItemHandle,
 };
 
 pub struct BufferOpener;
 
 #[derive(Clone)]
-pub struct BufferItemHandle(pub ModelHandle<MultiBuffer>);
+pub struct BufferItemHandle(pub ModelHandle<Buffer>);
 
 #[derive(Clone)]
-struct WeakBufferItemHandle(WeakModelHandle<MultiBuffer>);
+struct WeakBufferItemHandle(WeakModelHandle<Buffer>);
 
-impl EntryOpener for BufferOpener {
+impl PathOpener for BufferOpener {
     fn open(
         &self,
         worktree: &mut Worktree,
@@ -31,9 +31,8 @@ impl EntryOpener for BufferOpener {
         cx: &mut ModelContext<Worktree>,
     ) -> Option<Task<Result<Box<dyn ItemHandle>>>> {
         let buffer = worktree.open_buffer(project_path.path, cx);
-        let task = cx.spawn(|_, mut cx| async move {
+        let task = cx.spawn(|_, _| async move {
             let buffer = buffer.await?;
-            let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
             Ok(Box::new(BufferItemHandle(buffer)) as Box<dyn ItemHandle>)
         });
         Some(task)
@@ -47,13 +46,10 @@ impl ItemHandle for BufferItemHandle {
         settings: watch::Receiver<Settings>,
         cx: &mut MutableAppContext,
     ) -> Box<dyn ItemViewHandle> {
-        let buffer = self.0.downgrade();
+        let buffer = cx.add_model(|cx| MultiBuffer::singleton(self.0.clone(), cx));
+        let weak_buffer = buffer.downgrade();
         Box::new(cx.add_view(window_id, |cx| {
-            Editor::for_buffer(
-                self.0.clone(),
-                crate::settings_builder(buffer, settings),
-                cx,
-            )
+            Editor::for_buffer(buffer, crate::settings_builder(weak_buffer, settings), cx)
         }))
     }
 
@@ -66,7 +62,7 @@ impl ItemHandle for BufferItemHandle {
     }
 
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        File::from_dyn(self.0.read(cx).file(cx)).map(|f| ProjectPath {
+        File::from_dyn(self.0.read(cx).file()).map(|f| ProjectPath {
             worktree_id: f.worktree_id(cx),
             path: f.path().clone(),
         })
@@ -93,7 +89,7 @@ impl ItemView for Editor {
     type ItemHandle = BufferItemHandle;
 
     fn item_handle(&self, cx: &AppContext) -> Self::ItemHandle {
-        BufferItemHandle(self.buffer.clone())
+        BufferItemHandle(self.buffer.read(cx).as_singleton().unwrap())
     }
 
     fn title(&self, cx: &AppContext) -> String {

crates/workspace/src/workspace.rs 🔗

@@ -96,7 +96,7 @@ pub struct AppState {
     pub user_store: ModelHandle<client::UserStore>,
     pub fs: Arc<dyn fs::Fs>,
     pub channel_list: ModelHandle<client::ChannelList>,
-    pub path_openers: Arc<[Box<dyn EntryOpener>]>,
+    pub path_openers: Arc<[Box<dyn PathOpener>]>,
     pub build_window_options: &'static dyn Fn() -> WindowOptions<'static>,
     pub build_workspace: &'static dyn Fn(
         ModelHandle<Project>,
@@ -117,7 +117,7 @@ pub struct JoinProjectParams {
     pub app_state: Arc<AppState>,
 }
 
-pub trait EntryOpener {
+pub trait PathOpener {
     fn open(
         &self,
         worktree: &mut Worktree,
@@ -396,7 +396,7 @@ pub struct WorkspaceParams {
     pub settings: watch::Receiver<Settings>,
     pub user_store: ModelHandle<UserStore>,
     pub channel_list: ModelHandle<ChannelList>,
-    pub path_openers: Arc<[Box<dyn EntryOpener>]>,
+    pub path_openers: Arc<[Box<dyn PathOpener>]>,
 }
 
 impl WorkspaceParams {
@@ -466,7 +466,7 @@ pub struct Workspace {
     active_pane: ViewHandle<Pane>,
     status_bar: ViewHandle<StatusBar>,
     project: ModelHandle<Project>,
-    path_openers: Arc<[Box<dyn EntryOpener>]>,
+    path_openers: Arc<[Box<dyn PathOpener>]>,
     items: HashSet<Box<dyn WeakItemHandle>>,
     _observe_current_user: Task<()>,
 }