mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 19:25:33 +03:00
Remove workspace::Item trait
Co-Authored-By: Nathan Sobo <nathan@zed.dev> Co-Authored-By: Keith Simmons <keith@zed.dev> Co-Authored-By: Antonio Scandurra <antonio@zed.dev>
This commit is contained in:
parent
e8efaed1b2
commit
a88320dc5f
@ -25,7 +25,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use util::TryFutureExt;
|
||||
use workspace::{ItemHandle, ItemNavHistory, ItemViewHandle as _, Settings, Workspace};
|
||||
use workspace::{ItemNavHistory, ItemViewHandle as _, Settings, Workspace};
|
||||
|
||||
action!(Deploy);
|
||||
|
||||
@ -38,12 +38,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||
|
||||
type Event = editor::Event;
|
||||
|
||||
struct ProjectDiagnostics {
|
||||
project: ModelHandle<Project>,
|
||||
}
|
||||
|
||||
struct ProjectDiagnosticsEditor {
|
||||
model: ModelHandle<ProjectDiagnostics>,
|
||||
project: ModelHandle<Project>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
editor: ViewHandle<Editor>,
|
||||
summary: DiagnosticSummary,
|
||||
@ -65,16 +61,6 @@ struct DiagnosticGroupState {
|
||||
block_count: usize,
|
||||
}
|
||||
|
||||
impl ProjectDiagnostics {
|
||||
fn new(project: ModelHandle<Project>) -> Self {
|
||||
Self { project }
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for ProjectDiagnostics {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl Entity for ProjectDiagnosticsEditor {
|
||||
type Event = Event;
|
||||
}
|
||||
@ -109,12 +95,11 @@ impl View for ProjectDiagnosticsEditor {
|
||||
|
||||
impl ProjectDiagnosticsEditor {
|
||||
fn new(
|
||||
model: ModelHandle<ProjectDiagnostics>,
|
||||
project_handle: ModelHandle<Project>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let project = model.read(cx).project.clone();
|
||||
cx.subscribe(&project, |this, _, event, cx| match event {
|
||||
cx.subscribe(&project_handle, |this, _, event, cx| match event {
|
||||
project::Event::DiskBasedDiagnosticsFinished => {
|
||||
this.update_excerpts(cx);
|
||||
this.update_title(cx);
|
||||
@ -126,20 +111,21 @@ impl ProjectDiagnosticsEditor {
|
||||
})
|
||||
.detach();
|
||||
|
||||
let excerpts = cx.add_model(|cx| MultiBuffer::new(project.read(cx).replica_id()));
|
||||
let excerpts = cx.add_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
|
||||
let editor = cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(excerpts.clone(), Some(project.clone()), cx);
|
||||
let mut editor = Editor::for_buffer(excerpts.clone(), Some(project_handle.clone()), cx);
|
||||
editor.set_vertical_scroll_margin(5, cx);
|
||||
editor
|
||||
});
|
||||
cx.subscribe(&editor, |_, _, event, cx| cx.emit(*event))
|
||||
.detach();
|
||||
|
||||
let project = project.read(cx);
|
||||
let project = project_handle.read(cx);
|
||||
let paths_to_update = project.diagnostic_summaries(cx).map(|e| e.0).collect();
|
||||
let summary = project.diagnostic_summary(cx);
|
||||
let mut this = Self {
|
||||
model,
|
||||
summary: project.diagnostic_summary(cx),
|
||||
project: project_handle,
|
||||
summary,
|
||||
workspace,
|
||||
excerpts,
|
||||
editor,
|
||||
@ -151,18 +137,20 @@ impl ProjectDiagnosticsEditor {
|
||||
}
|
||||
|
||||
fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
|
||||
if let Some(existing) = workspace.item_of_type::<ProjectDiagnostics>(cx) {
|
||||
if let Some(existing) = workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
|
||||
workspace.activate_item(&existing, cx);
|
||||
} else {
|
||||
let diagnostics =
|
||||
cx.add_model(|_| ProjectDiagnostics::new(workspace.project().clone()));
|
||||
workspace.open_item(diagnostics, cx);
|
||||
let workspace_handle = cx.weak_handle();
|
||||
let diagnostics = cx.add_view(|cx| {
|
||||
ProjectDiagnosticsEditor::new(workspace.project().clone(), workspace_handle, cx)
|
||||
});
|
||||
workspace.open_item(Box::new(diagnostics), cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_excerpts(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let paths = mem::take(&mut self.paths_to_update);
|
||||
let project = self.model.read(cx).project.clone();
|
||||
let project = self.project.clone();
|
||||
cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
for path in paths {
|
||||
@ -443,37 +431,12 @@ impl ProjectDiagnosticsEditor {
|
||||
}
|
||||
|
||||
fn update_title(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.summary = self.model.read(cx).project.read(cx).diagnostic_summary(cx);
|
||||
self.summary = self.project.read(cx).diagnostic_summary(cx);
|
||||
cx.emit(Event::TitleChanged);
|
||||
}
|
||||
}
|
||||
|
||||
impl workspace::Item for ProjectDiagnostics {
|
||||
type View = ProjectDiagnosticsEditor;
|
||||
|
||||
fn build_view(
|
||||
handle: ModelHandle<Self>,
|
||||
workspace: &Workspace,
|
||||
nav_history: ItemNavHistory,
|
||||
cx: &mut ViewContext<Self::View>,
|
||||
) -> Self::View {
|
||||
let diagnostics = ProjectDiagnosticsEditor::new(handle, workspace.weak_handle(), cx);
|
||||
diagnostics
|
||||
.editor
|
||||
.update(cx, |editor, _| editor.set_nav_history(Some(nav_history)));
|
||||
diagnostics
|
||||
}
|
||||
|
||||
fn project_path(&self) -> Option<project::ProjectPath> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl workspace::ItemView for ProjectDiagnosticsEditor {
|
||||
fn item(&self, _: &AppContext) -> Box<dyn ItemHandle> {
|
||||
Box::new(self.model.clone())
|
||||
}
|
||||
|
||||
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox {
|
||||
render_summary(
|
||||
&self.summary,
|
||||
@ -486,6 +449,10 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
|
||||
None
|
||||
}
|
||||
|
||||
fn project_entry(&self, _: &AppContext) -> Option<project::ProjectEntry> {
|
||||
None
|
||||
}
|
||||
|
||||
fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, cx));
|
||||
@ -532,20 +499,21 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
|
||||
matches!(event, Event::Saved | Event::Dirtied | Event::TitleChanged)
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
nav_history: ItemNavHistory,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Self>
|
||||
fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
||||
self.editor.update(cx, |editor, _| {
|
||||
editor.set_nav_history(Some(nav_history));
|
||||
});
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let diagnostics =
|
||||
ProjectDiagnosticsEditor::new(self.model.clone(), self.workspace.clone(), cx);
|
||||
diagnostics.editor.update(cx, |editor, _| {
|
||||
editor.set_nav_history(Some(nav_history));
|
||||
});
|
||||
Some(diagnostics)
|
||||
Some(ProjectDiagnosticsEditor::new(
|
||||
self.project.clone(),
|
||||
self.workspace.clone(),
|
||||
cx,
|
||||
))
|
||||
}
|
||||
|
||||
fn act_as_type(
|
||||
@ -829,9 +797,8 @@ mod tests {
|
||||
});
|
||||
|
||||
// Open the project diagnostics view while there are already diagnostics.
|
||||
let model = cx.add_model(|_| ProjectDiagnostics::new(project.clone()));
|
||||
let view = cx.add_view(0, |cx| {
|
||||
ProjectDiagnosticsEditor::new(model, workspace.downgrade(), cx)
|
||||
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
||||
});
|
||||
|
||||
view.next_notification(&cx).await;
|
||||
|
@ -28,7 +28,6 @@ use gpui::{
|
||||
ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
|
||||
WeakViewHandle,
|
||||
};
|
||||
use items::{BufferItemHandle, MultiBufferItemHandle};
|
||||
use itertools::Itertools as _;
|
||||
pub use language::{char_kind, CharKind};
|
||||
use language::{
|
||||
@ -836,7 +835,32 @@ impl Editor {
|
||||
Self::new(EditorMode::Full, buffer, project, None, cx)
|
||||
}
|
||||
|
||||
pub fn clone(&self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) -> Self {
|
||||
pub fn find_or_create(
|
||||
workspace: &mut Workspace,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> ViewHandle<Editor> {
|
||||
let project = workspace.project().clone();
|
||||
|
||||
if let Some(project_entry) =
|
||||
project::File::from_dyn(buffer.read(cx).file()).and_then(|file| file.project_entry(cx))
|
||||
{
|
||||
return workspace
|
||||
.open_item_for_project_entry(project_entry, cx, |cx| {
|
||||
let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
Editor::for_buffer(multibuffer, Some(project.clone()), cx)
|
||||
})
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
let editor = cx.add_view(|cx| Editor::for_buffer(multibuffer, Some(project.clone()), cx));
|
||||
workspace.open_item(Box::new(editor.clone()), cx);
|
||||
editor
|
||||
}
|
||||
|
||||
pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
|
||||
let mut clone = Self::new(
|
||||
self.mode,
|
||||
self.buffer.clone(),
|
||||
@ -846,7 +870,6 @@ impl Editor {
|
||||
);
|
||||
clone.scroll_position = self.scroll_position;
|
||||
clone.scroll_top_anchor = self.scroll_top_anchor.clone();
|
||||
clone.nav_history = Some(nav_history);
|
||||
clone.searchable = self.searchable;
|
||||
clone
|
||||
}
|
||||
@ -938,14 +961,20 @@ impl Editor {
|
||||
_: &workspace::OpenNew,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let project = workspace.project();
|
||||
let project = workspace.project().clone();
|
||||
if project.read(cx).is_remote() {
|
||||
cx.propagate_action();
|
||||
} else if let Some(buffer) = project
|
||||
.update(cx, |project, cx| project.create_buffer(cx))
|
||||
.log_err()
|
||||
{
|
||||
workspace.open_item(BufferItemHandle(buffer), cx);
|
||||
let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
workspace.open_item(
|
||||
Box::new(
|
||||
cx.add_view(|cx| Editor::for_buffer(multibuffer, Some(project.clone()), cx)),
|
||||
),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2349,7 +2378,11 @@ impl Editor {
|
||||
});
|
||||
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let editor = workspace.open_item(MultiBufferItemHandle(excerpt_buffer), cx);
|
||||
let project = workspace.project().clone();
|
||||
let editor = workspace.open_item(
|
||||
Box::new(cx.add_view(|cx| Editor::for_buffer(excerpt_buffer, Some(project), cx))),
|
||||
cx,
|
||||
);
|
||||
if let Some(editor) = editor.act_as::<Self>(cx) {
|
||||
editor.update(cx, |editor, cx| {
|
||||
let color = editor.style(cx).highlighted_line_background;
|
||||
@ -4280,20 +4313,17 @@ impl Editor {
|
||||
return;
|
||||
};
|
||||
|
||||
let definitions = workspace
|
||||
.project()
|
||||
.update(cx, |project, cx| project.definition(&buffer, head, cx));
|
||||
let project = workspace.project().clone();
|
||||
let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
let definitions = definitions.await?;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let nav_history = workspace.active_pane().read(cx).nav_history().clone();
|
||||
for definition in definitions {
|
||||
let range = definition.range.to_offset(definition.buffer.read(cx));
|
||||
let target_editor_handle = workspace
|
||||
.open_item(BufferItemHandle(definition.buffer), cx)
|
||||
.downcast::<Self>()
|
||||
.unwrap();
|
||||
|
||||
let target_editor_handle =
|
||||
Self::find_or_create(workspace, definition.buffer, cx);
|
||||
target_editor_handle.update(cx, |target_editor, cx| {
|
||||
// When selecting a definition in a different buffer, disable the nav history
|
||||
// to avoid creating a history entry at the previous cursor location.
|
||||
@ -4324,9 +4354,8 @@ impl Editor {
|
||||
let (buffer, head) = editor.buffer.read(cx).text_anchor_for_position(head, cx)?;
|
||||
let replica_id = editor.replica_id(cx);
|
||||
|
||||
let references = workspace
|
||||
.project()
|
||||
.update(cx, |project, cx| project.references(&buffer, head, cx));
|
||||
let project = workspace.project().clone();
|
||||
let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
|
||||
Some(cx.spawn(|workspace, mut cx| async move {
|
||||
let mut locations = references.await?;
|
||||
if locations.is_empty() {
|
||||
@ -4370,13 +4399,13 @@ impl Editor {
|
||||
});
|
||||
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let editor = workspace.open_item(MultiBufferItemHandle(excerpt_buffer), cx);
|
||||
if let Some(editor) = editor.act_as::<Self>(cx) {
|
||||
editor.update(cx, |editor, cx| {
|
||||
let color = editor.style(cx).highlighted_line_background;
|
||||
editor.highlight_background::<Self>(ranges_to_highlight, color, cx);
|
||||
});
|
||||
}
|
||||
let editor =
|
||||
cx.add_view(|cx| Editor::for_buffer(excerpt_buffer, Some(project), cx));
|
||||
editor.update(cx, |editor, cx| {
|
||||
let color = editor.style(cx).highlighted_line_background;
|
||||
editor.highlight_background::<Self>(ranges_to_highlight, color, cx);
|
||||
});
|
||||
workspace.open_item(Box::new(editor), cx);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
@ -5563,17 +5592,10 @@ impl Editor {
|
||||
// and activating a new item causes the pane to call a method on us reentrantly,
|
||||
// which panics if we're on the stack.
|
||||
cx.defer(move |workspace, cx| {
|
||||
for (ix, (buffer, ranges)) in new_selections_by_buffer.into_iter().enumerate() {
|
||||
let buffer = BufferItemHandle(buffer);
|
||||
if ix == 0 && !workspace.activate_pane_for_item(&buffer, cx) {
|
||||
workspace.activate_next_pane(cx);
|
||||
}
|
||||
|
||||
let editor = workspace
|
||||
.open_item(buffer, cx)
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
workspace.activate_next_pane(cx);
|
||||
|
||||
for (buffer, ranges) in new_selections_by_buffer.into_iter() {
|
||||
let editor = Self::find_or_create(workspace, buffer, cx);
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.select_ranges(ranges, Some(Autoscroll::Newest), cx);
|
||||
});
|
||||
|
@ -1,20 +1,16 @@
|
||||
use crate::{Autoscroll, Editor, Event, MultiBuffer, NavigationData, ToOffset, ToPoint as _};
|
||||
use anyhow::Result;
|
||||
use gpui::{
|
||||
elements::*, AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, RenderContext,
|
||||
Subscription, Task, View, ViewContext, ViewHandle, WeakModelHandle,
|
||||
elements::*, AppContext, Entity, ModelContext, ModelHandle, RenderContext, Subscription, Task,
|
||||
View, ViewContext, ViewHandle, WeakModelHandle,
|
||||
};
|
||||
use language::{Bias, Buffer, Diagnostic, File as _};
|
||||
use project::{File, Project, ProjectPath};
|
||||
use project::{File, Project, ProjectEntry, ProjectPath};
|
||||
use std::fmt::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::{cell::RefCell, fmt::Write};
|
||||
use text::{Point, Selection};
|
||||
use util::ResultExt;
|
||||
use workspace::{
|
||||
ItemHandle, ItemNavHistory, ItemView, ItemViewHandle, NavHistory, PathOpener, Settings,
|
||||
StatusItemView, WeakItemHandle, Workspace,
|
||||
};
|
||||
use workspace::{ItemNavHistory, ItemView, ItemViewHandle, PathOpener, Settings, StatusItemView};
|
||||
|
||||
pub struct BufferOpener;
|
||||
|
||||
@ -35,127 +31,22 @@ impl PathOpener for BufferOpener {
|
||||
&self,
|
||||
project: &mut Project,
|
||||
project_path: ProjectPath,
|
||||
window_id: usize,
|
||||
cx: &mut ModelContext<Project>,
|
||||
) -> Option<Task<Result<Box<dyn ItemHandle>>>> {
|
||||
) -> Option<Task<Result<Box<dyn ItemViewHandle>>>> {
|
||||
let buffer = project.open_buffer(project_path, cx);
|
||||
let task = cx.spawn(|_, _| async move {
|
||||
Some(cx.spawn(|project, mut cx| async move {
|
||||
let buffer = buffer.await?;
|
||||
Ok(Box::new(BufferItemHandle(buffer)) as Box<dyn ItemHandle>)
|
||||
});
|
||||
Some(task)
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemHandle for BufferItemHandle {
|
||||
fn add_view(
|
||||
&self,
|
||||
window_id: usize,
|
||||
workspace: &Workspace,
|
||||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Box<dyn ItemViewHandle> {
|
||||
let buffer = cx.add_model(|cx| MultiBuffer::singleton(self.0.clone(), cx));
|
||||
Box::new(cx.add_view(window_id, |cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, Some(workspace.project().clone()), cx);
|
||||
editor.nav_history = Some(ItemNavHistory::new(nav_history, &cx.handle()));
|
||||
editor
|
||||
let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
let editor = cx.add_view(window_id, |cx| {
|
||||
Editor::for_buffer(multibuffer, Some(project), cx)
|
||||
});
|
||||
Ok(Box::new(editor) as Box<dyn ItemViewHandle>)
|
||||
}))
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn ItemHandle> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn to_any(&self) -> gpui::AnyModelHandle {
|
||||
self.0.clone().into()
|
||||
}
|
||||
|
||||
fn downgrade(&self) -> Box<dyn workspace::WeakItemHandle> {
|
||||
Box::new(WeakBufferItemHandle(self.0.downgrade()))
|
||||
}
|
||||
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
||||
File::from_dyn(self.0.read(cx).file()).map(|f| ProjectPath {
|
||||
worktree_id: f.worktree_id(cx),
|
||||
path: f.path().clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn id(&self) -> usize {
|
||||
self.0.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemHandle for MultiBufferItemHandle {
|
||||
fn add_view(
|
||||
&self,
|
||||
window_id: usize,
|
||||
workspace: &Workspace,
|
||||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Box<dyn ItemViewHandle> {
|
||||
Box::new(cx.add_view(window_id, |cx| {
|
||||
let mut editor =
|
||||
Editor::for_buffer(self.0.clone(), Some(workspace.project().clone()), cx);
|
||||
editor.nav_history = Some(ItemNavHistory::new(nav_history, &cx.handle()));
|
||||
editor
|
||||
}))
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn ItemHandle> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn to_any(&self) -> gpui::AnyModelHandle {
|
||||
self.0.clone().into()
|
||||
}
|
||||
|
||||
fn downgrade(&self) -> Box<dyn WeakItemHandle> {
|
||||
Box::new(WeakMultiBufferItemHandle(self.0.downgrade()))
|
||||
}
|
||||
|
||||
fn project_path(&self, _: &AppContext) -> Option<ProjectPath> {
|
||||
None
|
||||
}
|
||||
|
||||
fn id(&self) -> usize {
|
||||
self.0.id()
|
||||
}
|
||||
}
|
||||
|
||||
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>)
|
||||
}
|
||||
|
||||
fn id(&self) -> usize {
|
||||
self.0.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl WeakItemHandle for WeakMultiBufferItemHandle {
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
|
||||
self.0
|
||||
.upgrade(cx)
|
||||
.map(|buffer| Box::new(MultiBufferItemHandle(buffer)) as Box<dyn ItemHandle>)
|
||||
}
|
||||
|
||||
fn id(&self) -> usize {
|
||||
self.0.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemView for Editor {
|
||||
fn item(&self, cx: &AppContext) -> Box<dyn ItemHandle> {
|
||||
if let Some(buffer) = self.buffer.read(cx).as_singleton() {
|
||||
Box::new(BufferItemHandle(buffer))
|
||||
} else {
|
||||
Box::new(MultiBufferItemHandle(self.buffer.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) {
|
||||
if let Some(data) = data.downcast_ref::<NavigationData>() {
|
||||
let buffer = self.buffer.read(cx).read(cx);
|
||||
@ -184,15 +75,19 @@ impl ItemView for Editor {
|
||||
})
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
nav_history: ItemNavHistory,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Self>
|
||||
fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry> {
|
||||
File::from_dyn(self.buffer().read(cx).file(cx)).and_then(|file| file.project_entry(cx))
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Some(self.clone(nav_history, cx))
|
||||
Some(self.clone(cx))
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
|
||||
self.nav_history = Some(history);
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
|
@ -595,6 +595,14 @@ impl AsyncAppContext {
|
||||
self.update(|cx| cx.add_model(build_model))
|
||||
}
|
||||
|
||||
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
||||
where
|
||||
T: View,
|
||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||
{
|
||||
self.update(|cx| cx.add_view(window_id, build_view))
|
||||
}
|
||||
|
||||
pub fn platform(&self) -> Arc<dyn Platform> {
|
||||
self.0.borrow().platform()
|
||||
}
|
||||
@ -3459,6 +3467,12 @@ impl<T> PartialEq for ViewHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<WeakViewHandle<T>> for ViewHandle<T> {
|
||||
fn eq(&self, other: &WeakViewHandle<T>) -> bool {
|
||||
self.window_id == other.window_id && self.view_id == other.view_id
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for ViewHandle<T> {}
|
||||
|
||||
impl<T> Debug for ViewHandle<T> {
|
||||
|
@ -3221,6 +3221,16 @@ impl Project {
|
||||
self.active_entry
|
||||
}
|
||||
|
||||
pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<ProjectEntry> {
|
||||
self.worktree_for_id(path.worktree_id, cx)?
|
||||
.read(cx)
|
||||
.entry_for_path(&path.path)
|
||||
.map(|entry| ProjectEntry {
|
||||
worktree_id: path.worktree_id,
|
||||
entry_id: entry.id,
|
||||
})
|
||||
}
|
||||
|
||||
// RPC message handlers
|
||||
|
||||
async fn handle_unshare_project(
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::ProjectEntry;
|
||||
|
||||
use super::{
|
||||
fs::{self, Fs},
|
||||
ignore::IgnoreStack,
|
||||
@ -1502,6 +1504,13 @@ impl File {
|
||||
pub fn worktree_id(&self, cx: &AppContext) -> WorktreeId {
|
||||
self.worktree.read(cx).id()
|
||||
}
|
||||
|
||||
pub fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry> {
|
||||
self.entry_id.map(|entry_id| ProjectEntry {
|
||||
worktree_id: self.worktree_id(cx),
|
||||
entry_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -1,6 +1,5 @@
|
||||
use editor::{
|
||||
combine_syntax_and_fuzzy_match_highlights, items::BufferItemHandle, styled_runs_for_code_label,
|
||||
Autoscroll, Bias, Editor,
|
||||
combine_syntax_and_fuzzy_match_highlights, styled_runs_for_code_label, Autoscroll, Bias, Editor,
|
||||
};
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
@ -346,6 +345,7 @@ impl ProjectSymbolsView {
|
||||
let buffer = workspace
|
||||
.project()
|
||||
.update(cx, |project, cx| project.open_buffer_for_symbol(symbol, cx));
|
||||
|
||||
let symbol = symbol.clone();
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
let buffer = buffer.await?;
|
||||
@ -353,10 +353,8 @@ impl ProjectSymbolsView {
|
||||
let position = buffer
|
||||
.read(cx)
|
||||
.clip_point_utf16(symbol.range.start, Bias::Left);
|
||||
let editor = workspace
|
||||
.open_item(BufferItemHandle(buffer), cx)
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
|
||||
let editor = Editor::find_or_create(workspace, buffer, cx);
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.select_ranges(
|
||||
[position..position],
|
||||
|
@ -7,7 +7,7 @@ use editor::{Anchor, Autoscroll, Editor, MultiBuffer, SelectAll};
|
||||
use gpui::{
|
||||
action, elements::*, keymap::Binding, platform::CursorStyle, AppContext, ElementBox, Entity,
|
||||
ModelContext, ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext,
|
||||
ViewHandle, WeakModelHandle,
|
||||
ViewHandle, WeakModelHandle, WeakViewHandle,
|
||||
};
|
||||
use project::{search::SearchQuery, Project};
|
||||
use std::{
|
||||
@ -16,7 +16,7 @@ use std::{
|
||||
path::PathBuf,
|
||||
};
|
||||
use util::ResultExt as _;
|
||||
use workspace::{Item, ItemHandle, ItemNavHistory, ItemView, Settings, Workspace};
|
||||
use workspace::{ItemNavHistory, ItemView, Settings, Workspace};
|
||||
|
||||
action!(Deploy);
|
||||
action!(Search);
|
||||
@ -26,7 +26,7 @@ action!(ToggleFocus);
|
||||
const MAX_TAB_TITLE_LEN: usize = 24;
|
||||
|
||||
#[derive(Default)]
|
||||
struct ActiveSearches(HashMap<WeakModelHandle<Project>, WeakModelHandle<ProjectSearch>>);
|
||||
struct ActiveSearches(HashMap<WeakModelHandle<Project>, WeakViewHandle<ProjectSearchView>>);
|
||||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_app_state(ActiveSearches::default());
|
||||
@ -139,23 +139,6 @@ impl ProjectSearch {
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for ProjectSearch {
|
||||
type View = ProjectSearchView;
|
||||
|
||||
fn build_view(
|
||||
model: ModelHandle<Self>,
|
||||
_: &Workspace,
|
||||
nav_history: ItemNavHistory,
|
||||
cx: &mut gpui::ViewContext<Self::View>,
|
||||
) -> Self::View {
|
||||
ProjectSearchView::new(model, Some(nav_history), cx)
|
||||
}
|
||||
|
||||
fn project_path(&self) -> Option<project::ProjectPath> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
enum ViewEvent {
|
||||
UpdateTab,
|
||||
}
|
||||
@ -199,11 +182,11 @@ impl View for ProjectSearchView {
|
||||
}
|
||||
|
||||
fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let handle = cx.weak_handle();
|
||||
cx.update_app_state(|state: &mut ActiveSearches, cx| {
|
||||
state.0.insert(
|
||||
self.model.read(cx).project.downgrade(),
|
||||
self.model.downgrade(),
|
||||
)
|
||||
state
|
||||
.0
|
||||
.insert(self.model.read(cx).project.downgrade(), handle)
|
||||
});
|
||||
|
||||
if self.model.read(cx).match_ranges.is_empty() {
|
||||
@ -235,10 +218,6 @@ impl ItemView for ProjectSearchView {
|
||||
.update(cx, |editor, cx| editor.deactivated(cx));
|
||||
}
|
||||
|
||||
fn item(&self, _: &gpui::AppContext) -> Box<dyn ItemHandle> {
|
||||
Box::new(self.model.clone())
|
||||
}
|
||||
|
||||
fn tab_content(&self, tab_theme: &theme::Tab, cx: &gpui::AppContext) -> ElementBox {
|
||||
let settings = cx.app_state::<Settings>();
|
||||
let search_theme = &settings.theme.search;
|
||||
@ -271,6 +250,10 @@ impl ItemView for ProjectSearchView {
|
||||
None
|
||||
}
|
||||
|
||||
fn project_entry(&self, _: &AppContext) -> Option<project::ProjectEntry> {
|
||||
None
|
||||
}
|
||||
|
||||
fn can_save(&self, _: &gpui::AppContext) -> bool {
|
||||
true
|
||||
}
|
||||
@ -305,16 +288,18 @@ impl ItemView for ProjectSearchView {
|
||||
unreachable!("save_as should not have been called")
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
nav_history: ItemNavHistory,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Self>
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let model = self.model.update(cx, |model, cx| model.clone(cx));
|
||||
Some(Self::new(model, Some(nav_history), cx))
|
||||
Some(Self::new(model, cx))
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
||||
self.results_editor.update(cx, |editor, _| {
|
||||
editor.set_nav_history(Some(nav_history));
|
||||
});
|
||||
}
|
||||
|
||||
fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) {
|
||||
@ -328,11 +313,7 @@ impl ItemView for ProjectSearchView {
|
||||
}
|
||||
|
||||
impl ProjectSearchView {
|
||||
fn new(
|
||||
model: ModelHandle<ProjectSearch>,
|
||||
nav_history: Option<ItemNavHistory>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
fn new(model: ModelHandle<ProjectSearch>, cx: &mut ViewContext<Self>) -> Self {
|
||||
let project;
|
||||
let excerpts;
|
||||
let mut query_text = String::new();
|
||||
@ -364,7 +345,6 @@ impl ProjectSearchView {
|
||||
let results_editor = cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(excerpts, Some(project), cx);
|
||||
editor.set_searchable(false);
|
||||
editor.set_nav_history(nav_history);
|
||||
editor
|
||||
});
|
||||
cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab))
|
||||
@ -406,16 +386,19 @@ impl ProjectSearchView {
|
||||
let existing = active_search
|
||||
.and_then(|active_search| {
|
||||
workspace
|
||||
.items_of_type::<ProjectSearch>(cx)
|
||||
.items_of_type::<ProjectSearchView>(cx)
|
||||
.find(|search| search == active_search)
|
||||
})
|
||||
.or_else(|| workspace.item_of_type::<ProjectSearch>(cx));
|
||||
.or_else(|| workspace.item_of_type::<ProjectSearchView>(cx));
|
||||
|
||||
if let Some(existing) = existing {
|
||||
workspace.activate_item(&existing, cx);
|
||||
} else {
|
||||
let model = cx.add_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||
workspace.open_item(model, cx);
|
||||
workspace.open_item(
|
||||
Box::new(cx.add_view(|cx| ProjectSearchView::new(model, cx))),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,7 +433,10 @@ impl ProjectSearchView {
|
||||
model.search(new_query, cx);
|
||||
model
|
||||
});
|
||||
workspace.open_item(model, cx);
|
||||
workspace.open_item(
|
||||
Box::new(cx.add_view(|cx| ProjectSearchView::new(model, cx))),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -732,7 +718,7 @@ mod tests {
|
||||
|
||||
let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
|
||||
let search_view = cx.add_view(Default::default(), |cx| {
|
||||
ProjectSearchView::new(search.clone(), None, cx)
|
||||
ProjectSearchView::new(search.clone(), cx)
|
||||
});
|
||||
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{ItemViewHandle, SplitDirection};
|
||||
use crate::{ItemHandle, ItemView, Settings, WeakItemViewHandle, Workspace};
|
||||
use crate::{ItemView, Settings, WeakItemViewHandle, Workspace};
|
||||
use collections::{HashMap, VecDeque};
|
||||
use gpui::{
|
||||
action,
|
||||
@ -10,7 +10,7 @@ use gpui::{
|
||||
AnyViewHandle, Entity, MutableAppContext, Quad, RenderContext, Task, View, ViewContext,
|
||||
ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use project::ProjectPath;
|
||||
use project::{ProjectEntry, ProjectPath};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
@ -97,7 +97,7 @@ pub enum Event {
|
||||
}
|
||||
|
||||
pub struct Pane {
|
||||
item_views: Vec<(usize, Box<dyn ItemViewHandle>)>,
|
||||
item_views: Vec<(Option<usize>, Box<dyn ItemViewHandle>)>,
|
||||
active_item_index: usize,
|
||||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
toolbars: HashMap<TypeId, Box<dyn ToolbarHandle>>,
|
||||
@ -281,27 +281,23 @@ impl Pane {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_item<T>(
|
||||
pub fn open_item(
|
||||
&mut self,
|
||||
item_handle: T,
|
||||
workspace: &Workspace,
|
||||
item_view_to_open: Box<dyn ItemViewHandle>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Box<dyn ItemViewHandle>
|
||||
where
|
||||
T: 'static + ItemHandle,
|
||||
{
|
||||
for (ix, (item_id, item_view)) in self.item_views.iter().enumerate() {
|
||||
if *item_id == item_handle.id() {
|
||||
) -> Box<dyn ItemViewHandle> {
|
||||
// Find an existing view for the same project entry.
|
||||
for (ix, (entry_id, item_view)) in self.item_views.iter().enumerate() {
|
||||
if *entry_id == item_view_to_open.project_entry_id(cx) {
|
||||
let item_view = item_view.boxed_clone();
|
||||
self.activate_item(ix, cx);
|
||||
return item_view;
|
||||
}
|
||||
}
|
||||
|
||||
let item_view =
|
||||
item_handle.add_view(cx.window_id(), workspace, self.nav_history.clone(), cx);
|
||||
self.add_item_view(item_view.boxed_clone(), cx);
|
||||
item_view
|
||||
item_view_to_open.set_nav_history(self.nav_history.clone(), cx);
|
||||
self.add_item_view(item_view_to_open.boxed_clone(), cx);
|
||||
item_view_to_open
|
||||
}
|
||||
|
||||
pub fn add_item_view(
|
||||
@ -312,18 +308,11 @@ impl Pane {
|
||||
item_view.added_to_pane(cx);
|
||||
let item_idx = cmp::min(self.active_item_index + 1, self.item_views.len());
|
||||
self.item_views
|
||||
.insert(item_idx, (item_view.item(cx).id(), item_view));
|
||||
.insert(item_idx, (item_view.project_entry_id(cx), item_view));
|
||||
self.activate_item(item_idx, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn contains_item(&self, item: &dyn ItemHandle) -> bool {
|
||||
let item_id = item.id();
|
||||
self.item_views
|
||||
.iter()
|
||||
.any(|(existing_item_id, _)| *existing_item_id == item_id)
|
||||
}
|
||||
|
||||
pub fn item_views(&self) -> impl Iterator<Item = &Box<dyn ItemViewHandle>> {
|
||||
self.item_views.iter().map(|(_, view)| view)
|
||||
}
|
||||
@ -334,14 +323,26 @@ impl Pane {
|
||||
.map(|(_, view)| view.clone())
|
||||
}
|
||||
|
||||
pub fn item_for_entry(&self, entry: ProjectEntry) -> Option<Box<dyn ItemViewHandle>> {
|
||||
self.item_views.iter().find_map(|(id, view)| {
|
||||
if *id == Some(entry.entry_id) {
|
||||
Some(view.boxed_clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn index_for_item_view(&self, item_view: &dyn ItemViewHandle) -> Option<usize> {
|
||||
self.item_views
|
||||
.iter()
|
||||
.position(|(_, i)| i.id() == item_view.id())
|
||||
}
|
||||
|
||||
pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option<usize> {
|
||||
self.item_views.iter().position(|(id, _)| *id == item.id())
|
||||
pub fn index_for_item(&self, item: &dyn ItemViewHandle) -> Option<usize> {
|
||||
self.item_views
|
||||
.iter()
|
||||
.position(|(_, my_item)| my_item.id() == item.id())
|
||||
}
|
||||
|
||||
pub fn activate_item(&mut self, index: usize, cx: &mut ViewContext<Self>) {
|
||||
|
@ -9,7 +9,6 @@ mod status_bar;
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{Authenticate, ChannelList, Client, User, UserStore};
|
||||
use clock::ReplicaId;
|
||||
use collections::BTreeMap;
|
||||
use gpui::{
|
||||
action,
|
||||
color::Color,
|
||||
@ -18,16 +17,16 @@ use gpui::{
|
||||
json::{self, to_string_pretty, ToJson},
|
||||
keymap::Binding,
|
||||
platform::{CursorStyle, WindowOptions},
|
||||
AnyModelHandle, AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, ModelContext,
|
||||
ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View,
|
||||
ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle,
|
||||
AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, ModelContext, ModelHandle,
|
||||
MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext,
|
||||
ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use language::LanguageRegistry;
|
||||
use log::error;
|
||||
pub use pane::*;
|
||||
pub use pane_group::*;
|
||||
use postage::prelude::Stream;
|
||||
use project::{fs, Fs, Project, ProjectPath, Worktree};
|
||||
use project::{fs, Fs, Project, ProjectEntry, ProjectPath, Worktree};
|
||||
pub use settings::Settings;
|
||||
use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus};
|
||||
use status_bar::StatusBar;
|
||||
@ -35,9 +34,7 @@ pub use status_bar::StatusItemView;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
cmp::Reverse,
|
||||
future::Future,
|
||||
hash::{Hash, Hasher},
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
@ -131,30 +128,19 @@ pub trait PathOpener {
|
||||
&self,
|
||||
project: &mut Project,
|
||||
path: ProjectPath,
|
||||
window_id: usize,
|
||||
cx: &mut ModelContext<Project>,
|
||||
) -> Option<Task<Result<Box<dyn ItemHandle>>>>;
|
||||
}
|
||||
|
||||
pub trait Item: Entity + Sized {
|
||||
type View: ItemView;
|
||||
|
||||
fn build_view(
|
||||
handle: ModelHandle<Self>,
|
||||
workspace: &Workspace,
|
||||
nav_history: ItemNavHistory,
|
||||
cx: &mut ViewContext<Self::View>,
|
||||
) -> Self::View;
|
||||
|
||||
fn project_path(&self) -> Option<ProjectPath>;
|
||||
) -> Option<Task<Result<Box<dyn ItemViewHandle>>>>;
|
||||
}
|
||||
|
||||
pub trait ItemView: View {
|
||||
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
||||
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {}
|
||||
fn item(&self, cx: &AppContext) -> Box<dyn ItemHandle>;
|
||||
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
||||
fn clone_on_split(&self, _: ItemNavHistory, _: &mut ViewContext<Self>) -> Option<Self>
|
||||
fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry>;
|
||||
fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>);
|
||||
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@ -202,36 +188,13 @@ pub trait ItemView: View {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ItemHandle: Send + Sync {
|
||||
fn id(&self) -> usize;
|
||||
fn add_view(
|
||||
&self,
|
||||
window_id: usize,
|
||||
workspace: &Workspace,
|
||||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Box<dyn ItemViewHandle>;
|
||||
fn boxed_clone(&self) -> Box<dyn ItemHandle>;
|
||||
fn downgrade(&self) -> Box<dyn WeakItemHandle>;
|
||||
fn to_any(&self) -> AnyModelHandle;
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
||||
}
|
||||
|
||||
pub trait WeakItemHandle {
|
||||
fn id(&self) -> usize;
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>>;
|
||||
}
|
||||
|
||||
pub trait ItemViewHandle: 'static {
|
||||
fn item(&self, cx: &AppContext) -> Box<dyn ItemHandle>;
|
||||
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<usize>;
|
||||
fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Option<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>>;
|
||||
fn added_to_pane(&mut self, cx: &mut ViewContext<Pane>);
|
||||
fn deactivated(&self, cx: &mut MutableAppContext);
|
||||
fn navigate(&self, data: Box<dyn Any>, cx: &mut MutableAppContext);
|
||||
@ -256,97 +219,6 @@ pub trait WeakItemViewHandle {
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemViewHandle>>;
|
||||
}
|
||||
|
||||
impl<T: Item> ItemHandle for ModelHandle<T> {
|
||||
fn id(&self) -> usize {
|
||||
self.id()
|
||||
}
|
||||
|
||||
fn add_view(
|
||||
&self,
|
||||
window_id: usize,
|
||||
workspace: &Workspace,
|
||||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Box<dyn ItemViewHandle> {
|
||||
Box::new(cx.add_view(window_id, |cx| {
|
||||
let nav_history = ItemNavHistory::new(nav_history, &cx.handle());
|
||||
T::build_view(self.clone(), workspace, nav_history, cx)
|
||||
}))
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn ItemHandle> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn downgrade(&self) -> Box<dyn WeakItemHandle> {
|
||||
Box::new(self.downgrade())
|
||||
}
|
||||
|
||||
fn to_any(&self) -> AnyModelHandle {
|
||||
self.clone().into()
|
||||
}
|
||||
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
||||
self.read(cx).project_path()
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemHandle for Box<dyn ItemHandle> {
|
||||
fn id(&self) -> usize {
|
||||
ItemHandle::id(self.as_ref())
|
||||
}
|
||||
|
||||
fn add_view(
|
||||
&self,
|
||||
window_id: usize,
|
||||
workspace: &Workspace,
|
||||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Box<dyn ItemViewHandle> {
|
||||
ItemHandle::add_view(self.as_ref(), window_id, workspace, nav_history, cx)
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn ItemHandle> {
|
||||
self.as_ref().boxed_clone()
|
||||
}
|
||||
|
||||
fn downgrade(&self) -> Box<dyn WeakItemHandle> {
|
||||
self.as_ref().downgrade()
|
||||
}
|
||||
|
||||
fn to_any(&self) -> AnyModelHandle {
|
||||
self.as_ref().to_any()
|
||||
}
|
||||
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
||||
self.as_ref().project_path(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Item> WeakItemHandle for WeakModelHandle<T> {
|
||||
fn id(&self) -> usize {
|
||||
WeakModelHandle::id(self)
|
||||
}
|
||||
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
|
||||
WeakModelHandle::<T>::upgrade(self, cx).map(|i| Box::new(i) as Box<dyn ItemHandle>)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Box<dyn WeakItemHandle> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.id().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Box<dyn WeakItemHandle> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Box<dyn WeakItemHandle> {}
|
||||
|
||||
impl dyn ItemViewHandle {
|
||||
pub fn downcast<T: View>(&self) -> Option<ViewHandle<T>> {
|
||||
self.to_any().downcast()
|
||||
@ -359,10 +231,6 @@ impl dyn ItemViewHandle {
|
||||
}
|
||||
|
||||
impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
||||
fn item(&self, cx: &AppContext) -> Box<dyn ItemHandle> {
|
||||
self.read(cx).item(cx)
|
||||
}
|
||||
|
||||
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox {
|
||||
self.read(cx).tab_content(style, cx)
|
||||
}
|
||||
@ -371,23 +239,31 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
||||
self.read(cx).project_path(cx)
|
||||
}
|
||||
|
||||
fn project_entry_id(&self, cx: &AppContext) -> Option<usize> {
|
||||
Some(self.read(cx).project_entry(cx)?.entry_id)
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn ItemViewHandle> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
// nav_history: Rc<RefCell<NavHistory>>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Option<Box<dyn ItemViewHandle>> {
|
||||
self.update(cx, |item, cx| {
|
||||
cx.add_option_view(|cx| {
|
||||
item.clone_on_split(ItemNavHistory::new(nav_history, &cx.handle()), cx)
|
||||
})
|
||||
cx.add_option_view(|cx| item.clone_on_split(cx))
|
||||
})
|
||||
.map(|handle| Box::new(handle) as Box<dyn ItemViewHandle>)
|
||||
}
|
||||
|
||||
fn set_nav_history(&self, nav_history: Rc<RefCell<NavHistory>>, cx: &mut MutableAppContext) {
|
||||
self.update(cx, |item, cx| {
|
||||
item.set_nav_history(ItemNavHistory::new(nav_history, &cx.handle()), cx);
|
||||
})
|
||||
}
|
||||
|
||||
fn added_to_pane(&mut self, cx: &mut ViewContext<Pane>) {
|
||||
cx.subscribe(self, |pane, item, event, cx| {
|
||||
if T::should_close_item_on_event(event) {
|
||||
@ -469,12 +345,6 @@ impl Clone for Box<dyn ItemViewHandle> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn ItemHandle> {
|
||||
fn clone(&self) -> Box<dyn ItemHandle> {
|
||||
self.boxed_clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ItemView> WeakItemViewHandle for WeakViewHandle<T> {
|
||||
fn id(&self) -> usize {
|
||||
self.id()
|
||||
@ -563,7 +433,7 @@ pub struct Workspace {
|
||||
status_bar: ViewHandle<StatusBar>,
|
||||
project: ModelHandle<Project>,
|
||||
path_openers: Arc<[Box<dyn PathOpener>]>,
|
||||
items: BTreeMap<Reverse<usize>, Box<dyn WeakItemHandle>>,
|
||||
// items: BTreeMap<Reverse<usize>, Box<dyn WeakItemHandle>>,
|
||||
_observe_current_user: Task<()>,
|
||||
}
|
||||
|
||||
@ -627,7 +497,6 @@ impl Workspace {
|
||||
right_sidebar: Sidebar::new(Side::Right),
|
||||
project: params.project.clone(),
|
||||
path_openers: params.path_openers.clone(),
|
||||
items: Default::default(),
|
||||
_observe_current_user,
|
||||
}
|
||||
}
|
||||
@ -804,16 +673,23 @@ impl Workspace {
|
||||
&mut self,
|
||||
path: ProjectPath,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<Box<dyn ItemHandle>>> {
|
||||
if let Some(existing_item) = self.item_for_path(&path, cx) {
|
||||
) -> Task<Result<Box<dyn ItemViewHandle>>> {
|
||||
let project_entry = self.project.read(cx).entry_for_path(&path, cx);
|
||||
|
||||
if let Some(existing_item) = project_entry.and_then(|entry| {
|
||||
self.panes
|
||||
.iter()
|
||||
.find_map(|pane| pane.read(cx).item_for_entry(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(), cx) {
|
||||
if let Some(task) = opener.open(project, project_path.clone(), window_id, cx) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
@ -821,26 +697,19 @@ impl Workspace {
|
||||
})
|
||||
}
|
||||
|
||||
fn item_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
|
||||
self.items
|
||||
.values()
|
||||
.filter_map(|i| i.upgrade(cx))
|
||||
.find(|i| i.project_path(cx).as_ref() == Some(path))
|
||||
pub fn item_of_type<T: ItemView>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
|
||||
self.items_of_type(cx).max_by_key(|item| item.id())
|
||||
}
|
||||
|
||||
pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<ModelHandle<T>> {
|
||||
self.items
|
||||
.values()
|
||||
.find_map(|i| i.upgrade(cx).and_then(|i| i.to_any().downcast()))
|
||||
}
|
||||
|
||||
pub fn items_of_type<'a, T: Item>(
|
||||
pub fn items_of_type<'a, T: ItemView>(
|
||||
&'a self,
|
||||
cx: &'a AppContext,
|
||||
) -> impl 'a + Iterator<Item = ModelHandle<T>> {
|
||||
self.items
|
||||
.values()
|
||||
.filter_map(|i| i.upgrade(cx).and_then(|i| i.to_any().downcast()))
|
||||
) -> impl 'a + Iterator<Item = ViewHandle<T>> {
|
||||
self.panes.iter().flat_map(|pane| {
|
||||
pane.read(cx)
|
||||
.item_views()
|
||||
.filter_map(|item| item.to_any().downcast())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn active_item(&self, cx: &AppContext) -> Option<Box<dyn ItemViewHandle>> {
|
||||
@ -962,52 +831,46 @@ impl Workspace {
|
||||
pane
|
||||
}
|
||||
|
||||
pub fn open_item<T>(
|
||||
pub fn open_item(
|
||||
&mut self,
|
||||
item_handle: T,
|
||||
item_view: Box<dyn ItemViewHandle>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Box<dyn ItemViewHandle>
|
||||
where
|
||||
T: 'static + ItemHandle,
|
||||
{
|
||||
self.open_item_in_pane(item_handle, &self.active_pane().clone(), cx)
|
||||
) -> Box<dyn ItemViewHandle> {
|
||||
self.open_item_in_pane(item_view, &self.active_pane().clone(), cx)
|
||||
}
|
||||
|
||||
pub fn open_item_in_pane<T>(
|
||||
pub fn open_item_in_pane(
|
||||
&mut self,
|
||||
item_handle: T,
|
||||
item_view: Box<dyn ItemViewHandle>,
|
||||
pane: &ViewHandle<Pane>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Box<dyn ItemViewHandle> {
|
||||
pane.update(cx, |pane, cx| pane.open_item(item_view, cx))
|
||||
}
|
||||
|
||||
pub fn open_item_for_project_entry<T, F>(
|
||||
&mut self,
|
||||
project_entry: ProjectEntry,
|
||||
cx: &mut ViewContext<Self>,
|
||||
build_view: F,
|
||||
) -> Box<dyn ItemViewHandle>
|
||||
where
|
||||
T: 'static + ItemHandle,
|
||||
T: ItemView,
|
||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||
{
|
||||
self.items
|
||||
.insert(Reverse(item_handle.id()), item_handle.downgrade());
|
||||
pane.update(cx, |pane, cx| pane.open_item(item_handle, self, cx))
|
||||
}
|
||||
|
||||
pub fn activate_pane_for_item(
|
||||
&mut self,
|
||||
item: &dyn ItemHandle,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> bool {
|
||||
let pane = self.panes.iter().find_map(|pane| {
|
||||
if pane.read(cx).contains_item(item) {
|
||||
Some(pane.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(pane) = pane {
|
||||
self.activate_pane(pane.clone(), cx);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
if let Some(existing_item) = self
|
||||
.panes
|
||||
.iter()
|
||||
.find_map(|pane| pane.read(cx).item_for_entry(project_entry))
|
||||
{
|
||||
return existing_item.boxed_clone();
|
||||
}
|
||||
|
||||
let view = Box::new(cx.add_view(build_view));
|
||||
self.open_item(view, cx)
|
||||
}
|
||||
|
||||
pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext<Self>) -> bool {
|
||||
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) {
|
||||
Some((pane.clone(), ix))
|
||||
@ -1078,9 +941,8 @@ impl Workspace {
|
||||
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();
|
||||
if let Some(clone) = item.clone_on_split(nav_history, cx.as_mut()) {
|
||||
let item = clone.item(cx).downgrade();
|
||||
self.items.insert(Reverse(item.id()), item);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user