Invert dependency between editor and workspace

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-11-24 17:07:16 +01:00
parent 2cf44d30b7
commit e88d3bb97e
10 changed files with 580 additions and 536 deletions

3
Cargo.lock generated
View file

@ -1543,6 +1543,7 @@ dependencies = [
"tree-sitter-rust", "tree-sitter-rust",
"unindent", "unindent",
"util", "util",
"workspace",
] ]
[[package]] [[package]]
@ -5612,9 +5613,7 @@ name = "workspace"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"buffer",
"client", "client",
"editor",
"gpui", "gpui",
"language", "language",
"log", "log",

View file

@ -19,6 +19,7 @@ project = { path = "../project" }
sum_tree = { path = "../sum_tree" } sum_tree = { path = "../sum_tree" }
theme = { path = "../theme" } theme = { path = "../theme" }
util = { path = "../util" } util = { path = "../util" }
workspace = { path = "../workspace" }
anyhow = "1.0" anyhow = "1.0"
lazy_static = "1.4" lazy_static = "1.4"
log = "0.4" log = "0.4"

View file

@ -1,28 +1,55 @@
use super::{Item, ItemView}; use crate::{Editor, EditorSettings, Event};
use crate::{status_bar::StatusItemView, Settings};
use anyhow::Result; use anyhow::Result;
use buffer::{Point, Selection, ToPoint}; use buffer::{Point, Selection, ToPoint};
use editor::{Editor, EditorSettings, Event};
use gpui::{ use gpui::{
elements::*, fonts::TextStyle, AppContext, Entity, ModelHandle, RenderContext, Subscription, elements::*, fonts::TextStyle, AppContext, Entity, ModelContext, ModelHandle,
Task, View, ViewContext, ViewHandle, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
WeakModelHandle,
}; };
use language::{Buffer, Diagnostic, File as _}; use language::{Buffer, Diagnostic, File as _};
use postage::watch; use postage::watch;
use project::{ProjectPath, Worktree}; use project::{ProjectPath, Worktree};
use std::fmt::Write; use std::fmt::Write;
use std::path::Path; use std::path::Path;
use workspace::{
EntryOpener, ItemHandle, ItemView, ItemViewHandle, Settings, StatusItemView, WeakItemHandle,
};
impl Item for Buffer { pub struct BufferOpener;
type View = Editor;
fn build_view( #[derive(Clone)]
handle: ModelHandle<Self>, pub struct BufferItemHandle(pub ModelHandle<Buffer>);
#[derive(Clone)]
struct WeakBufferItemHandle(WeakModelHandle<Buffer>);
impl EntryOpener for BufferOpener {
fn open(
&self,
worktree: &mut Worktree,
project_path: ProjectPath,
cx: &mut ModelContext<Worktree>,
) -> Option<Task<Result<Box<dyn ItemHandle>>>> {
let buffer = worktree.open_buffer(project_path.path, cx);
let task = cx.spawn(|_, _| async move {
buffer
.await
.map(|buffer| Box::new(BufferItemHandle(buffer)) as Box<dyn ItemHandle>)
});
Some(task)
}
}
impl ItemHandle for BufferItemHandle {
fn add_view(
&self,
window_id: usize,
settings: watch::Receiver<Settings>, settings: watch::Receiver<Settings>,
cx: &mut ViewContext<Self::View>, cx: &mut MutableAppContext,
) -> Self::View { ) -> Box<dyn ItemViewHandle> {
Box::new(cx.add_view(window_id, |cx| {
Editor::for_buffer( Editor::for_buffer(
handle, self.0.clone(),
move |cx| { move |cx| {
let settings = settings.borrow(); let settings = settings.borrow();
let font_cache = cx.font_cache(); let font_cache = cx.font_cache();
@ -51,16 +78,33 @@ impl Item for Buffer {
}, },
cx, cx,
) )
}))
} }
fn project_path(&self) -> Option<ProjectPath> { fn boxed_clone(&self) -> Box<dyn ItemHandle> {
self.file().map(|f| ProjectPath { Box::new(self.clone())
}
fn downgrade(&self) -> Box<dyn workspace::WeakItemHandle> {
Box::new(WeakBufferItemHandle(self.0.downgrade()))
}
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
self.0.read(cx).file().map(|f| ProjectPath {
worktree_id: f.worktree_id(), worktree_id: f.worktree_id(),
path: f.path().clone(), path: f.path().clone(),
}) })
} }
} }
impl WeakItemHandle for WeakBufferItemHandle {
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
self.0
.upgrade(cx)
.map(|buffer| Box::new(BufferItemHandle(buffer)) as Box<dyn ItemHandle>)
}
}
impl ItemView for Editor { impl ItemView for Editor {
fn should_activate_item_on_event(event: &Event) -> bool { fn should_activate_item_on_event(event: &Event) -> bool {
matches!(event, Event::Activate) matches!(event, Event::Activate)
@ -226,7 +270,7 @@ impl View for CursorPosition {
impl StatusItemView for CursorPosition { impl StatusItemView for CursorPosition {
fn set_active_pane_item( fn set_active_pane_item(
&mut self, &mut self,
active_pane_item: Option<&dyn crate::ItemViewHandle>, active_pane_item: Option<&dyn ItemViewHandle>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
if let Some(editor) = active_pane_item.and_then(|item| item.to_any().downcast::<Editor>()) { if let Some(editor) = active_pane_item.and_then(|item| item.to_any().downcast::<Editor>()) {
@ -312,7 +356,7 @@ impl View for DiagnosticMessage {
impl StatusItemView for DiagnosticMessage { impl StatusItemView for DiagnosticMessage {
fn set_active_pane_item( fn set_active_pane_item(
&mut self, &mut self,
active_pane_item: Option<&dyn crate::ItemViewHandle>, active_pane_item: Option<&dyn ItemViewHandle>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
if let Some(editor) = active_pane_item.and_then(|item| item.to_any().downcast::<Editor>()) { if let Some(editor) = active_pane_item.and_then(|item| item.to_any().downcast::<Editor>()) {

View file

@ -1,5 +1,6 @@
pub mod display_map; pub mod display_map;
mod element; mod element;
pub mod items;
pub mod movement; pub mod movement;
#[cfg(test)] #[cfg(test)]
@ -17,6 +18,7 @@ use gpui::{
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
MutableAppContext, RenderContext, View, ViewContext, WeakViewHandle, MutableAppContext, RenderContext, View, ViewContext, WeakViewHandle,
}; };
use items::BufferItemHandle;
use language::*; use language::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec; use smallvec::SmallVec;
@ -34,6 +36,7 @@ use std::{
use sum_tree::Bias; use sum_tree::Bias;
use theme::{DiagnosticStyle, EditorStyle, SyntaxTheme}; use theme::{DiagnosticStyle, EditorStyle, SyntaxTheme};
use util::post_inc; use util::post_inc;
use workspace::{EntryOpener, Workspace};
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const MAX_LINE_LEN: usize = 1024; const MAX_LINE_LEN: usize = 1024;
@ -97,7 +100,8 @@ action!(FoldSelectedRanges);
action!(Scroll, Vector2F); action!(Scroll, Vector2F);
action!(Select, SelectPhase); action!(Select, SelectPhase);
pub fn init(cx: &mut MutableAppContext) { pub fn init(cx: &mut MutableAppContext, entry_openers: &mut Vec<Box<dyn EntryOpener>>) {
entry_openers.push(Box::new(items::BufferOpener));
cx.add_bindings(vec![ cx.add_bindings(vec![
Binding::new("escape", Cancel, Some("Editor")), Binding::new("escape", Cancel, Some("Editor")),
Binding::new("backspace", Backspace, Some("Editor")), Binding::new("backspace", Backspace, Some("Editor")),
@ -201,6 +205,7 @@ pub fn init(cx: &mut MutableAppContext) {
Binding::new("alt-cmd-f", FoldSelectedRanges, Some("Editor")), Binding::new("alt-cmd-f", FoldSelectedRanges, Some("Editor")),
]); ]);
cx.add_action(Editor::open_new);
cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx)); cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
cx.add_action(Editor::select); cx.add_action(Editor::select);
cx.add_action(Editor::cancel); cx.add_action(Editor::cancel);
@ -478,6 +483,15 @@ impl Editor {
} }
} }
pub fn open_new(
workspace: &mut Workspace,
_: &workspace::OpenNew,
cx: &mut ViewContext<Workspace>,
) {
let buffer = cx.add_model(|cx| Buffer::new(0, "", cx));
workspace.add_item(BufferItemHandle(buffer), cx);
}
pub fn replica_id(&self, cx: &AppContext) -> ReplicaId { pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
self.buffer.read(cx).replica_id() self.buffer.read(cx).replica_id()
} }

View file

@ -14,5 +14,6 @@ workspace = { path = "../workspace" }
postage = { version = "0.4.1", features = ["futures-traits"] } postage = { version = "0.4.1", features = ["futures-traits"] }
[dev-dependencies] [dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] } serde_json = { version = "1.0.64", features = ["preserve_order"] }
workspace = { path = "../workspace", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] }

View file

@ -429,7 +429,14 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_matching_paths(mut cx: gpui::TestAppContext) { async fn test_matching_paths(mut cx: gpui::TestAppContext) {
let params = cx.update(WorkspaceParams::test); let mut entry_openers = Vec::new();
cx.update(|cx| {
super::init(cx);
editor::init(cx, &mut entry_openers);
});
let mut params = cx.update(WorkspaceParams::test);
params.entry_openers = Arc::from(entry_openers);
params params
.fs .fs
.as_fake() .as_fake()
@ -443,10 +450,6 @@ mod tests {
}), }),
) )
.await; .await;
cx.update(|cx| {
super::init(cx);
editor::init(cx);
});
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx)); let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
workspace workspace

View file

@ -12,9 +12,7 @@ test-support = [
] ]
[dependencies] [dependencies]
buffer = { path = "../buffer" }
client = { path = "../client" } client = { path = "../client" }
editor = { path = "../editor" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
language = { path = "../language" } language = { path = "../language" }
project = { path = "../project" } project = { path = "../project" }

File diff suppressed because it is too large Load diff

View file

@ -130,11 +130,9 @@ fn open_paths(action: &OpenPaths, cx: &mut MutableAppContext) -> Task<()> {
} }
fn open_new(action: &workspace::OpenNew, cx: &mut MutableAppContext) { fn open_new(action: &workspace::OpenNew, cx: &mut MutableAppContext) {
cx.add_window(window_options(), |cx| { let (window_id, workspace) =
let mut workspace = build_workspace(&action.0, cx); cx.add_window(window_options(), |cx| build_workspace(&action.0, cx));
workspace.open_new_file(&action, cx); cx.dispatch_action(window_id, vec![workspace.id()], action);
workspace
});
} }
fn build_workspace(params: &WorkspaceParams, cx: &mut ViewContext<Workspace>) -> Workspace { fn build_workspace(params: &WorkspaceParams, cx: &mut ViewContext<Workspace>) -> Workspace {
@ -163,9 +161,9 @@ fn build_workspace(params: &WorkspaceParams, cx: &mut ViewContext<Workspace>) ->
); );
let diagnostic = let diagnostic =
cx.add_view(|_| workspace::items::DiagnosticMessage::new(params.settings.clone())); cx.add_view(|_| editor::items::DiagnosticMessage::new(params.settings.clone()));
let cursor_position = let cursor_position =
cx.add_view(|_| workspace::items::CursorPosition::new(params.settings.clone())); cx.add_view(|_| editor::items::CursorPosition::new(params.settings.clone()));
workspace.status_bar().update(cx, |status_bar, cx| { workspace.status_bar().update(cx, |status_bar, cx| {
status_bar.add_left_item(diagnostic, cx); status_bar.add_left_item(diagnostic, cx);
status_bar.add_right_item(cursor_position, cx); status_bar.add_right_item(cursor_position, cx);

View file

@ -36,8 +36,8 @@ fn main() {
let mut entry_openers = Vec::new(); let mut entry_openers = Vec::new();
client::init(client.clone(), cx); client::init(client.clone(), cx);
workspace::init(cx, &mut entry_openers); workspace::init(cx);
editor::init(cx); editor::init(cx, &mut entry_openers);
file_finder::init(cx); file_finder::init(cx);
people_panel::init(cx); people_panel::init(cx);
chat_panel::init(cx); chat_panel::init(cx);