WIP: Massage opening of editors

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Nathan Sobo 2022-03-16 17:40:09 -06:00
parent 1f9885ec42
commit 728c708150
8 changed files with 338 additions and 134 deletions

View file

@ -9,6 +9,7 @@ mod status_bar;
use anyhow::{anyhow, Result};
use client::{Authenticate, ChannelList, Client, User, UserStore};
use clock::ReplicaId;
use futures::TryFutureExt;
use gpui::{
action,
color::Color,
@ -21,7 +22,7 @@ use gpui::{
MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext,
ViewHandle, WeakViewHandle,
};
use language::LanguageRegistry;
use language::{Buffer, LanguageRegistry};
use log::error;
pub use pane::*;
pub use pane_group::*;
@ -41,6 +42,15 @@ use std::{
};
use theme::{Theme, ThemeRegistry};
pub type BuildEditor = Box<
dyn Fn(
usize,
ModelHandle<Project>,
ModelHandle<Buffer>,
&mut MutableAppContext,
) -> Box<dyn ItemViewHandle>,
>;
action!(Open, Arc<AppState>);
action!(OpenNew, Arc<AppState>);
action!(OpenPaths, OpenParams);
@ -95,6 +105,16 @@ pub fn init(cx: &mut MutableAppContext) {
]);
}
pub fn register_editor_builder<F, V>(cx: &mut MutableAppContext, build_editor: F)
where
V: ItemView,
F: 'static + Fn(ModelHandle<Project>, ModelHandle<Buffer>, &mut ViewContext<V>) -> V,
{
cx.add_app_state::<BuildEditor>(Box::new(|window_id, project, model, cx| {
Box::new(cx.add_view(window_id, |cx| build_editor(project, model, cx)))
}));
}
pub struct AppState {
pub languages: Arc<LanguageRegistry>,
pub themes: Arc<ThemeRegistry>,
@ -138,7 +158,6 @@ pub trait ItemView: View {
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {}
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>);
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
where
@ -191,7 +210,6 @@ pub trait ItemView: View {
pub trait ItemViewHandle: 'static {
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
fn set_nav_history(&self, nav_history: Rc<RefCell<NavHistory>>, cx: &mut MutableAppContext);
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
@ -239,10 +257,6 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
self.read(cx).project_path(cx)
}
fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
self.read(cx).project_entry_id(cx)
}
fn boxed_clone(&self) -> Box<dyn ItemViewHandle> {
Box::new(self.clone())
}
@ -656,6 +670,24 @@ impl Workspace {
path: ProjectPath,
cx: &mut ViewContext<Self>,
) -> Task<Result<Box<dyn ItemViewHandle>, Arc<anyhow::Error>>> {
let project_entry = self.project.read(cx).entry_for_path(&path, cx);
let existing_entry = self
.active_pane()
.update(cx, |pane, cx| pane.activate_project_entry(project_entry));
cx.spawn(|this, cx| {
if let Some(existing_entry) = existing_entry {
return Ok(existing_entry);
}
let load_task = this
.update(&mut cx, |this, cx| {
this.load_project_entry(project_entry, cx)
})
.await;
});
let load_task = self.load_path(path, cx);
let pane = self.active_pane().clone().downgrade();
cx.as_mut().spawn(|mut cx| async move {
@ -663,7 +695,7 @@ impl Workspace {
let pane = pane
.upgrade(&cx)
.ok_or_else(|| anyhow!("could not upgrade pane reference"))?;
Ok(pane.update(&mut cx, |pane, cx| pane.open_item(item, cx)))
Ok(pane.update(&mut cx, |pane, cx| pane.open_editor(item, cx)))
})
}
@ -672,13 +704,23 @@ impl Workspace {
path: ProjectPath,
cx: &mut ViewContext<Self>,
) -> Task<Result<Box<dyn ItemViewHandle>>> {
let project_entry = self.project.read(cx).entry_for_path(&path, cx);
if let Some(project_entry) = self.project.read(cx).entry_for_path(&path, cx) {
self.load_project_entry(project_entry, cx)
} else {
Task::ready(Err(anyhow!("no such file {:?}", path)))
}
}
if let Some(existing_item) = project_entry.and_then(|entry| {
self.panes
.iter()
.find_map(|pane| pane.read(cx).item_for_entry(entry))
}) {
pub fn load_project_entry(
&mut self,
project_entry: ProjectEntryId,
cx: &mut ViewContext<Self>,
) -> Task<Result<Box<dyn ItemViewHandle>>> {
if let Some(existing_item) = self
.panes
.iter()
.find_map(|pane| pane.read(cx).item_for_entry(project_entry))
{
return Task::ready(Ok(existing_item));
}
@ -829,37 +871,108 @@ impl Workspace {
pane
}
pub fn open_item(
&mut self,
item_view: Box<dyn ItemViewHandle>,
cx: &mut ViewContext<Self>,
) -> Box<dyn ItemViewHandle> {
pub fn open_item(&mut self, item_view: Box<dyn ItemViewHandle>, cx: &mut ViewContext<Self>) {
self.active_pane()
.update(cx, |pane, cx| pane.open_item(item_view, cx))
}
pub fn open_item_for_project_entry<T, F>(
pub fn open_editor(
&mut self,
project_entry: ProjectEntryId,
cx: &mut ViewContext<Self>,
build_view: F,
) -> Box<dyn ItemViewHandle>
where
T: ItemView,
F: FnOnce(&mut ViewContext<T>) -> T,
{
if let Some(existing_item) = self
.panes
.iter()
.find_map(|pane| pane.read(cx).item_for_entry(project_entry))
{
return existing_item.boxed_clone();
}
) -> Task<Result<Box<dyn ItemViewHandle>, Arc<anyhow::Error>>> {
let pane = self.active_pane().clone();
let project = self.project().clone();
let buffer = project.update(cx, |project, cx| {
project.open_buffer_for_entry(project_entry, cx)
});
let view = Box::new(cx.add_view(build_view));
self.open_item(view, cx)
cx.spawn(|this, cx| async move {
let buffer = buffer.await?;
let editor = this.update(&mut cx, |this, cx| {
let window_id = cx.window_id();
pane.update(cx, |pane, cx| {
pane.open_editor(project_entry, cx, |cx| {
cx.app_state::<BuildEditor>()(window_id, project, buffer, cx)
})
})
});
Ok(editor)
})
}
// pub fn open_path(
// &mut self,
// path: ProjectPath,
// cx: &mut ViewContext<Self>,
// ) -> Task<Result<Box<dyn ItemViewHandle>, Arc<anyhow::Error>>> {
// let project_entry = self.project.read(cx).entry_for_path(&path, cx);
// let existing_entry = self
// .active_pane()
// .update(cx, |pane, cx| pane.activate_project_entry(project_entry));
// cx.spawn(|this, cx| {
// if let Some(existing_entry) = existing_entry {
// return Ok(existing_entry);
// }
// let load_task = this
// .update(&mut cx, |this, cx| {
// this.load_project_entry(project_entry, cx)
// })
// .await;
// });
// let load_task = self.load_path(path, cx);
// let pane = self.active_pane().clone().downgrade();
// cx.as_mut().spawn(|mut cx| async move {
// let item = load_task.await?;
// let pane = pane
// .upgrade(&cx)
// .ok_or_else(|| anyhow!("could not upgrade pane reference"))?;
// Ok(pane.update(&mut cx, |pane, cx| pane.open_editor(item, cx)))
// })
// }
// pub fn load_path(
// &mut self,
// path: ProjectPath,
// cx: &mut ViewContext<Self>,
// ) -> Task<Result<Box<dyn ItemViewHandle>>> {
// if let Some(project_entry) = self.project.read(cx).entry_for_path(&path, cx) {
// self.load_project_entry(project_entry, cx)
// } else {
// Task::ready(Err(anyhow!("no such file {:?}", path)))
// }
// }
// pub fn load_project_entry(
// &mut self,
// project_entry: ProjectEntryId,
// cx: &mut ViewContext<Self>,
// ) -> Task<Result<Box<dyn ItemViewHandle>>> {
// if let Some(existing_item) = self
// .panes
// .iter()
// .find_map(|pane| pane.read(cx).item_for_entry(project_entry))
// {
// return Task::ready(Ok(existing_item));
// }
// let project_path = path.clone();
// let path_openers = self.path_openers.clone();
// let window_id = cx.window_id();
// self.project.update(cx, |project, cx| {
// for opener in path_openers.iter() {
// if let Some(task) = opener.open(project, project_path.clone(), window_id, cx) {
// return task;
// }
// }
// Task::ready(Err(anyhow!("no opener found for path {:?}", project_path)))
// })
// }
pub fn activate_item(&mut self, item: &dyn ItemViewHandle, cx: &mut ViewContext<Self>) -> bool {
let result = self.panes.iter().find_map(|pane| {
if let Some(ix) = pane.read(cx).index_for_item(item) {
@ -930,10 +1043,11 @@ impl Workspace {
let new_pane = self.add_pane(cx);
self.activate_pane(new_pane.clone(), cx);
if let Some(item) = pane.read(cx).active_item() {
let nav_history = new_pane.read(cx).nav_history().clone();
let project_entry_id = pane.read(cx).project_entry_id_for_item(item.as_ref());
if let Some(clone) = item.clone_on_split(cx.as_mut()) {
clone.set_nav_history(nav_history, cx);
new_pane.update(cx, |new_pane, cx| new_pane.add_item_view(clone, cx));
new_pane.update(cx, |new_pane, cx| {
new_pane.open_item(project_entry_id, clone, cx);
});
}
}
self.center.split(&pane, &new_pane, direction).unwrap();