mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Introduce workspace::register_project_item
This lets downstream crates like `editor` define how project items should be opened. Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
bff414cfbc
commit
5d14c9abdf
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1782,7 +1782,9 @@ dependencies = [
|
||||
name = "file_finder"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"editor",
|
||||
"env_logger",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"postage",
|
||||
|
@ -340,7 +340,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_async_action(Editor::confirm_rename);
|
||||
cx.add_async_action(Editor::find_all_references);
|
||||
|
||||
workspace::register_editor_builder(cx, |project, buffer, cx| {
|
||||
workspace::register_project_item(cx, |project, buffer, cx| {
|
||||
Editor::for_buffer(buffer, Some(project), cx)
|
||||
});
|
||||
}
|
||||
|
@ -21,3 +21,5 @@ postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
serde_json = { version = "1.0.64", features = ["preserve_order"] }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
ctor = "0.1"
|
||||
env_logger = "0.8"
|
||||
|
@ -407,6 +407,13 @@ mod tests {
|
||||
use std::path::PathBuf;
|
||||
use workspace::{Workspace, WorkspaceParams};
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
if std::env::var("RUST_LOG").is_ok() {
|
||||
env_logger::init();
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_matching_paths(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
|
@ -1364,12 +1364,22 @@ impl MutableAppContext {
|
||||
Ok(pending)
|
||||
}
|
||||
|
||||
pub fn default_global<T: 'static + Default>(&mut self) -> &T {
|
||||
self.cx
|
||||
.globals
|
||||
.entry(TypeId::of::<T>())
|
||||
.or_insert_with(|| Box::new(T::default()))
|
||||
.downcast_ref()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn set_global<T: 'static>(&mut self, state: T) {
|
||||
self.cx.globals.insert(TypeId::of::<T>(), Box::new(state));
|
||||
}
|
||||
|
||||
pub fn update_global<T: 'static, F, U>(&mut self, update: F) -> U
|
||||
pub fn update_default_global<T, F, U>(&mut self, update: F) -> U
|
||||
where
|
||||
T: 'static + Default,
|
||||
F: FnOnce(&mut T, &mut MutableAppContext) -> U,
|
||||
{
|
||||
let type_id = TypeId::of::<T>();
|
||||
@ -1377,7 +1387,23 @@ impl MutableAppContext {
|
||||
.cx
|
||||
.globals
|
||||
.remove(&type_id)
|
||||
.expect("no app state has been added for this type");
|
||||
.unwrap_or_else(|| Box::new(T::default()));
|
||||
let result = update(state.downcast_mut().unwrap(), self);
|
||||
self.cx.globals.insert(type_id, state);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn update_global<T, F, U>(&mut self, update: F) -> U
|
||||
where
|
||||
T: 'static,
|
||||
F: FnOnce(&mut T, &mut MutableAppContext) -> U,
|
||||
{
|
||||
let type_id = TypeId::of::<T>();
|
||||
let mut state = self
|
||||
.cx
|
||||
.globals
|
||||
.remove(&type_id)
|
||||
.expect("no global has been added for this type");
|
||||
let result = update(state.downcast_mut().unwrap(), self);
|
||||
self.cx.globals.insert(type_id, state);
|
||||
result
|
||||
@ -3715,6 +3741,10 @@ impl AnyModelHandle {
|
||||
pub fn is<T: Entity>(&self) -> bool {
|
||||
self.model_type == TypeId::of::<T>()
|
||||
}
|
||||
|
||||
pub fn model_type(&self) -> TypeId {
|
||||
self.model_type
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Entity> From<ModelHandle<T>> for AnyModelHandle {
|
||||
|
@ -11,8 +11,8 @@ use collections::{hash_map, BTreeMap, HashMap, HashSet};
|
||||
use futures::{future::Shared, Future, FutureExt, StreamExt, TryFutureExt};
|
||||
use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
|
||||
use gpui::{
|
||||
AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task,
|
||||
UpgradeModelHandle, WeakModelHandle,
|
||||
AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
|
||||
MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle,
|
||||
};
|
||||
use language::{
|
||||
proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
|
||||
@ -822,6 +822,23 @@ impl Project {
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn open_path(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<(ProjectEntryId, AnyModelHandle)>> {
|
||||
let task = self.open_buffer(path, cx);
|
||||
cx.spawn_weak(|_, cx| async move {
|
||||
let buffer = task.await?;
|
||||
let project_entry_id = buffer
|
||||
.read_with(&cx, |buffer, cx| {
|
||||
File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
|
||||
})
|
||||
.ok_or_else(|| anyhow!("no project entry"))?;
|
||||
Ok((project_entry_id, buffer.into()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn open_buffer(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
|
@ -9,6 +9,7 @@ mod status_bar;
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{Authenticate, ChannelList, Client, User, UserStore};
|
||||
use clock::ReplicaId;
|
||||
use collections::HashMap;
|
||||
use gpui::{
|
||||
action,
|
||||
color::Color,
|
||||
@ -17,11 +18,11 @@ use gpui::{
|
||||
json::{self, to_string_pretty, ToJson},
|
||||
keymap::Binding,
|
||||
platform::{CursorStyle, WindowOptions},
|
||||
AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, ModelHandle, MutableAppContext,
|
||||
PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle,
|
||||
WeakViewHandle,
|
||||
AnyModelHandle, AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, ModelHandle,
|
||||
MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext,
|
||||
ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use language::{Buffer, LanguageRegistry};
|
||||
use language::LanguageRegistry;
|
||||
use log::error;
|
||||
pub use pane::*;
|
||||
pub use pane_group::*;
|
||||
@ -41,13 +42,16 @@ use std::{
|
||||
};
|
||||
use theme::{Theme, ThemeRegistry};
|
||||
|
||||
pub type BuildEditor = Arc<
|
||||
dyn Fn(
|
||||
usize,
|
||||
ModelHandle<Project>,
|
||||
ModelHandle<Buffer>,
|
||||
&mut MutableAppContext,
|
||||
) -> Box<dyn ItemHandle>,
|
||||
type ItemBuilders = HashMap<
|
||||
TypeId,
|
||||
Arc<
|
||||
dyn Fn(
|
||||
usize,
|
||||
ModelHandle<Project>,
|
||||
AnyModelHandle,
|
||||
&mut MutableAppContext,
|
||||
) -> Box<dyn ItemHandle>,
|
||||
>,
|
||||
>;
|
||||
|
||||
action!(Open, Arc<AppState>);
|
||||
@ -104,14 +108,20 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||
]);
|
||||
}
|
||||
|
||||
pub fn register_editor_builder<F, V>(cx: &mut MutableAppContext, build_editor: F)
|
||||
pub fn register_project_item<F, V>(cx: &mut MutableAppContext, build_item: F)
|
||||
where
|
||||
V: Item,
|
||||
F: 'static + Fn(ModelHandle<Project>, ModelHandle<Buffer>, &mut ViewContext<V>) -> V,
|
||||
V: ProjectItem,
|
||||
F: 'static + Fn(ModelHandle<Project>, ModelHandle<V::Item>, &mut ViewContext<V>) -> V,
|
||||
{
|
||||
cx.set_global::<BuildEditor>(Arc::new(move |window_id, project, model, cx| {
|
||||
Box::new(cx.add_view(window_id, |cx| build_editor(project, model, cx)))
|
||||
}));
|
||||
cx.update_default_global(|builders: &mut ItemBuilders, _| {
|
||||
builders.insert(
|
||||
TypeId::of::<V::Item>(),
|
||||
Arc::new(move |window_id, project, model, cx| {
|
||||
let model = model.downcast::<V::Item>().unwrap();
|
||||
Box::new(cx.add_view(window_id, |cx| build_item(project, model, cx)))
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub struct AppState {
|
||||
@ -826,20 +836,19 @@ impl Workspace {
|
||||
)>,
|
||||
> {
|
||||
let project = self.project().clone();
|
||||
let buffer = project.update(cx, |project, cx| project.open_buffer(path, cx));
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let buffer = buffer.await?;
|
||||
let project_entry_id = buffer.read_with(&cx, |buffer, cx| {
|
||||
project::File::from_dyn(buffer.file())
|
||||
.and_then(|file| file.project_entry_id(cx))
|
||||
.ok_or_else(|| anyhow!("buffer has no entry"))
|
||||
let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
|
||||
let window_id = cx.window_id();
|
||||
cx.as_mut().spawn(|mut cx| async move {
|
||||
let (project_entry_id, project_item) = project_item.await?;
|
||||
let build_item = cx.update(|cx| {
|
||||
cx.default_global::<ItemBuilders>()
|
||||
.get(&project_item.model_type())
|
||||
.ok_or_else(|| anyhow!("no item builder for project item"))
|
||||
.cloned()
|
||||
})?;
|
||||
let (window_id, build_editor) = this.update(&mut cx, |_, cx| {
|
||||
(cx.window_id(), cx.global::<BuildEditor>().clone())
|
||||
});
|
||||
let build_editor =
|
||||
move |cx: &mut MutableAppContext| build_editor(window_id, project, buffer, cx);
|
||||
Ok((project_entry_id, build_editor))
|
||||
let build_item =
|
||||
move |cx: &mut MutableAppContext| build_item(window_id, project, project_item, cx);
|
||||
Ok((project_entry_id, build_item))
|
||||
})
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user