diff --git a/Cargo.lock b/Cargo.lock index 28c433c43f85e7df2de479d96eaf99b3d1a453d0..4cfb831a58cd49ea148afd07382d7fd983795e58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1782,7 +1782,9 @@ dependencies = [ name = "file_finder" version = "0.1.0" dependencies = [ + "ctor", "editor", + "env_logger", "fuzzy", "gpui", "postage", diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index ceeddc599a0d3d90b0328e2d6d94148b98e42002..bed338d3f43c3ab2e3bbd311b237eed8a015ba34 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -64,13 +64,13 @@ impl ChatPanel { ix, item_type, is_hovered, - &cx.app_state::().theme.chat_panel.channel_select, + &cx.global::().theme.chat_panel.channel_select, cx, ) } }) .with_style(move |cx| { - let theme = &cx.app_state::().theme.chat_panel.channel_select; + let theme = &cx.global::().theme.chat_panel.channel_select; SelectStyle { header: theme.header.container.clone(), menu: theme.menu.clone(), @@ -200,7 +200,7 @@ impl ChatPanel { } fn render_channel(&self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.app_state::().theme; + let theme = &cx.global::().theme; Flex::column() .with_child( Container::new(ChildView::new(&self.channel_select).boxed()) @@ -224,7 +224,7 @@ impl ChatPanel { fn render_message(&self, message: &ChannelMessage, cx: &AppContext) -> ElementBox { let now = OffsetDateTime::now_utc(); - let settings = cx.app_state::(); + let settings = cx.global::(); let theme = if message.is_pending() { &settings.theme.chat_panel.pending_message } else { @@ -267,7 +267,7 @@ impl ChatPanel { } fn render_input_box(&self, cx: &AppContext) -> ElementBox { - let theme = &cx.app_state::().theme; + let theme = &cx.global::().theme; Container::new(ChildView::new(&self.input_editor).boxed()) .with_style(theme.chat_panel.input_editor.container) .boxed() @@ -304,7 +304,7 @@ impl ChatPanel { } fn render_sign_in_prompt(&self, cx: &mut RenderContext) -> ElementBox { - let theme = cx.app_state::().theme.clone(); + let theme = cx.global::().theme.clone(); let rpc = self.rpc.clone(); let this = cx.handle(); @@ -385,7 +385,7 @@ impl View for ChatPanel { } else { self.render_sign_in_prompt(cx) }; - let theme = &cx.app_state::().theme; + let theme = &cx.global::().theme; ConstrainedBox::new( Container::new(element) .with_style(theme.chat_panel.container) diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index b5740a5ed75bde0fdfd7233133a4afd025c2f80e..b8b5b3a361bfa548372b81d982094ab2e0cf19ad 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -55,7 +55,7 @@ impl ContactsPanel { app_state: Arc, cx: &mut LayoutContext, ) -> ElementBox { - let theme = cx.app_state::().theme.clone(); + let theme = cx.global::().theme.clone(); let theme = &theme.contacts_panel; let project_count = collaborator.projects.len(); let font_cache = cx.font_cache(); @@ -236,7 +236,7 @@ impl View for ContactsPanel { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.app_state::().theme.contacts_panel; + let theme = &cx.global::().theme.contacts_panel; Container::new(List::new(self.contacts.clone()).boxed()) .with_style(theme.container) .boxed() diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 1a432f52e20658eacd3141aa141d243324b89299..a1dd7be744ae4c2f79ee78075f620c60595ea640 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -25,7 +25,7 @@ use std::{ sync::Arc, }; use util::TryFutureExt; -use workspace::{ItemHandle, ItemNavHistory, ItemViewHandle as _, Settings, Workspace}; +use workspace::{ItemHandle as _, ItemNavHistory, Settings, Workspace}; action!(Deploy); @@ -38,12 +38,8 @@ pub fn init(cx: &mut MutableAppContext) { type Event = editor::Event; -struct ProjectDiagnostics { - project: ModelHandle, -} - struct ProjectDiagnosticsEditor { - model: ModelHandle, + project: ModelHandle, workspace: WeakViewHandle, editor: ViewHandle, summary: DiagnosticSummary, @@ -65,16 +61,6 @@ struct DiagnosticGroupState { block_count: usize, } -impl ProjectDiagnostics { - fn new(project: ModelHandle) -> Self { - Self { project } - } -} - -impl Entity for ProjectDiagnostics { - type Event = (); -} - impl Entity for ProjectDiagnosticsEditor { type Event = Event; } @@ -86,7 +72,7 @@ impl View for ProjectDiagnosticsEditor { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { if self.path_states.is_empty() { - let theme = &cx.app_state::().theme.project_diagnostics; + let theme = &cx.global::().theme.project_diagnostics; Label::new( "No problems in workspace".to_string(), theme.empty_message.clone(), @@ -109,12 +95,11 @@ impl View for ProjectDiagnosticsEditor { impl ProjectDiagnosticsEditor { fn new( - model: ModelHandle, + project_handle: ModelHandle, workspace: WeakViewHandle, cx: &mut ViewContext, ) -> 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,22 @@ 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_multibuffer(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 +138,20 @@ impl ProjectDiagnosticsEditor { } fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext) { - if let Some(existing) = workspace.item_of_type::(cx) { + if let Some(existing) = workspace.item_of_type::(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.add_item(Box::new(diagnostics), cx); } } fn update_excerpts(&mut self, cx: &mut ViewContext) { 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,42 +432,17 @@ impl ProjectDiagnosticsEditor { } fn update_title(&mut self, cx: &mut ViewContext) { - 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, - workspace: &Workspace, - nav_history: ItemNavHistory, - cx: &mut ViewContext, - ) -> 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 { - None - } -} - -impl workspace::ItemView for ProjectDiagnosticsEditor { - fn item(&self, _: &AppContext) -> Box { - Box::new(self.model.clone()) - } - +impl workspace::Item for ProjectDiagnosticsEditor { fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox { render_summary( &self.summary, &style.label.text, - &cx.app_state::().theme.project_diagnostics, + &cx.global::().theme.project_diagnostics, ) } @@ -532,20 +496,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, - ) -> Option + fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext) { + self.editor.update(cx, |editor, _| { + editor.set_nav_history(Some(nav_history)); + }); + } + + fn clone_on_split(&self, cx: &mut ViewContext) -> Option 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( @@ -571,7 +536,7 @@ impl workspace::ItemView for ProjectDiagnosticsEditor { fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { let (message, highlights) = highlight_diagnostic_message(&diagnostic.message); Arc::new(move |cx| { - let settings = cx.app_state::(); + let settings = cx.global::(); let theme = &settings.theme.editor; let style = &theme.diagnostic_header; let font_size = (style.text_scale_factor * settings.buffer_font_size).round(); @@ -829,9 +794,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; diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 13d90f01190ea0eeddf14585193f22097988b326..690c5100ecf9ec3a8edf89414a2ef545f868201a 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -49,7 +49,7 @@ impl View for DiagnosticSummary { let in_progress = self.in_progress; MouseEventHandler::new::(0, cx, |_, cx| { - let theme = &cx.app_state::().theme.project_diagnostics; + let theme = &cx.global::().theme.project_diagnostics; if in_progress { Label::new( "Checking... ".to_string(), @@ -71,7 +71,7 @@ impl View for DiagnosticSummary { impl StatusItemView for DiagnosticSummary { fn set_active_pane_item( &mut self, - _: Option<&dyn workspace::ItemViewHandle>, + _: Option<&dyn workspace::ItemHandle>, _: &mut ViewContext, ) { } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8c6904d1360e63b5024f492985db5e18950cef82..30888d8a408ada78170e6dd6a39979392e085fe4 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -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::{ @@ -58,7 +57,7 @@ pub use sum_tree::Bias; use text::rope::TextDimension; use theme::DiagnosticStyle; use util::{post_inc, ResultExt, TryFutureExt}; -use workspace::{settings, ItemNavHistory, PathOpener, Settings, Workspace}; +use workspace::{settings, ItemNavHistory, Settings, Workspace}; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); const MAX_LINE_LEN: usize = 1024; @@ -142,8 +141,7 @@ pub enum Direction { Next, } -pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec>) { - path_openers.push(Box::new(items::BufferOpener)); +pub fn init(cx: &mut MutableAppContext) { cx.add_bindings(vec![ Binding::new("escape", Cancel, Some("Editor")), Binding::new("backspace", Backspace, Some("Editor")), @@ -341,6 +339,10 @@ pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec, + project: Option>, + cx: &mut ViewContext, + ) -> Self { + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + Self::new(EditorMode::Full, buffer, project, None, cx) + } + + pub fn for_multibuffer( buffer: ModelHandle, project: Option>, cx: &mut ViewContext, @@ -836,7 +847,7 @@ impl Editor { Self::new(EditorMode::Full, buffer, project, None, cx) } - pub fn clone(&self, nav_history: ItemNavHistory, cx: &mut ViewContext) -> Self { + pub fn clone(&self, cx: &mut ViewContext) -> Self { let mut clone = Self::new( self.mode, self.buffer.clone(), @@ -846,7 +857,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 } @@ -859,7 +869,7 @@ impl Editor { cx: &mut ViewContext, ) -> Self { let display_map = cx.add_model(|cx| { - let settings = cx.app_state::(); + let settings = cx.global::(); let style = build_style(&*settings, get_field_editor_theme, None, cx); DisplayMap::new( buffer.clone(), @@ -938,14 +948,17 @@ impl Editor { _: &workspace::OpenNew, cx: &mut ViewContext, ) { - 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); + workspace.add_item( + Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))), + cx, + ); } } @@ -981,7 +994,7 @@ impl Editor { fn style(&self, cx: &AppContext) -> EditorStyle { build_style( - cx.app_state::(), + cx.global::(), self.get_field_editor_theme, self.override_text_style.as_deref(), cx, @@ -2349,13 +2362,14 @@ impl Editor { }); workspace.update(&mut cx, |workspace, cx| { - let editor = workspace.open_item(MultiBufferItemHandle(excerpt_buffer), cx); - if let Some(editor) = editor.act_as::(cx) { - editor.update(cx, |editor, cx| { - let color = editor.style(cx).highlighted_line_background; - editor.highlight_background::(ranges_to_highlight, color, cx); - }); - } + let project = workspace.project().clone(); + let editor = + cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx)); + workspace.add_item(Box::new(editor.clone()), cx); + editor.update(cx, |editor, cx| { + let color = editor.style(cx).highlighted_line_background; + editor.highlight_background::(ranges_to_highlight, color, cx); + }); }); Ok(()) @@ -2699,7 +2713,7 @@ impl Editor { } self.start_transaction(cx); - let tab_size = cx.app_state::().tab_size; + let tab_size = cx.global::().tab_size; let mut selections = self.local_selections::(cx); let mut last_indent = None; self.buffer.update(cx, |buffer, cx| { @@ -2776,7 +2790,7 @@ impl Editor { } self.start_transaction(cx); - let tab_size = cx.app_state::().tab_size; + let tab_size = cx.global::().tab_size; let selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut deletion_ranges = Vec::new(); @@ -4280,20 +4294,16 @@ 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::() - .unwrap(); + let target_editor_handle = workspace.open_project_item(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 +4334,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 +4379,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::(cx) { - editor.update(cx, |editor, cx| { - let color = editor.style(cx).highlighted_line_background; - editor.highlight_background::(ranges_to_highlight, color, cx); - }); - } + let editor = + cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx)); + editor.update(cx, |editor, cx| { + let color = editor.style(cx).highlighted_line_background; + editor.highlight_background::(ranges_to_highlight, color, cx); + }); + workspace.add_item(Box::new(editor), cx); }); Ok(()) @@ -5298,7 +5307,7 @@ impl Editor { pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap { let language = self.language(cx); - let settings = cx.app_state::(); + let settings = cx.global::(); let mode = self .soft_wrap_mode_override .unwrap_or_else(|| settings.soft_wrap(language)); @@ -5563,17 +5572,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::() - .unwrap(); + workspace.activate_next_pane(cx); + for (buffer, ranges) in new_selections_by_buffer.into_iter() { + let editor = workspace.open_project_item::(buffer, cx); editor.update(cx, |editor, cx| { editor.select_ranges(ranges, Some(Autoscroll::Newest), cx); }); @@ -5887,7 +5889,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend } Arc::new(move |cx: &BlockContext| { - let settings = cx.app_state::(); + let settings = cx.global::(); let theme = &settings.theme.editor; let style = diagnostic_style(diagnostic.severity, is_valid, theme); let font_size = (style.text_scale_factor * settings.buffer_font_size).round(); @@ -6246,7 +6248,7 @@ mod tests { #[gpui::test] fn test_navigation_history(cx: &mut gpui::MutableAppContext) { populate_settings(cx); - use workspace::ItemView; + use workspace::Item; let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default())); let buffer = MultiBuffer::build_simple(&sample_text(30, 5, 'a'), cx); @@ -6265,7 +6267,7 @@ mod tests { editor.select_display_ranges(&[DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)], cx); let nav_entry = nav_history.borrow_mut().pop_backward().unwrap(); editor.navigate(nav_entry.data.unwrap(), cx); - assert_eq!(nav_entry.item_view.id(), cx.view_id()); + assert_eq!(nav_entry.item.id(), cx.view_id()); assert_eq!( editor.selected_display_ranges(cx), &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)] @@ -6291,7 +6293,7 @@ mod tests { ); let nav_entry = nav_history.borrow_mut().pop_backward().unwrap(); editor.navigate(nav_entry.data.unwrap(), cx); - assert_eq!(nav_entry.item_view.id(), cx.view_id()); + assert_eq!(nav_entry.item.id(), cx.view_id()); assert_eq!( editor.selected_display_ranges(cx), &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] @@ -9089,7 +9091,7 @@ mod tests { fn populate_settings(cx: &mut gpui::MutableAppContext) { let settings = Settings::test(cx); - cx.add_app_state(settings); + cx.set_global(settings); } } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index ad8057db4e8421b4729d653d844f4706484b98b8..49d800d619829b4e691095bcc5e588712691c5da 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1490,7 +1490,7 @@ mod tests { #[gpui::test] fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) { - cx.add_app_state(Settings::test(cx)); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); let (window_id, editor) = cx.add_window(Default::default(), |cx| { Editor::new(EditorMode::Full, buffer, None, None, cx) @@ -1512,7 +1512,7 @@ mod tests { #[gpui::test] fn test_layout_with_placeholder_text_and_blocks(cx: &mut gpui::MutableAppContext) { - cx.add_app_state(Settings::test(cx)); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("", cx); let (window_id, editor) = cx.add_window(Default::default(), |cx| { Editor::new(EditorMode::Full, buffer, None, None, cx) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index c9af9f0854cf7beba9254dc02cb76e7053079bfc..6f083f00a91eb1c18504be4e3c3efcd17f8ac866 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1,161 +1,18 @@ -use crate::{Autoscroll, Editor, Event, MultiBuffer, NavigationData, ToOffset, ToPoint as _}; +use crate::{Autoscroll, Editor, Event, 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, ModelHandle, RenderContext, Subscription, Task, View, + ViewContext, ViewHandle, }; use language::{Bias, Buffer, Diagnostic, File as _}; use project::{File, Project, 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, -}; - -pub struct BufferOpener; - -#[derive(Clone)] -pub struct BufferItemHandle(pub ModelHandle); - -#[derive(Clone)] -struct WeakBufferItemHandle(WeakModelHandle); - -#[derive(Clone)] -pub struct MultiBufferItemHandle(pub ModelHandle); - -#[derive(Clone)] -struct WeakMultiBufferItemHandle(WeakModelHandle); - -impl PathOpener for BufferOpener { - fn open( - &self, - project: &mut Project, - project_path: ProjectPath, - cx: &mut ModelContext, - ) -> Option>>> { - let buffer = project.open_buffer(project_path, cx); - let task = cx.spawn(|_, _| async move { - let buffer = buffer.await?; - Ok(Box::new(BufferItemHandle(buffer)) as Box) - }); - Some(task) - } -} - -impl ItemHandle for BufferItemHandle { - fn add_view( - &self, - window_id: usize, - workspace: &Workspace, - nav_history: Rc>, - cx: &mut MutableAppContext, - ) -> Box { - 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 - })) - } - - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) - } - - fn to_any(&self) -> gpui::AnyModelHandle { - self.0.clone().into() - } - - fn downgrade(&self) -> Box { - Box::new(WeakBufferItemHandle(self.0.downgrade())) - } - - fn project_path(&self, cx: &AppContext) -> Option { - 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>, - cx: &mut MutableAppContext, - ) -> Box { - 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 { - Box::new(self.clone()) - } - - fn to_any(&self) -> gpui::AnyModelHandle { - self.0.clone().into() - } - - fn downgrade(&self) -> Box { - Box::new(WeakMultiBufferItemHandle(self.0.downgrade())) - } - - fn project_path(&self, _: &AppContext) -> Option { - None - } - - fn id(&self) -> usize { - self.0.id() - } -} - -impl WeakItemHandle for WeakBufferItemHandle { - fn upgrade(&self, cx: &AppContext) -> Option> { - self.0 - .upgrade(cx) - .map(|buffer| Box::new(BufferItemHandle(buffer)) as Box) - } - - fn id(&self) -> usize { - self.0.id() - } -} - -impl WeakItemHandle for WeakMultiBufferItemHandle { - fn upgrade(&self, cx: &AppContext) -> Option> { - self.0 - .upgrade(cx) - .map(|buffer| Box::new(MultiBufferItemHandle(buffer)) as Box) - } - - fn id(&self) -> usize { - self.0.id() - } -} - -impl ItemView for Editor { - fn item(&self, cx: &AppContext) -> Box { - if let Some(buffer) = self.buffer.read(cx).as_singleton() { - Box::new(BufferItemHandle(buffer)) - } else { - Box::new(MultiBufferItemHandle(self.buffer.clone())) - } - } +use workspace::{Item, ItemHandle, ItemNavHistory, ProjectItem, Settings, StatusItemView}; +impl Item for Editor { fn navigate(&mut self, data: Box, cx: &mut ViewContext) { if let Some(data) = data.downcast_ref::() { let buffer = self.buffer.read(cx).read(cx); @@ -184,15 +41,15 @@ impl ItemView for Editor { }) } - fn clone_on_split( - &self, - nav_history: ItemNavHistory, - cx: &mut ViewContext, - ) -> Option + fn clone_on_split(&self, cx: &mut ViewContext) -> Option where Self: Sized, { - Some(self.clone(nav_history, cx)) + Some(self.clone(cx)) + } + + fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext) { + self.nav_history = Some(history); } fn deactivated(&mut self, cx: &mut ViewContext) { @@ -275,6 +132,18 @@ impl ItemView for Editor { } } +impl ProjectItem for Editor { + type Item = Buffer; + + fn for_project_item( + project: ModelHandle, + buffer: ModelHandle, + cx: &mut ViewContext, + ) -> Self { + Self::for_buffer(buffer, Some(project), cx) + } +} + pub struct CursorPosition { position: Option, selected_count: usize, @@ -322,7 +191,7 @@ impl View for CursorPosition { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { if let Some(position) = self.position { - let theme = &cx.app_state::().theme.workspace.status_bar; + let theme = &cx.global::().theme.workspace.status_bar; let mut text = format!("{},{}", position.row + 1, position.column + 1); if self.selected_count > 0 { write!(text, " ({} selected)", self.selected_count).unwrap(); @@ -337,7 +206,7 @@ impl View for CursorPosition { impl StatusItemView for CursorPosition { fn set_active_pane_item( &mut self, - active_pane_item: Option<&dyn ItemViewHandle>, + active_pane_item: Option<&dyn ItemHandle>, cx: &mut ViewContext, ) { if let Some(editor) = active_pane_item.and_then(|item| item.downcast::()) { @@ -395,7 +264,7 @@ impl View for DiagnosticMessage { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { if let Some(diagnostic) = &self.diagnostic { - let theme = &cx.app_state::().theme.workspace.status_bar; + let theme = &cx.global::().theme.workspace.status_bar; Label::new( diagnostic.message.split('\n').next().unwrap().to_string(), theme.diagnostic_message.clone(), @@ -410,7 +279,7 @@ impl View for DiagnosticMessage { impl StatusItemView for DiagnosticMessage { fn set_active_pane_item( &mut self, - active_pane_item: Option<&dyn ItemViewHandle>, + active_pane_item: Option<&dyn ItemHandle>, cx: &mut ViewContext, ) { if let Some(editor) = active_pane_item.and_then(|item| item.downcast::()) { diff --git a/crates/file_finder/Cargo.toml b/crates/file_finder/Cargo.toml index c5300dbcd9f47279aac127315e2d03f354aaae04..b946ea48fbbf58a7196f56949ececfe23fa1fd75 100644 --- a/crates/file_finder/Cargo.toml +++ b/crates/file_finder/Cargo.toml @@ -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" diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 4340cd5a1b7bb30bf05e40c5e0bf6ab1de88a6b5..ca41eb74a11cb2d08103c22e90eebff3ccef7d53 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -67,7 +67,7 @@ impl View for FileFinder { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let settings = cx.app_state::(); + let settings = cx.global::(); Align::new( ConstrainedBox::new( Container::new( @@ -106,7 +106,7 @@ impl View for FileFinder { impl FileFinder { fn render_matches(&self, cx: &AppContext) -> ElementBox { if self.matches.is_empty() { - let settings = cx.app_state::(); + let settings = cx.global::(); return Container::new( Label::new( "No matches".into(), @@ -142,7 +142,7 @@ impl FileFinder { fn render_match(&self, path_match: &PathMatch, index: usize, cx: &AppContext) -> ElementBox { let selected_index = self.selected_index(); - let settings = cx.app_state::(); + let settings = cx.global::(); let style = if index == selected_index { &settings.theme.selector.active_item } else { @@ -407,16 +407,21 @@ 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) { - let mut path_openers = Vec::new(); cx.update(|cx| { super::init(cx); - editor::init(cx, &mut path_openers); + editor::init(cx); }); - let mut params = cx.update(WorkspaceParams::test); - params.path_openers = Arc::from(path_openers); + let params = cx.update(WorkspaceParams::test); params .fs .as_fake() diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 50bae39ae9af73b5b8a82596129deec652dfd776..f2dd4e76b1dc414bd6df9113ae4e61da3524e6f1 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -59,7 +59,8 @@ impl GoToLine { } fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { - if let Some(editor) = workspace.active_item(cx) + if let Some(editor) = workspace + .active_item(cx) .and_then(|active_item| active_item.downcast::()) { workspace.toggle_modal(cx, |cx, _| { @@ -148,7 +149,7 @@ impl View for GoToLine { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.app_state::().theme.selector; + let theme = &cx.global::().theme.selector; let label = format!( "{},{} of {} lines", diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index a03e6e54ae555ca964f2d820fd2df2050874e8a8..5488d31416368a19f91f88c1d3bc51e2e7a6c264 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -595,6 +595,14 @@ impl AsyncAppContext { self.update(|cx| cx.add_model(build_model)) } + pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle + where + T: View, + F: FnOnce(&mut ViewContext) -> T, + { + self.update(|cx| cx.add_view(window_id, build_view)) + } + pub fn platform(&self) -> Arc { self.0.borrow().platform() } @@ -791,7 +799,7 @@ impl MutableAppContext { models: Default::default(), views: Default::default(), windows: Default::default(), - app_states: Default::default(), + globals: Default::default(), element_states: Default::default(), ref_counts: Arc::new(Mutex::new(ref_counts)), background, @@ -1356,24 +1364,48 @@ impl MutableAppContext { Ok(pending) } - pub fn add_app_state(&mut self, state: T) { + pub fn default_global(&mut self) -> &T { self.cx - .app_states - .insert(TypeId::of::(), Box::new(state)); + .globals + .entry(TypeId::of::()) + .or_insert_with(|| Box::new(T::default())) + .downcast_ref() + .unwrap() } - pub fn update_app_state(&mut self, update: F) -> U + pub fn set_global(&mut self, state: T) { + self.cx.globals.insert(TypeId::of::(), Box::new(state)); + } + + pub fn update_default_global(&mut self, update: F) -> U where + T: 'static + Default, F: FnOnce(&mut T, &mut MutableAppContext) -> U, { let type_id = TypeId::of::(); let mut state = self .cx - .app_states + .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.app_states.insert(type_id, state); + self.cx.globals.insert(type_id, state); + result + } + + pub fn update_global(&mut self, update: F) -> U + where + T: 'static, + F: FnOnce(&mut T, &mut MutableAppContext) -> U, + { + let type_id = TypeId::of::(); + 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 } @@ -2046,7 +2078,7 @@ pub struct AppContext { models: HashMap>, views: HashMap<(usize, usize), Box>, windows: HashMap, - app_states: HashMap>, + globals: HashMap>, element_states: HashMap>, background: Arc, ref_counts: Arc>, @@ -2079,8 +2111,8 @@ impl AppContext { &self.platform } - pub fn app_state(&self) -> &T { - self.app_states + pub fn global(&self) -> &T { + self.globals .get(&TypeId::of::()) .expect("no app state has been added for this type") .downcast_ref() @@ -3459,6 +3491,18 @@ impl PartialEq for ViewHandle { } } +impl PartialEq> for ViewHandle { + fn eq(&self, other: &WeakViewHandle) -> bool { + self.window_id == other.window_id && self.view_id == other.view_id + } +} + +impl PartialEq> for WeakViewHandle { + fn eq(&self, other: &ViewHandle) -> bool { + self.window_id == other.window_id && self.view_id == other.view_id + } +} + impl Eq for ViewHandle {} impl Debug for ViewHandle { @@ -3697,6 +3741,10 @@ impl AnyModelHandle { pub fn is(&self) -> bool { self.model_type == TypeId::of::() } + + pub fn model_type(&self) -> TypeId { + self.model_type + } } impl From> for AnyModelHandle { diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 2b4bafa479de3e8b70ac1ded33a7a1f9d4454060..fd4c8ff60b5d660d199e888596cd70649ae478a8 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -69,7 +69,7 @@ impl View for OutlineView { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let settings = cx.app_state::(); + let settings = cx.global::(); Flex::new(Axis::Vertical) .with_child( @@ -124,9 +124,12 @@ impl OutlineView { .active_item(cx) .and_then(|item| item.downcast::()) { - let buffer = editor.read(cx).buffer().read(cx).read(cx).outline(Some( - cx.app_state::().theme.editor.syntax.as_ref(), - )); + let buffer = editor + .read(cx) + .buffer() + .read(cx) + .read(cx) + .outline(Some(cx.global::().theme.editor.syntax.as_ref())); if let Some(outline) = buffer { workspace.toggle_modal(cx, |cx, _| { let view = cx.add_view(|cx| OutlineView::new(outline, editor, cx)); @@ -288,7 +291,7 @@ impl OutlineView { fn render_matches(&self, cx: &AppContext) -> ElementBox { if self.matches.is_empty() { - let settings = cx.app_state::(); + let settings = cx.global::(); return Container::new( Label::new( "No matches".into(), @@ -330,7 +333,7 @@ impl OutlineView { index: usize, cx: &AppContext, ) -> ElementBox { - let settings = cx.app_state::(); + let settings = cx.global::(); let style = if index == self.selected_match_index { &settings.theme.selector.active_item } else { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f5af7fc70ea0c0c89ae707ea3b8cde487c686038..317cf1ba028d19b920d04c4a70094adddb932b97 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -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}, @@ -39,7 +39,7 @@ use std::{ path::{Component, Path, PathBuf}, rc::Rc, sync::{ - atomic::{AtomicBool, AtomicUsize}, + atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}, Arc, }, time::Instant, @@ -49,9 +49,13 @@ use util::{post_inc, ResultExt, TryFutureExt as _}; pub use fs::*; pub use worktree::*; +pub trait Item: Entity { + fn entry_id(&self, cx: &AppContext) -> Option; +} + pub struct Project { worktrees: Vec, - active_entry: Option, + active_entry: Option, languages: Arc, language_servers: HashMap<(WorktreeId, Arc), Arc>, started_language_servers: HashMap<(WorktreeId, Arc), Task>>>, @@ -114,7 +118,7 @@ pub struct Collaborator { #[derive(Clone, Debug, PartialEq)] pub enum Event { - ActiveEntryChanged(Option), + ActiveEntryChanged(Option), WorktreeRemoved(WorktreeId), DiskBasedDiagnosticsStarted, DiskBasedDiagnosticsUpdated, @@ -226,10 +230,25 @@ impl DiagnosticSummary { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct ProjectEntry { - pub worktree_id: WorktreeId, - pub entry_id: usize, +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct ProjectEntryId(usize); + +impl ProjectEntryId { + pub fn new(counter: &AtomicUsize) -> Self { + Self(counter.fetch_add(1, SeqCst)) + } + + pub fn from_proto(id: u64) -> Self { + Self(id as usize) + } + + pub fn to_proto(&self) -> u64 { + self.0 as u64 + } + + pub fn to_usize(&self) -> usize { + self.0 + } } impl Project { @@ -623,6 +642,24 @@ impl Project { .find(|worktree| worktree.read(cx).id() == id) } + pub fn worktree_for_entry( + &self, + entry_id: ProjectEntryId, + cx: &AppContext, + ) -> Option> { + self.worktrees(cx) + .find(|worktree| worktree.read(cx).contains_entry(entry_id)) + } + + pub fn worktree_id_for_entry( + &self, + entry_id: ProjectEntryId, + cx: &AppContext, + ) -> Option { + self.worktree_for_entry(entry_id, cx) + .map(|worktree| worktree.read(cx).id()) + } + pub fn share(&self, cx: &mut ModelContext) -> Task> { let rpc = self.client.clone(); cx.spawn(|this, mut cx| async move { @@ -785,6 +822,23 @@ impl Project { Ok(buffer) } + pub fn open_path( + &mut self, + path: impl Into, + cx: &mut ModelContext, + ) -> Task> { + 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, @@ -3163,10 +3217,7 @@ impl Project { let new_active_entry = entry.and_then(|project_path| { let worktree = self.worktree_for_id(project_path.worktree_id, cx)?; let entry = worktree.read(cx).entry_for_path(project_path.path)?; - Some(ProjectEntry { - worktree_id: project_path.worktree_id, - entry_id: entry.id, - }) + Some(entry.id) }); if new_active_entry != self.active_entry { self.active_entry = new_active_entry; @@ -3217,10 +3268,25 @@ impl Project { } } - pub fn active_entry(&self) -> Option { + pub fn active_entry(&self) -> Option { self.active_entry } + pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option { + self.worktree_for_id(path.worktree_id, cx)? + .read(cx) + .entry_for_path(&path.path) + .map(|entry| entry.id) + } + + pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option { + let worktree = self.worktree_for_entry(entry_id, cx)?; + let worktree = worktree.read(cx); + let worktree_id = worktree.id(); + let path = worktree.entry_for_id(entry_id)?.path.clone(); + Some(ProjectPath { worktree_id, path }) + } + // RPC message handlers async fn handle_unshare_project( @@ -4477,6 +4543,12 @@ fn relativize_path(base: &Path, path: &Path) -> PathBuf { components.iter().map(|c| c.as_os_str()).collect() } +impl Item for Buffer { + fn entry_id(&self, cx: &AppContext) -> Option { + File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx)) + } +} + #[cfg(test)] mod tests { use super::{Event, *}; diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 1ef8dd34a0da1d0e11d7e50e7c2b45a248ed08cf..2bc9c3d234c58ceb33ba66a0c6cf3a3b1fc925c7 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1,3 +1,5 @@ +use crate::ProjectEntryId; + use super::{ fs::{self, Fs}, ignore::IgnoreStack, @@ -39,10 +41,7 @@ use std::{ future::Future, ops::{Deref, DerefMut}, path::{Path, PathBuf}, - sync::{ - atomic::{AtomicUsize, Ordering::SeqCst}, - Arc, - }, + sync::{atomic::AtomicUsize, Arc}, time::{Duration, SystemTime}, }; use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap}; @@ -101,7 +100,7 @@ pub struct LocalSnapshot { abs_path: Arc, scan_id: usize, ignores: HashMap, (Arc, usize)>, - removed_entry_ids: HashMap, + removed_entry_ids: HashMap, next_entry_id: Arc, snapshot: Snapshot, } @@ -856,13 +855,16 @@ impl Snapshot { self.id } + pub fn contains_entry(&self, entry_id: ProjectEntryId) -> bool { + self.entries_by_id.get(&entry_id, &()).is_some() + } + pub(crate) fn apply_remote_update(&mut self, update: proto::UpdateWorktree) -> Result<()> { let mut entries_by_path_edits = Vec::new(); let mut entries_by_id_edits = Vec::new(); for entry_id in update.removed_entries { - let entry_id = entry_id as usize; let entry = self - .entry_for_id(entry_id) + .entry_for_id(ProjectEntryId::from_proto(entry_id)) .ok_or_else(|| anyhow!("unknown entry"))?; entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone()))); entries_by_id_edits.push(Edit::Remove(entry.id)); @@ -985,7 +987,7 @@ impl Snapshot { }) } - pub fn entry_for_id(&self, id: usize) -> Option<&Entry> { + pub fn entry_for_id(&self, id: ProjectEntryId) -> Option<&Entry> { let entry = self.entries_by_id.get(&id, &())?; self.entry_for_path(&entry.path) } @@ -1062,7 +1064,7 @@ impl LocalSnapshot { other_entries.next(); } Ordering::Greater => { - removed_entries.push(other_entry.id as u64); + removed_entries.push(other_entry.id.to_proto()); other_entries.next(); } } @@ -1073,7 +1075,7 @@ impl LocalSnapshot { self_entries.next(); } (None, Some(other_entry)) => { - removed_entries.push(other_entry.id as u64); + removed_entries.push(other_entry.id.to_proto()); other_entries.next(); } (None, None) => break, @@ -1326,7 +1328,7 @@ pub struct File { pub worktree: ModelHandle, pub path: Arc, pub mtime: SystemTime, - pub(crate) entry_id: Option, + pub(crate) entry_id: Option, pub(crate) is_local: bool, } @@ -1423,7 +1425,7 @@ impl language::File for File { fn to_proto(&self) -> rpc::proto::File { rpc::proto::File { worktree_id: self.worktree.id() as u64, - entry_id: self.entry_id.map(|entry_id| entry_id as u64), + entry_id: self.entry_id.map(|entry_id| entry_id.to_proto()), path: self.path.to_string_lossy().into(), mtime: Some(self.mtime.into()), } @@ -1490,7 +1492,7 @@ impl File { worktree, path: Path::new(&proto.path).into(), mtime: proto.mtime.ok_or_else(|| anyhow!("no timestamp"))?.into(), - entry_id: proto.entry_id.map(|entry_id| entry_id as usize), + entry_id: proto.entry_id.map(ProjectEntryId::from_proto), is_local: false, }) } @@ -1502,11 +1504,15 @@ impl File { pub fn worktree_id(&self, cx: &AppContext) -> WorktreeId { self.worktree.read(cx).id() } + + pub fn project_entry_id(&self, _: &AppContext) -> Option { + self.entry_id + } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Entry { - pub id: usize, + pub id: ProjectEntryId, pub kind: EntryKind, pub path: Arc, pub inode: u64, @@ -1530,7 +1536,7 @@ impl Entry { root_char_bag: CharBag, ) -> Self { Self { - id: next_entry_id.fetch_add(1, SeqCst), + id: ProjectEntryId::new(next_entry_id), kind: if metadata.is_dir { EntryKind::PendingDir } else { @@ -1620,7 +1626,7 @@ impl sum_tree::Summary for EntrySummary { #[derive(Clone, Debug)] struct PathEntry { - id: usize, + id: ProjectEntryId, path: Arc, is_ignored: bool, scan_id: usize, @@ -1635,7 +1641,7 @@ impl sum_tree::Item for PathEntry { } impl sum_tree::KeyedItem for PathEntry { - type Key = usize; + type Key = ProjectEntryId; fn key(&self) -> Self::Key { self.id @@ -1644,7 +1650,7 @@ impl sum_tree::KeyedItem for PathEntry { #[derive(Clone, Debug, Default)] struct PathEntrySummary { - max_id: usize, + max_id: ProjectEntryId, } impl sum_tree::Summary for PathEntrySummary { @@ -1655,7 +1661,7 @@ impl sum_tree::Summary for PathEntrySummary { } } -impl<'a> sum_tree::Dimension<'a, PathEntrySummary> for usize { +impl<'a> sum_tree::Dimension<'a, PathEntrySummary> for ProjectEntryId { fn add_summary(&mut self, summary: &'a PathEntrySummary, _: &()) { *self = summary.max_id; } @@ -2345,7 +2351,7 @@ impl<'a> Iterator for ChildEntriesIter<'a> { impl<'a> From<&'a Entry> for proto::Entry { fn from(entry: &'a Entry) -> Self { Self { - id: entry.id as u64, + id: entry.id.to_proto(), is_dir: entry.is_dir(), path: entry.path.to_string_lossy().to_string(), inode: entry.inode, @@ -2370,7 +2376,7 @@ impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry { }; let path: Arc = Arc::from(Path::new(&entry.path)); Ok(Entry { - id: entry.id as usize, + id: ProjectEntryId::from_proto(entry.id), kind, path: path.clone(), inode: entry.inode, diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 7f03005b80cc2d8eddab64f0c59a509cf2d6f12b..0dd6b08bacc68bf888e9348636ec0b66669a509c 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -9,7 +9,7 @@ use gpui::{ AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, WeakViewHandle, }; -use project::{Project, ProjectEntry, ProjectPath, Worktree, WorktreeId}; +use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; use std::{ collections::{hash_map, HashMap}, ffi::OsStr, @@ -24,7 +24,7 @@ pub struct ProjectPanel { project: ModelHandle, list: UniformListState, visible_entries: Vec<(WorktreeId, Vec)>, - expanded_dir_ids: HashMap>, + expanded_dir_ids: HashMap>, selection: Option, handle: WeakViewHandle, } @@ -32,7 +32,7 @@ pub struct ProjectPanel { #[derive(Copy, Clone)] struct Selection { worktree_id: WorktreeId, - entry_id: usize, + entry_id: ProjectEntryId, index: usize, } @@ -47,8 +47,8 @@ struct EntryDetails { action!(ExpandSelectedEntry); action!(CollapseSelectedEntry); -action!(ToggleExpanded, ProjectEntry); -action!(Open, ProjectEntry); +action!(ToggleExpanded, ProjectEntryId); +action!(Open, ProjectEntryId); pub fn init(cx: &mut MutableAppContext) { cx.add_action(ProjectPanel::expand_selected_entry); @@ -64,10 +64,7 @@ pub fn init(cx: &mut MutableAppContext) { } pub enum Event { - OpenedEntry { - worktree_id: WorktreeId, - entry_id: usize, - }, + OpenedEntry(ProjectEntryId), } impl ProjectPanel { @@ -78,15 +75,15 @@ impl ProjectPanel { cx.notify(); }) .detach(); - cx.subscribe(&project, |this, _, event, cx| match event { - project::Event::ActiveEntryChanged(Some(ProjectEntry { - worktree_id, - entry_id, - })) => { - this.expand_entry(*worktree_id, *entry_id, cx); - this.update_visible_entries(Some((*worktree_id, *entry_id)), cx); - this.autoscroll(); - cx.notify(); + cx.subscribe(&project, |this, project, event, cx| match event { + project::Event::ActiveEntryChanged(Some(entry_id)) => { + if let Some(worktree_id) = project.read(cx).worktree_id_for_entry(*entry_id, cx) + { + this.expand_entry(worktree_id, *entry_id, cx); + this.update_visible_entries(Some((worktree_id, *entry_id)), cx); + this.autoscroll(); + cx.notify(); + } } project::Event::WorktreeRemoved(id) => { this.expanded_dir_ids.remove(id); @@ -109,16 +106,13 @@ impl ProjectPanel { this }); cx.subscribe(&project_panel, move |workspace, _, event, cx| match event { - &Event::OpenedEntry { - worktree_id, - entry_id, - } => { - if let Some(worktree) = project.read(cx).worktree_for_id(worktree_id, cx) { + &Event::OpenedEntry(entry_id) => { + if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) { if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) { workspace .open_path( ProjectPath { - worktree_id, + worktree_id: worktree.read(cx).id(), path: entry.path.clone(), }, cx, @@ -152,10 +146,7 @@ impl ProjectPanel { } } } else { - let event = Event::OpenedEntry { - worktree_id: worktree.id(), - entry_id: entry.id, - }; + let event = Event::OpenedEntry(entry.id); cx.emit(event); } } @@ -193,22 +184,20 @@ impl ProjectPanel { } fn toggle_expanded(&mut self, action: &ToggleExpanded, cx: &mut ViewContext) { - let ProjectEntry { - worktree_id, - entry_id, - } = action.0; - - if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) { - match expanded_dir_ids.binary_search(&entry_id) { - Ok(ix) => { - expanded_dir_ids.remove(ix); - } - Err(ix) => { - expanded_dir_ids.insert(ix, entry_id); + let entry_id = action.0; + if let Some(worktree_id) = self.project.read(cx).worktree_id_for_entry(entry_id, cx) { + if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) { + match expanded_dir_ids.binary_search(&entry_id) { + Ok(ix) => { + expanded_dir_ids.remove(ix); + } + Err(ix) => { + expanded_dir_ids.insert(ix, entry_id); + } } + self.update_visible_entries(Some((worktree_id, entry_id)), cx); + cx.focus_self(); } - self.update_visible_entries(Some((worktree_id, entry_id)), cx); - cx.focus_self(); } } @@ -229,10 +218,7 @@ impl ProjectPanel { } fn open_entry(&mut self, action: &Open, cx: &mut ViewContext) { - cx.emit(Event::OpenedEntry { - worktree_id: action.0.worktree_id, - entry_id: action.0.entry_id, - }); + cx.emit(Event::OpenedEntry(action.0)); } fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { @@ -313,7 +299,7 @@ impl ProjectPanel { fn update_visible_entries( &mut self, - new_selected_entry: Option<(WorktreeId, usize)>, + new_selected_entry: Option<(WorktreeId, ProjectEntryId)>, cx: &mut ViewContext, ) { let worktrees = self @@ -379,7 +365,7 @@ impl ProjectPanel { fn expand_entry( &mut self, worktree_id: WorktreeId, - entry_id: usize, + entry_id: ProjectEntryId, cx: &mut ViewContext, ) { let project = self.project.read(cx); @@ -411,7 +397,7 @@ impl ProjectPanel { &self, range: Range, cx: &mut ViewContext, - mut callback: impl FnMut(ProjectEntry, EntryDetails, &mut ViewContext), + mut callback: impl FnMut(ProjectEntryId, EntryDetails, &mut ViewContext), ) { let mut ix = 0; for (worktree_id, visible_worktree_entries) in &self.visible_entries { @@ -450,11 +436,7 @@ impl ProjectPanel { e.worktree_id == snapshot.id() && e.entry_id == entry.id }), }; - let entry = ProjectEntry { - worktree_id: snapshot.id(), - entry_id: entry.id, - }; - callback(entry, details, cx); + callback(entry.id, details, cx); } } } @@ -463,13 +445,13 @@ impl ProjectPanel { } fn render_entry( - entry: ProjectEntry, + entry_id: ProjectEntryId, details: EntryDetails, theme: &theme::ProjectPanel, cx: &mut ViewContext, ) -> ElementBox { let is_dir = details.is_dir; - MouseEventHandler::new::(entry.entry_id, cx, |state, _| { + MouseEventHandler::new::(entry_id.to_usize(), cx, |state, _| { let style = match (details.is_selected, state.hovered) { (false, false) => &theme.entry, (false, true) => &theme.hovered_entry, @@ -519,9 +501,9 @@ impl ProjectPanel { }) .on_click(move |cx| { if is_dir { - cx.dispatch_action(ToggleExpanded(entry)) + cx.dispatch_action(ToggleExpanded(entry_id)) } else { - cx.dispatch_action(Open(entry)) + cx.dispatch_action(Open(entry_id)) } }) .with_cursor_style(CursorStyle::PointingHand) @@ -535,7 +517,7 @@ impl View for ProjectPanel { } fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { - let theme = &cx.app_state::().theme.project_panel; + let theme = &cx.global::().theme.project_panel; let mut container_style = theme.container; let padding = std::mem::take(&mut container_style.padding); let handle = self.handle.clone(); @@ -546,7 +528,7 @@ impl View for ProjectPanel { .map(|(_, worktree_entries)| worktree_entries.len()) .sum(), move |range, items, cx| { - let theme = cx.app_state::().theme.clone(); + let theme = cx.global::().theme.clone(); let this = handle.upgrade(cx).unwrap(); this.update(cx.app, |this, cx| { this.for_each_visible_entry(range.clone(), cx, |entry, details, cx| { @@ -830,13 +812,7 @@ mod tests { let worktree = worktree.read(cx); if let Ok(relative_path) = path.strip_prefix(worktree.root_name()) { let entry_id = worktree.entry_for_path(relative_path).unwrap().id; - panel.toggle_expanded( - &ToggleExpanded(ProjectEntry { - worktree_id: worktree.id(), - entry_id, - }), - cx, - ); + panel.toggle_expanded(&ToggleExpanded(entry_id), cx); return; } } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index bfd204671f6994a07b5ab1717afc684b9659c9c8..27e125a59205d39c9b3a647103ea7ada1fceb8a8 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -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::{ @@ -70,7 +69,7 @@ impl View for ProjectSymbolsView { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let settings = cx.app_state::(); + let settings = cx.global::(); Flex::new(Axis::Vertical) .with_child( Container::new(ChildView::new(&self.query_editor).boxed()) @@ -234,7 +233,7 @@ impl ProjectSymbolsView { fn render_matches(&self, cx: &AppContext) -> ElementBox { if self.matches.is_empty() { - let settings = cx.app_state::(); + let settings = cx.global::(); return Container::new( Label::new( "No matches".into(), @@ -277,7 +276,7 @@ impl ProjectSymbolsView { show_worktree_root_name: bool, cx: &AppContext, ) -> ElementBox { - let settings = cx.app_state::(); + let settings = cx.global::(); let style = if index == self.selected_match_index { &settings.theme.selector.active_item } else { @@ -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::() - .unwrap(); + + let editor = workspace.open_project_item::(buffer, cx); editor.update(cx, |editor, cx| { editor.select_ranges( [position..position], diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 24b438c7e465deef2ff6963fa1faa56af790a6dd..8eae666c454ea2870301f4238cfd3e137da3fd9a 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -8,7 +8,7 @@ use gpui::{ use language::OffsetRangeExt; use project::search::SearchQuery; use std::ops::Range; -use workspace::{ItemViewHandle, Pane, Settings, Toolbar, Workspace}; +use workspace::{ItemHandle, Pane, Settings, Toolbar, Workspace}; action!(Deploy, bool); action!(Dismiss); @@ -66,7 +66,7 @@ impl View for SearchBar { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = cx.app_state::().theme.clone(); + let theme = cx.global::().theme.clone(); let editor_container = if self.query_contains_error { theme.search.invalid_editor } else { @@ -126,7 +126,7 @@ impl View for SearchBar { impl Toolbar for SearchBar { fn active_item_changed( &mut self, - item: Option>, + item: Option>, cx: &mut ViewContext, ) -> bool { self.active_editor_subscription.take(); @@ -197,7 +197,7 @@ impl SearchBar { ) -> ElementBox { let is_active = self.is_search_option_enabled(search_option); MouseEventHandler::new::(search_option as usize, cx, |state, cx| { - let theme = &cx.app_state::().theme.search; + let theme = &cx.global::().theme.search; let style = match (is_active, state.hovered) { (false, false) => &theme.option_button, (false, true) => &theme.hovered_option_button, @@ -222,7 +222,7 @@ impl SearchBar { ) -> ElementBox { enum NavButton {} MouseEventHandler::new::(direction as usize, cx, |state, cx| { - let theme = &cx.app_state::().theme.search; + let theme = &cx.global::().theme.search; let style = if state.hovered { &theme.hovered_option_button } else { @@ -475,7 +475,7 @@ impl SearchBar { } } - let theme = &cx.app_state::().theme.search; + let theme = &cx.global::().theme.search; editor.highlight_background::( ranges, theme.match_background, @@ -510,8 +510,9 @@ impl SearchBar { #[cfg(test)] mod tests { use super::*; - use editor::{DisplayPoint, Editor, MultiBuffer}; + use editor::{DisplayPoint, Editor}; use gpui::{color::Color, TestAppContext}; + use language::Buffer; use std::sync::Arc; use unindent::Unindent as _; @@ -521,11 +522,12 @@ mod tests { let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default()); theme.search.match_background = Color::red(); let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap(); - cx.update(|cx| cx.add_app_state(settings)); + cx.update(|cx| cx.set_global(settings)); - let buffer = cx.update(|cx| { - MultiBuffer::build_simple( - &r#" + let buffer = cx.add_model(|cx| { + Buffer::new( + 0, + r#" A regular expression (shortened as regex or regexp;[1] also referred to as rational expression[2][3]) is a sequence of characters that specifies a search pattern in text. Usually such patterns are used by string-searching algorithms diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index b09c88a4a0ff8b020dbf839684cbbe4c1f3cf88b..ef8ff3611ae675df8f7f86969b3c8adeaa9161cc 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -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::{Item, ItemNavHistory, Settings, Workspace}; action!(Deploy); action!(Search); @@ -26,10 +26,10 @@ action!(ToggleFocus); const MAX_TAB_TITLE_LEN: usize = 24; #[derive(Default)] -struct ActiveSearches(HashMap, WeakModelHandle>); +struct ActiveSearches(HashMap, WeakViewHandle>); pub fn init(cx: &mut MutableAppContext) { - cx.add_app_state(ActiveSearches::default()); + cx.set_global(ActiveSearches::default()); cx.add_bindings([ Binding::new("cmd-shift-F", ToggleFocus, Some("ProjectSearchView")), Binding::new("cmd-f", ToggleFocus, Some("ProjectSearchView")), @@ -139,23 +139,6 @@ impl ProjectSearch { } } -impl Item for ProjectSearch { - type View = ProjectSearchView; - - fn build_view( - model: ModelHandle, - _: &Workspace, - nav_history: ItemNavHistory, - cx: &mut gpui::ViewContext, - ) -> Self::View { - ProjectSearchView::new(model, Some(nav_history), cx) - } - - fn project_path(&self) -> Option { - None - } -} - enum ViewEvent { UpdateTab, } @@ -172,7 +155,7 @@ impl View for ProjectSearchView { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { let model = &self.model.read(cx); let results = if model.match_ranges.is_empty() { - let theme = &cx.app_state::().theme; + let theme = &cx.global::().theme; let text = if self.query_editor.read(cx).text(cx).is_empty() { "" } else if model.pending_search.is_some() { @@ -199,11 +182,11 @@ impl View for ProjectSearchView { } fn on_focus(&mut self, cx: &mut ViewContext) { - cx.update_app_state(|state: &mut ActiveSearches, cx| { - state.0.insert( - self.model.read(cx).project.downgrade(), - self.model.downgrade(), - ) + let handle = cx.weak_handle(); + cx.update_global(|state: &mut ActiveSearches, cx| { + state + .0 + .insert(self.model.read(cx).project.downgrade(), handle) }); if self.model.read(cx).match_ranges.is_empty() { @@ -214,7 +197,7 @@ impl View for ProjectSearchView { } } -impl ItemView for ProjectSearchView { +impl Item for ProjectSearchView { fn act_as_type( &self, type_id: TypeId, @@ -235,12 +218,8 @@ impl ItemView for ProjectSearchView { .update(cx, |editor, cx| editor.deactivated(cx)); } - fn item(&self, _: &gpui::AppContext) -> Box { - Box::new(self.model.clone()) - } - fn tab_content(&self, tab_theme: &theme::Tab, cx: &gpui::AppContext) -> ElementBox { - let settings = cx.app_state::(); + let settings = cx.global::(); let search_theme = &settings.theme.search; Flex::row() .with_child( @@ -305,16 +284,18 @@ impl ItemView for ProjectSearchView { unreachable!("save_as should not have been called") } - fn clone_on_split( - &self, - nav_history: ItemNavHistory, - cx: &mut ViewContext, - ) -> Option + fn clone_on_split(&self, cx: &mut ViewContext) -> Option 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.results_editor.update(cx, |editor, _| { + editor.set_nav_history(Some(nav_history)); + }); } fn navigate(&mut self, data: Box, cx: &mut ViewContext) { @@ -328,11 +309,7 @@ impl ItemView for ProjectSearchView { } impl ProjectSearchView { - fn new( - model: ModelHandle, - nav_history: Option, - cx: &mut ViewContext, - ) -> Self { + fn new(model: ModelHandle, cx: &mut ViewContext) -> Self { let project; let excerpts; let mut query_text = String::new(); @@ -362,9 +339,8 @@ impl ProjectSearchView { }); let results_editor = cx.add_view(|cx| { - let mut editor = Editor::for_buffer(excerpts, Some(project), cx); + let mut editor = Editor::for_multibuffer(excerpts, Some(project), cx); editor.set_searchable(false); - editor.set_nav_history(nav_history); editor }); cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab)) @@ -394,28 +370,31 @@ impl ProjectSearchView { // If no search exists in the workspace, create a new one. fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext) { // Clean up entries for dropped projects - cx.update_app_state(|state: &mut ActiveSearches, cx| { + cx.update_global(|state: &mut ActiveSearches, cx| { state.0.retain(|project, _| project.is_upgradable(cx)) }); let active_search = cx - .app_state::() + .global::() .0 .get(&workspace.project().downgrade()); let existing = active_search .and_then(|active_search| { workspace - .items_of_type::(cx) + .items_of_type::(cx) .find(|search| search == active_search) }) - .or_else(|| workspace.item_of_type::(cx)); + .or_else(|| workspace.item_of_type::(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.add_item( + Box::new(cx.add_view(|cx| ProjectSearchView::new(model, cx))), + cx, + ); } } @@ -450,7 +429,10 @@ impl ProjectSearchView { model.search(new_query, cx); model }); - workspace.open_item(model, cx); + workspace.add_item( + Box::new(cx.add_view(|cx| ProjectSearchView::new(model, cx))), + cx, + ); } } } @@ -552,7 +534,7 @@ impl ProjectSearchView { if reset_selections { editor.select_ranges(match_ranges.first().cloned(), Some(Autoscroll::Fit), cx); } - let theme = &cx.app_state::().theme.search; + let theme = &cx.global::().theme.search; editor.highlight_background::(match_ranges, theme.match_background, cx); }); if self.query_editor.is_focused(cx) { @@ -578,7 +560,7 @@ impl ProjectSearchView { } fn render_query_editor(&self, cx: &mut RenderContext) -> ElementBox { - let theme = cx.app_state::().theme.clone(); + let theme = cx.global::().theme.clone(); let editor_container = if self.query_contains_error { theme.search.invalid_editor } else { @@ -642,7 +624,7 @@ impl ProjectSearchView { ) -> ElementBox { let is_active = self.is_option_enabled(option); MouseEventHandler::new::(option as usize, cx, |state, cx| { - let theme = &cx.app_state::().theme.search; + let theme = &cx.global::().theme.search; let style = match (is_active, state.hovered) { (false, false) => &theme.option_button, (false, true) => &theme.hovered_option_button, @@ -675,7 +657,7 @@ impl ProjectSearchView { ) -> ElementBox { enum NavButton {} MouseEventHandler::new::(direction as usize, cx, |state, cx| { - let theme = &cx.app_state::().theme.search; + let theme = &cx.global::().theme.search; let style = if state.hovered { &theme.hovered_option_button } else { @@ -707,7 +689,7 @@ mod tests { let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default()); theme.search.match_background = Color::red(); let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap(); - cx.update(|cx| cx.add_app_state(settings)); + cx.update(|cx| cx.set_global(settings)); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -732,7 +714,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| { diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index ed45c2d5d6b80d46c9d0a7455ff3621506bf14c1..7e9bb38021809685565033e5c007e07f4c2cf97a 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1013,8 +1013,8 @@ mod tests { }; use collections::BTreeMap; use editor::{ - self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Input, MultiBuffer, - Redo, Rename, ToOffset, ToggleCodeActions, Undo, + self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Input, Redo, Rename, + ToOffset, ToggleCodeActions, Undo, }; use gpui::{executor, ModelHandle, TestAppContext}; use language::{ @@ -1140,10 +1140,7 @@ mod tests { .update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx)) .await .unwrap(); - let buffer_b = cx_b.add_model(|cx| MultiBuffer::singleton(buffer_b, cx)); - buffer_b.read_with(cx_b, |buf, cx| { - assert_eq!(buf.read(cx).text(), "b-contents") - }); + buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "b-contents")); project_a.read_with(cx_a, |project, cx| { assert!(project.has_open_buffer((worktree_id, "b.txt"), cx)) }); @@ -2176,11 +2173,7 @@ mod tests { .unwrap(); let (window_b, _) = cx_b.add_window(|_| EmptyView); let editor_b = cx_b.add_view(window_b, |cx| { - Editor::for_buffer( - cx.add_model(|cx| MultiBuffer::singleton(buffer_b.clone(), cx)), - Some(project_b.clone()), - cx, - ) + Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx) }); let mut fake_language_server = fake_language_servers.next().await.unwrap(); @@ -3159,8 +3152,7 @@ mod tests { cx_a.foreground().forbid_parking(); let mut lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); - let mut path_openers_b = Vec::new(); - cx_b.update(|cx| editor::init(cx, &mut path_openers_b)); + cx_b.update(|cx| editor::init(cx)); // Set up a fake language server. let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); @@ -3229,7 +3221,6 @@ mod tests { params.client = client_b.client.clone(); params.user_store = client_b.user_store.clone(); params.project = project_b; - params.path_openers = path_openers_b.into(); let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(¶ms, cx)); let editor_b = workspace_b @@ -3395,8 +3386,7 @@ mod tests { cx_a.foreground().forbid_parking(); let mut lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); - let mut path_openers_b = Vec::new(); - cx_b.update(|cx| editor::init(cx, &mut path_openers_b)); + cx_b.update(|cx| editor::init(cx)); // Set up a fake language server. let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); @@ -3465,7 +3455,6 @@ mod tests { params.client = client_b.client.clone(); params.user_store = client_b.user_store.clone(); params.project = project_b; - params.path_openers = path_openers_b.into(); let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(¶ms, cx)); let editor_b = workspace_b @@ -4418,7 +4407,7 @@ mod tests { async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient { cx.update(|cx| { let settings = Settings::test(cx); - cx.add_app_state(settings); + cx.set_global(settings); }); let http = FakeHttpClient::with_404_response(); diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index c3199bc20bf24153de96ef98f24da8b97d6874ed..f879940f219f266a1f3108e0f570948cb9ef584e 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -54,7 +54,7 @@ impl ThemeSelector { cx.subscribe(&query_editor, Self::on_query_editor_event) .detach(); - let original_theme = cx.app_state::().theme.clone(); + let original_theme = cx.global::().theme.clone(); let mut this = Self { themes: registry, @@ -82,7 +82,7 @@ impl ThemeSelector { } fn reload(_: &mut Workspace, action: &Reload, cx: &mut ViewContext) { - let current_theme_name = cx.app_state::().theme.name.clone(); + let current_theme_name = cx.global::().theme.name.clone(); action.0.clear(); match action.0.get(¤t_theme_name) { Ok(theme) => { @@ -206,7 +206,7 @@ impl ThemeSelector { match event { editor::Event::Edited => { self.update_matches(cx); - self.select_if_matching(&cx.app_state::().theme.name); + self.select_if_matching(&cx.global::().theme.name); self.show_selected_theme(cx); } editor::Event::Blurred => cx.emit(Event::Dismissed), @@ -216,7 +216,7 @@ impl ThemeSelector { fn render_matches(&self, cx: &mut RenderContext) -> ElementBox { if self.matches.is_empty() { - let settings = cx.app_state::(); + let settings = cx.global::(); return Container::new( Label::new( "No matches".into(), @@ -251,7 +251,7 @@ impl ThemeSelector { } fn render_match(&self, theme_match: &StringMatch, index: usize, cx: &AppContext) -> ElementBox { - let settings = cx.app_state::(); + let settings = cx.global::(); let theme = &settings.theme; let container = Container::new( @@ -276,7 +276,7 @@ impl ThemeSelector { } fn set_theme(theme: Arc, cx: &mut MutableAppContext) { - cx.update_app_state::(|settings, cx| { + cx.update_global::(|settings, cx| { settings.theme = theme; cx.refresh_windows(); }); @@ -299,7 +299,7 @@ impl View for ThemeSelector { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = cx.app_state::().theme.clone(); + let theme = cx.global::().theme.clone(); Align::new( ConstrainedBox::new( Container::new( diff --git a/crates/workspace/src/lsp_status.rs b/crates/workspace/src/lsp_status.rs index db160ab06fed41d4974d7fd83721f58acb415a19..a12f81857f1d1b2ed4d44aa01c6283da67f2c8c2 100644 --- a/crates/workspace/src/lsp_status.rs +++ b/crates/workspace/src/lsp_status.rs @@ -1,4 +1,4 @@ -use crate::{ItemViewHandle, Settings, StatusItemView}; +use crate::{ItemHandle, Settings, StatusItemView}; use futures::StreamExt; use gpui::AppContext; use gpui::{ @@ -116,7 +116,7 @@ impl View for LspStatus { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.app_state::().theme; + let theme = &cx.global::().theme; let mut pending_work = self.pending_language_server_work(cx); if let Some((lang_server_name, progress_token, progress)) = pending_work.next() { @@ -166,7 +166,7 @@ impl View for LspStatus { } else if !self.failed.is_empty() { drop(pending_work); MouseEventHandler::new::(0, cx, |_, cx| { - let theme = &cx.app_state::().theme; + let theme = &cx.global::().theme; Label::new( format!( "Failed to download {} language server{}. Click to dismiss.", @@ -187,5 +187,5 @@ impl View for LspStatus { } impl StatusItemView for LspStatus { - fn set_active_pane_item(&mut self, _: Option<&dyn ItemViewHandle>, _: &mut ViewContext) {} + fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext) {} } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index c54a1b050b341cb75f1768269579f25c43fa64cb..57a74b52ef405bcbe0457536caa852187cfe8c2c 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1,5 +1,5 @@ -use super::{ItemViewHandle, SplitDirection}; -use crate::{ItemHandle, ItemView, Settings, WeakItemViewHandle, Workspace}; +use super::{ItemHandle, SplitDirection}; +use crate::{Item, Settings, WeakItemHandle, 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::{ProjectEntryId, ProjectPath}; use std::{ any::{Any, TypeId}, cell::RefCell, @@ -97,7 +97,7 @@ pub enum Event { } pub struct Pane { - item_views: Vec<(usize, Box)>, + items: Vec<(Option, Box)>, active_item_index: usize, nav_history: Rc>, toolbars: HashMap>, @@ -108,7 +108,7 @@ pub struct Pane { pub trait Toolbar: View { fn active_item_changed( &mut self, - item: Option>, + item: Option>, cx: &mut ViewContext, ) -> bool; fn on_dismiss(&mut self, cx: &mut ViewContext); @@ -117,7 +117,7 @@ pub trait Toolbar: View { trait ToolbarHandle { fn active_item_changed( &self, - item: Option>, + item: Option>, cx: &mut MutableAppContext, ) -> bool; fn on_dismiss(&self, cx: &mut MutableAppContext); @@ -126,7 +126,7 @@ trait ToolbarHandle { pub struct ItemNavHistory { history: Rc>, - item_view: Rc, + item: Rc, } #[derive(Default)] @@ -152,14 +152,14 @@ impl Default for NavigationMode { } pub struct NavigationEntry { - pub item_view: Rc, + pub item: Rc, pub data: Option>, } impl Pane { pub fn new() -> Self { Self { - item_views: Vec::new(), + items: Vec::new(), active_item_index: 0, nav_history: Default::default(), toolbars: Default::default(), @@ -216,13 +216,13 @@ impl Pane { // If the item is still present in this pane, then activate it. if let Some(index) = entry - .item_view + .item .upgrade(cx) - .and_then(|v| pane.index_for_item_view(v.as_ref())) + .and_then(|v| pane.index_for_item(v.as_ref())) { - if let Some(item_view) = pane.active_item() { + if let Some(item) = pane.active_item() { pane.nav_history.borrow_mut().set_mode(mode); - item_view.deactivated(cx); + item.deactivated(cx); pane.nav_history .borrow_mut() .set_mode(NavigationMode::Normal); @@ -242,7 +242,7 @@ impl Pane { pane.nav_history .borrow_mut() .paths_by_item - .get(&entry.item_view.id()) + .get(&entry.item.id()) .cloned() .map(|project_path| (project_path, entry)) } @@ -253,18 +253,17 @@ impl Pane { let pane = pane.downgrade(); let task = workspace.load_path(project_path, cx); cx.spawn(|workspace, mut cx| async move { - let item = task.await; + let task = task.await; if let Some(pane) = pane.upgrade(&cx) { - if let Some(item) = item.log_err() { - workspace.update(&mut cx, |workspace, cx| { - pane.update(cx, |p, _| p.nav_history.borrow_mut().set_mode(mode)); - let item_view = workspace.open_item_in_pane(item, &pane, cx); - pane.update(cx, |p, _| { - p.nav_history.borrow_mut().set_mode(NavigationMode::Normal) - }); - + if let Some((project_entry_id, build_item)) = task.log_err() { + pane.update(&mut cx, |pane, cx| { + pane.nav_history.borrow_mut().set_mode(mode); + let item = pane.open_item(project_entry_id, cx, build_item); + pane.nav_history + .borrow_mut() + .set_mode(NavigationMode::Normal); if let Some(data) = entry.data { - item_view.navigate(data, cx); + item.navigate(data, cx); } }); } else { @@ -281,76 +280,80 @@ impl Pane { } } - pub fn open_item( + pub fn open_item( &mut self, - item_handle: T, - workspace: &Workspace, + project_entry_id: ProjectEntryId, cx: &mut ViewContext, - ) -> Box - where - T: 'static + ItemHandle, - { - for (ix, (item_id, item_view)) in self.item_views.iter().enumerate() { - if *item_id == item_handle.id() { - let item_view = item_view.boxed_clone(); + build_item: impl FnOnce(&mut MutableAppContext) -> Box, + ) -> Box { + for (ix, (existing_entry_id, item)) in self.items.iter().enumerate() { + if *existing_entry_id == Some(project_entry_id) { + let item = item.boxed_clone(); self.activate_item(ix, cx); - return item_view; + return item; } } - 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 + let item = build_item(cx); + self.add_item(Some(project_entry_id), item.boxed_clone(), cx); + item } - pub fn add_item_view( + pub(crate) fn add_item( &mut self, - mut item_view: Box, + project_entry_id: Option, + mut item: Box, cx: &mut ViewContext, ) { - 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)); + item.set_nav_history(self.nav_history.clone(), cx); + item.added_to_pane(cx); + let item_idx = cmp::min(self.active_item_index + 1, self.items.len()); + self.items.insert(item_idx, (project_entry_id, item)); 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> { - self.item_views.iter().map(|(_, view)| view) + pub fn items(&self) -> impl Iterator> { + self.items.iter().map(|(_, view)| view) } - pub fn active_item(&self) -> Option> { - self.item_views + pub fn active_item(&self) -> Option> { + self.items .get(self.active_item_index) .map(|(_, view)| view.clone()) } - pub fn index_for_item_view(&self, item_view: &dyn ItemViewHandle) -> Option { - self.item_views - .iter() - .position(|(_, i)| i.id() == item_view.id()) + pub fn project_entry_id_for_item(&self, item: &dyn ItemHandle) -> Option { + self.items.iter().find_map(|(entry_id, existing)| { + if existing.id() == item.id() { + *entry_id + } else { + None + } + }) + } + + pub fn item_for_entry(&self, entry_id: ProjectEntryId) -> Option> { + self.items.iter().find_map(|(id, view)| { + if *id == Some(entry_id) { + Some(view.boxed_clone()) + } else { + None + } + }) } pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option { - self.item_views.iter().position(|(id, _)| *id == item.id()) + self.items.iter().position(|(_, i)| i.id() == item.id()) } pub fn activate_item(&mut self, index: usize, cx: &mut ViewContext) { - if index < self.item_views.len() { + if index < self.items.len() { let prev_active_item_ix = mem::replace(&mut self.active_item_index, index); if prev_active_item_ix != self.active_item_index - && prev_active_item_ix < self.item_views.len() + && prev_active_item_ix < self.items.len() { - self.item_views[prev_active_item_ix].1.deactivated(cx); + self.items[prev_active_item_ix].1.deactivated(cx); } self.update_active_toolbar(cx); self.focus_active_item(cx); @@ -363,15 +366,15 @@ impl Pane { let mut index = self.active_item_index; if index > 0 { index -= 1; - } else if self.item_views.len() > 0 { - index = self.item_views.len() - 1; + } else if self.items.len() > 0 { + index = self.items.len() - 1; } self.activate_item(index, cx); } pub fn activate_next_item(&mut self, cx: &mut ViewContext) { let mut index = self.active_item_index; - if index + 1 < self.item_views.len() { + if index + 1 < self.items.len() { index += 1; } else { index = 0; @@ -380,14 +383,14 @@ impl Pane { } pub fn close_active_item(&mut self, cx: &mut ViewContext) { - if !self.item_views.is_empty() { - self.close_item(self.item_views[self.active_item_index].1.id(), cx) + if !self.items.is_empty() { + self.close_item(self.items[self.active_item_index].1.id(), cx) } } pub fn close_inactive_items(&mut self, cx: &mut ViewContext) { - if !self.item_views.is_empty() { - let active_item_id = self.item_views[self.active_item_index].1.id(); + if !self.items.is_empty() { + let active_item_id = self.items[self.active_item_index].1.id(); self.close_items(cx, |id| id != active_item_id); } } @@ -403,10 +406,10 @@ impl Pane { ) { let mut item_ix = 0; let mut new_active_item_index = self.active_item_index; - self.item_views.retain(|(_, item_view)| { - if should_close(item_view.id()) { + self.items.retain(|(_, item)| { + if should_close(item.id()) { if item_ix == self.active_item_index { - item_view.deactivated(cx); + item.deactivated(cx); } if item_ix < self.active_item_index { @@ -414,10 +417,10 @@ impl Pane { } let mut nav_history = self.nav_history.borrow_mut(); - if let Some(path) = item_view.project_path(cx) { - nav_history.paths_by_item.insert(item_view.id(), path); + if let Some(path) = item.project_path(cx) { + nav_history.paths_by_item.insert(item.id(), path); } else { - nav_history.paths_by_item.remove(&item_view.id()); + nav_history.paths_by_item.remove(&item.id()); } item_ix += 1; @@ -428,10 +431,10 @@ impl Pane { } }); - if self.item_views.is_empty() { + if self.items.is_empty() { cx.emit(Event::Remove); } else { - self.active_item_index = cmp::min(new_active_item_index, self.item_views.len() - 1); + self.active_item_index = cmp::min(new_active_item_index, self.items.len() - 1); self.focus_active_item(cx); self.activate(cx); } @@ -500,7 +503,7 @@ impl Pane { } fn update_active_toolbar(&mut self, cx: &mut ViewContext) { - let active_item = self.item_views.get(self.active_item_index); + let active_item = self.items.get(self.active_item_index); for (toolbar_type_id, toolbar) in &self.toolbars { let visible = toolbar.active_item_changed(active_item.map(|i| i.1.clone()), cx); if Some(*toolbar_type_id) == self.active_toolbar_type { @@ -510,12 +513,12 @@ impl Pane { } fn render_tabs(&self, cx: &mut RenderContext) -> ElementBox { - let theme = cx.app_state::().theme.clone(); + let theme = cx.global::().theme.clone(); enum Tabs {} let tabs = MouseEventHandler::new::(0, cx, |mouse_state, cx| { let mut row = Flex::row(); - for (ix, (_, item_view)) in self.item_views.iter().enumerate() { + for (ix, (_, item)) in self.items.iter().enumerate() { let is_active = ix == self.active_item_index; row.add_child({ @@ -524,7 +527,7 @@ impl Pane { } else { theme.workspace.tab.clone() }; - let title = item_view.tab_content(&tab_style, cx); + let title = item.tab_content(&tab_style, cx); let mut style = if is_active { theme.workspace.active_tab.clone() @@ -541,9 +544,9 @@ impl Pane { .with_child( Align::new({ let diameter = 7.0; - let icon_color = if item_view.has_conflict(cx) { + let icon_color = if item.has_conflict(cx) { Some(style.icon_conflict) - } else if item_view.is_dirty(cx) { + } else if item.is_dirty(cx) { Some(style.icon_dirty) } else { None @@ -587,7 +590,7 @@ impl Pane { .with_child( Align::new( ConstrainedBox::new(if mouse_state.hovered { - let item_id = item_view.id(); + let item_id = item.id(); enum TabCloseButton {} let icon = Svg::new("icons/x.svg"); MouseEventHandler::new::( @@ -691,7 +694,7 @@ impl View for Pane { impl ToolbarHandle for ViewHandle { fn active_item_changed( &self, - item: Option>, + item: Option>, cx: &mut MutableAppContext, ) -> bool { self.update(cx, |this, cx| this.active_item_changed(item, cx)) @@ -707,10 +710,10 @@ impl ToolbarHandle for ViewHandle { } impl ItemNavHistory { - pub fn new(history: Rc>, item_view: &ViewHandle) -> Self { + pub fn new(history: Rc>, item: &ViewHandle) -> Self { Self { history, - item_view: Rc::new(item_view.downgrade()), + item: Rc::new(item.downgrade()), } } @@ -719,7 +722,7 @@ impl ItemNavHistory { } pub fn push(&self, data: Option) { - self.history.borrow_mut().push(data, self.item_view.clone()); + self.history.borrow_mut().push(data, self.item.clone()); } } @@ -752,11 +755,7 @@ impl NavHistory { self.mode = mode; } - pub fn push( - &mut self, - data: Option, - item_view: Rc, - ) { + pub fn push(&mut self, data: Option, item: Rc) { match self.mode { NavigationMode::Disabled => {} NavigationMode::Normal => { @@ -764,7 +763,7 @@ impl NavHistory { self.backward_stack.pop_front(); } self.backward_stack.push_back(NavigationEntry { - item_view, + item, data: data.map(|data| Box::new(data) as Box), }); self.forward_stack.clear(); @@ -774,7 +773,7 @@ impl NavHistory { self.forward_stack.pop_front(); } self.forward_stack.push_back(NavigationEntry { - item_view, + item, data: data.map(|data| Box::new(data) as Box), }); } @@ -783,7 +782,7 @@ impl NavHistory { self.backward_stack.pop_front(); } self.backward_stack.push_back(NavigationEntry { - item_view, + item, data: data.map(|data| Box::new(data) as Box), }); } diff --git a/crates/workspace/src/status_bar.rs b/crates/workspace/src/status_bar.rs index c387fbe07064ccc136e839f4d8c07a57b1962d84..4d00591787c9cf88e20ab9a60c705a73250421ee 100644 --- a/crates/workspace/src/status_bar.rs +++ b/crates/workspace/src/status_bar.rs @@ -1,4 +1,4 @@ -use crate::{ItemViewHandle, Pane, Settings}; +use crate::{ItemHandle, Pane, Settings}; use gpui::{ elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, @@ -7,7 +7,7 @@ use gpui::{ pub trait StatusItemView: View { fn set_active_pane_item( &mut self, - active_pane_item: Option<&dyn crate::ItemViewHandle>, + active_pane_item: Option<&dyn crate::ItemHandle>, cx: &mut ViewContext, ); } @@ -16,7 +16,7 @@ trait StatusItemViewHandle { fn to_any(&self) -> AnyViewHandle; fn set_active_pane_item( &self, - active_pane_item: Option<&dyn ItemViewHandle>, + active_pane_item: Option<&dyn ItemHandle>, cx: &mut MutableAppContext, ); } @@ -38,7 +38,7 @@ impl View for StatusBar { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.app_state::().theme.workspace.status_bar; + let theme = &cx.global::().theme.workspace.status_bar; Flex::row() .with_children(self.left_items.iter().map(|i| { ChildView::new(i.as_ref()) @@ -114,7 +114,7 @@ impl StatusItemViewHandle for ViewHandle { fn set_active_pane_item( &self, - active_pane_item: Option<&dyn ItemViewHandle>, + active_pane_item: Option<&dyn ItemHandle>, cx: &mut MutableAppContext, ) { self.update(cx, |this, cx| { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 6bff12a577bab115c96bf8554cbf5e2a3ca59d44..33155b5d4f4710338d6c4d4570af129f21e92624 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -9,7 +9,7 @@ mod status_bar; use anyhow::{anyhow, Result}; use client::{Authenticate, ChannelList, Client, User, UserStore}; use clock::ReplicaId; -use collections::BTreeMap; +use collections::HashMap; use gpui::{ action, color::Color, @@ -18,16 +18,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, + AnyModelHandle, AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, 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, ProjectEntryId, ProjectPath, Worktree}; pub use settings::Settings; use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus}; use status_bar::StatusBar; @@ -35,15 +35,25 @@ 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, }; use theme::{Theme, ThemeRegistry}; +type ItemBuilders = HashMap< + TypeId, + Arc< + dyn Fn( + usize, + ModelHandle, + AnyModelHandle, + &mut MutableAppContext, + ) -> Box, + >, +>; + action!(Open, Arc); action!(OpenNew, Arc); action!(OpenPaths, OpenParams); @@ -98,6 +108,22 @@ pub fn init(cx: &mut MutableAppContext) { ]); } +pub fn register_project_item(cx: &mut MutableAppContext, build_item: F) +where + V: ProjectItem, + F: 'static + Fn(ModelHandle, ModelHandle, &mut ViewContext) -> V, +{ + cx.update_default_global(|builders: &mut ItemBuilders, _| { + builders.insert( + TypeId::of::(), + Arc::new(move |window_id, project, model, cx| { + let model = model.downcast::().unwrap(); + Box::new(cx.add_view(window_id, |cx| build_item(project, model, cx))) + }), + ); + }); +} + pub struct AppState { pub languages: Arc, pub themes: Arc, @@ -105,7 +131,6 @@ pub struct AppState { pub user_store: ModelHandle, pub fs: Arc, pub channel_list: ModelHandle, - pub path_openers: Arc<[Box]>, pub build_window_options: &'static dyn Fn() -> WindowOptions<'static>, pub build_workspace: &'static dyn Fn( ModelHandle, @@ -126,35 +151,13 @@ pub struct JoinProjectParams { pub app_state: Arc, } -pub trait PathOpener { - fn open( - &self, - project: &mut Project, - path: ProjectPath, - cx: &mut ModelContext, - ) -> Option>>>; -} - -pub trait Item: Entity + Sized { - type View: ItemView; - - fn build_view( - handle: ModelHandle, - workspace: &Workspace, - nav_history: ItemNavHistory, - cx: &mut ViewContext, - ) -> Self::View; - - fn project_path(&self) -> Option; -} - -pub trait ItemView: View { +pub trait Item: View { fn deactivated(&mut self, _: &mut ViewContext) {} fn navigate(&mut self, _: Box, _: &mut ViewContext) {} - fn item(&self, cx: &AppContext) -> Box; fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox; fn project_path(&self, cx: &AppContext) -> Option; - fn clone_on_split(&self, _: ItemNavHistory, _: &mut ViewContext) -> Option + fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext); + fn clone_on_split(&self, _: &mut ViewContext) -> Option where Self: Sized, { @@ -202,36 +205,22 @@ 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>, - cx: &mut MutableAppContext, - ) -> Box; - fn boxed_clone(&self) -> Box; - fn downgrade(&self) -> Box; - fn to_any(&self) -> AnyModelHandle; - fn project_path(&self, cx: &AppContext) -> Option; -} +pub trait ProjectItem: Item { + type Item: project::Item; -pub trait WeakItemHandle { - fn id(&self) -> usize; - fn upgrade(&self, cx: &AppContext) -> Option>; + fn for_project_item( + project: ModelHandle, + item: ModelHandle, + cx: &mut ViewContext, + ) -> Self; } -pub trait ItemViewHandle: 'static { - fn item(&self, cx: &AppContext) -> Box; +pub trait ItemHandle: 'static { fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox; fn project_path(&self, cx: &AppContext) -> Option; - fn boxed_clone(&self) -> Box; - fn clone_on_split( - &self, - nav_history: Rc>, - cx: &mut MutableAppContext, - ) -> Option>; + fn boxed_clone(&self) -> Box; + fn set_nav_history(&self, nav_history: Rc>, cx: &mut MutableAppContext); + fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option>; fn added_to_pane(&mut self, cx: &mut ViewContext); fn deactivated(&self, cx: &mut MutableAppContext); fn navigate(&self, data: Box, cx: &mut MutableAppContext); @@ -251,103 +240,12 @@ pub trait ItemViewHandle: 'static { fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option; } -pub trait WeakItemViewHandle { +pub trait WeakItemHandle { fn id(&self) -> usize; - fn upgrade(&self, cx: &AppContext) -> Option>; -} - -impl ItemHandle for ModelHandle { - fn id(&self) -> usize { - self.id() - } - - fn add_view( - &self, - window_id: usize, - workspace: &Workspace, - nav_history: Rc>, - cx: &mut MutableAppContext, - ) -> Box { - 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 { - Box::new(self.clone()) - } - - fn downgrade(&self) -> Box { - Box::new(self.downgrade()) - } - - fn to_any(&self) -> AnyModelHandle { - self.clone().into() - } - - fn project_path(&self, cx: &AppContext) -> Option { - self.read(cx).project_path() - } -} - -impl ItemHandle for Box { - fn id(&self) -> usize { - ItemHandle::id(self.as_ref()) - } - - fn add_view( - &self, - window_id: usize, - workspace: &Workspace, - nav_history: Rc>, - cx: &mut MutableAppContext, - ) -> Box { - ItemHandle::add_view(self.as_ref(), window_id, workspace, nav_history, cx) - } - - fn boxed_clone(&self) -> Box { - self.as_ref().boxed_clone() - } - - fn downgrade(&self) -> Box { - self.as_ref().downgrade() - } - - fn to_any(&self) -> AnyModelHandle { - self.as_ref().to_any() - } - - fn project_path(&self, cx: &AppContext) -> Option { - self.as_ref().project_path(cx) - } -} - -impl WeakItemHandle for WeakModelHandle { - fn id(&self) -> usize { - WeakModelHandle::id(self) - } - - fn upgrade(&self, cx: &AppContext) -> Option> { - WeakModelHandle::::upgrade(self, cx).map(|i| Box::new(i) as Box) - } -} - -impl Hash for Box { - fn hash(&self, state: &mut H) { - self.id().hash(state); - } -} - -impl PartialEq for Box { - fn eq(&self, other: &Self) -> bool { - self.id() == other.id() - } + fn upgrade(&self, cx: &AppContext) -> Option>; } -impl Eq for Box {} - -impl dyn ItemViewHandle { +impl dyn ItemHandle { pub fn downcast(&self) -> Option> { self.to_any().downcast() } @@ -358,11 +256,7 @@ impl dyn ItemViewHandle { } } -impl ItemViewHandle for ViewHandle { - fn item(&self, cx: &AppContext) -> Box { - self.read(cx).item(cx) - } - +impl ItemHandle for ViewHandle { fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox { self.read(cx).tab_content(style, cx) } @@ -371,21 +265,25 @@ impl ItemViewHandle for ViewHandle { self.read(cx).project_path(cx) } - fn boxed_clone(&self) -> Box { + fn boxed_clone(&self) -> Box { Box::new(self.clone()) } fn clone_on_split( &self, - nav_history: Rc>, + // nav_history: Rc>, cx: &mut MutableAppContext, - ) -> Option> { + ) -> Option> { 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) + } + + fn set_nav_history(&self, nav_history: Rc>, cx: &mut MutableAppContext) { + self.update(cx, |item, cx| { + item.set_nav_history(ItemNavHistory::new(nav_history, &cx.handle()), cx); }) - .map(|handle| Box::new(handle) as Box) } fn added_to_pane(&mut self, cx: &mut ViewContext) { @@ -395,7 +293,7 @@ impl ItemViewHandle for ViewHandle { return; } if T::should_activate_item_on_event(event) { - if let Some(ix) = pane.index_for_item_view(&item) { + if let Some(ix) = pane.index_for_item(&item) { pane.activate_item(ix, cx); pane.activate(cx); } @@ -457,32 +355,25 @@ impl ItemViewHandle for ViewHandle { } } -impl Into for Box { +impl Into for Box { fn into(self) -> AnyViewHandle { self.to_any() } } -impl Clone for Box { - fn clone(&self) -> Box { - self.boxed_clone() - } -} - impl Clone for Box { fn clone(&self) -> Box { self.boxed_clone() } } -impl WeakItemViewHandle for WeakViewHandle { +impl WeakItemHandle for WeakViewHandle { fn id(&self) -> usize { self.id() } - fn upgrade(&self, cx: &AppContext) -> Option> { - self.upgrade(cx) - .map(|v| Box::new(v) as Box) + fn upgrade(&self, cx: &AppContext) -> Option> { + self.upgrade(cx).map(|v| Box::new(v) as Box) } } @@ -494,14 +385,13 @@ pub struct WorkspaceParams { pub languages: Arc, pub user_store: ModelHandle, pub channel_list: ModelHandle, - pub path_openers: Arc<[Box]>, } impl WorkspaceParams { #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut MutableAppContext) -> Self { let settings = Settings::test(cx); - cx.add_app_state(settings); + cx.set_global(settings); let fs = project::FakeFs::new(cx.background().clone()); let languages = Arc::new(LanguageRegistry::test()); @@ -525,7 +415,6 @@ impl WorkspaceParams { fs, languages, user_store, - path_openers: Arc::from([]), } } @@ -544,7 +433,6 @@ impl WorkspaceParams { languages: app_state.languages.clone(), user_store: app_state.user_store.clone(), channel_list: app_state.channel_list.clone(), - path_openers: app_state.path_openers.clone(), } } } @@ -562,8 +450,6 @@ pub struct Workspace { active_pane: ViewHandle, status_bar: ViewHandle, project: ModelHandle, - path_openers: Arc<[Box]>, - items: BTreeMap, Box>, _observe_current_user: Task<()>, } @@ -626,8 +512,6 @@ impl Workspace { left_sidebar: Sidebar::new(Side::Left), right_sidebar: Sidebar::new(Side::Right), project: params.project.clone(), - path_openers: params.path_openers.clone(), - items: Default::default(), _observe_current_user, } } @@ -690,7 +574,7 @@ impl Workspace { &mut self, abs_paths: &[PathBuf], cx: &mut ViewContext, - ) -> Task, Arc>>>> { + ) -> Task, Arc>>>> { let entries = abs_paths .iter() .cloned() @@ -782,68 +666,22 @@ impl Workspace { } } - pub fn open_path( - &mut self, - path: ProjectPath, - cx: &mut ViewContext, - ) -> Task, Arc>> { - let load_task = self.load_path(path, cx); - let pane = self.active_pane().clone().downgrade(); - cx.spawn(|this, mut cx| async move { - let item = load_task.await?; - this.update(&mut cx, |this, cx| { - let pane = pane - .upgrade(cx) - .ok_or_else(|| anyhow!("could not upgrade pane reference"))?; - Ok(this.open_item_in_pane(item, &pane, cx)) - }) - }) - } - - pub fn load_path( - &mut self, - path: ProjectPath, - cx: &mut ViewContext, - ) -> Task>> { - if let Some(existing_item) = self.item_for_path(&path, cx) { - return Task::ready(Ok(existing_item)); - } - - let project_path = path.clone(); - let path_openers = self.path_openers.clone(); - self.project.update(cx, |project, cx| { - for opener in path_openers.iter() { - if let Some(task) = opener.open(project, project_path.clone(), cx) { - return task; - } - } - Task::ready(Err(anyhow!("no opener found for path {:?}", project_path))) - }) - } - - fn item_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option> { - self.items - .values() - .filter_map(|i| i.upgrade(cx)) - .find(|i| i.project_path(cx).as_ref() == Some(path)) - } - - pub fn item_of_type(&self, cx: &AppContext) -> Option> { - self.items - .values() - .find_map(|i| i.upgrade(cx).and_then(|i| i.to_any().downcast())) + pub fn item_of_type(&self, cx: &AppContext) -> Option> { + self.items_of_type(cx).max_by_key(|item| item.id()) } pub fn items_of_type<'a, T: Item>( &'a self, cx: &'a AppContext, - ) -> impl 'a + Iterator> { - self.items - .values() - .filter_map(|i| i.upgrade(cx).and_then(|i| i.to_any().downcast())) + ) -> impl 'a + Iterator> { + self.panes.iter().flat_map(|pane| { + pane.read(cx) + .items() + .filter_map(|item| item.to_any().downcast()) + }) } - pub fn active_item(&self, cx: &AppContext) -> Option> { + pub fn active_item(&self, cx: &AppContext) -> Option> { self.active_pane().read(cx).active_item() } @@ -962,49 +800,81 @@ impl Workspace { pane } - pub fn open_item( + pub fn add_item(&mut self, item: Box, cx: &mut ViewContext) { + self.active_pane() + .update(cx, |pane, cx| pane.add_item(None, item, cx)) + } + + pub fn open_path( &mut self, - item_handle: T, + path: ProjectPath, cx: &mut ViewContext, - ) -> Box - where - T: 'static + ItemHandle, - { - self.open_item_in_pane(item_handle, &self.active_pane().clone(), cx) + ) -> Task, Arc>> { + let pane = self.active_pane().downgrade(); + let task = self.load_path(path, cx); + cx.spawn(|this, mut cx| async move { + let (project_entry_id, build_editor) = task.await?; + let pane = pane + .upgrade(&cx) + .ok_or_else(|| anyhow!("pane was closed"))?; + this.update(&mut cx, |_, cx| { + pane.update(cx, |pane, cx| { + Ok(pane.open_item(project_entry_id, cx, build_editor)) + }) + }) + }) } - pub fn open_item_in_pane( + pub(crate) fn load_path( &mut self, - item_handle: T, - pane: &ViewHandle, + path: ProjectPath, cx: &mut ViewContext, - ) -> Box - where - T: 'static + ItemHandle, - { - self.items - .insert(Reverse(item_handle.id()), item_handle.downgrade()); - pane.update(cx, |pane, cx| pane.open_item(item_handle, self, cx)) + ) -> Task< + Result<( + ProjectEntryId, + impl 'static + FnOnce(&mut MutableAppContext) -> Box, + )>, + > { + let project = self.project().clone(); + 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::() + .get(&project_item.model_type()) + .ok_or_else(|| anyhow!("no item builder for project item")) + .cloned() + })?; + let build_item = + move |cx: &mut MutableAppContext| build_item(window_id, project, project_item, cx); + Ok((project_entry_id, build_item)) + }) } - pub fn activate_pane_for_item( + pub fn open_project_item( &mut self, - item: &dyn ItemHandle, + project_item: ModelHandle, cx: &mut ViewContext, - ) -> 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 + ) -> ViewHandle + where + T: ProjectItem, + { + use project::Item as _; + + if let Some(item) = project_item + .read(cx) + .entry_id(cx) + .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id)) + .and_then(|item| item.downcast()) + { + self.activate_item(&item, cx); + return item; } + + let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx)); + self.add_item(Box::new(item.clone()), cx); + item } pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext) -> bool { @@ -1077,11 +947,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(); - 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); - new_pane.update(cx, |new_pane, cx| new_pane.add_item_view(clone, cx)); + 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()) { + new_pane.update(cx, |new_pane, cx| { + new_pane.add_item(project_entry_id, clone, cx); + }); } } self.center.split(&pane, &new_pane, direction).unwrap(); @@ -1122,7 +992,7 @@ impl Workspace { } fn render_connection_status(&self, cx: &mut RenderContext) -> Option { - let theme = &cx.app_state::().theme; + let theme = &cx.global::().theme; match &*self.client.status().borrow() { client::Status::ConnectionError | client::Status::ConnectionLost @@ -1308,7 +1178,7 @@ impl Workspace { fn render_disconnected_overlay(&self, cx: &AppContext) -> Option { if self.project.read(cx).is_read_only() { - let theme = &cx.app_state::().theme; + let theme = &cx.global::().theme; Some( EventHandler::new( Label::new( @@ -1339,7 +1209,7 @@ impl View for Workspace { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = cx.app_state::().theme.clone(); + let theme = cx.global::().theme.clone(); Stack::new() .with_child( Flex::column() diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 85960002469249d94503954f4775d4e4e7a23851..cf149b2469d5c66ebecfaca239decda7cca01e2f 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -61,7 +61,6 @@ fn main() { app.run(move |cx| { let http = http::client(); let client = client::Client::new(http.clone()); - let mut path_openers = Vec::new(); let mut languages = language::build_language_registry(login_shell_env_loaded); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); let channel_list = @@ -71,7 +70,7 @@ fn main() { client::Channel::init(&client); client::init(client.clone(), cx); workspace::init(cx); - editor::init(cx, &mut path_openers); + editor::init(cx); go_to_line::init(cx); file_finder::init(cx); chat_panel::init(cx); @@ -102,7 +101,7 @@ fn main() { cx.spawn(|mut cx| async move { while let Some(settings) = settings_rx.next().await { cx.update(|cx| { - cx.update_app_state(|s, _| *s = settings); + cx.update_global(|s, _| *s = settings); cx.refresh_windows(); }); } @@ -111,7 +110,7 @@ fn main() { languages.set_language_server_download_dir(zed::ROOT_PATH.clone()); languages.set_theme(&settings.theme.editor.syntax); - cx.add_app_state(settings); + cx.set_global(settings); let app_state = Arc::new(AppState { languages: Arc::new(languages), @@ -120,7 +119,6 @@ fn main() { client, user_store, fs, - path_openers: Arc::from(path_openers), build_window_options: &build_window_options, build_workspace: &build_workspace, }); diff --git a/crates/zed/src/test.rs b/crates/zed/src/test.rs index 35610854f30fd272a8763f6ecc8d016381e12baf..5b3bb41c1523bf910a523766f6af2c9a631d264d 100644 --- a/crates/zed/src/test.rs +++ b/crates/zed/src/test.rs @@ -17,9 +17,8 @@ fn init_logger() { pub fn test_app_state(cx: &mut MutableAppContext) -> Arc { let settings = Settings::test(cx); - let mut path_openers = Vec::new(); - editor::init(cx, &mut path_openers); - cx.add_app_state(settings); + editor::init(cx); + cx.set_global(settings); let themes = ThemeRegistry::new(Assets, cx.font_cache().clone()); let http = FakeHttpClient::with_404_response(); let client = Client::new(http.clone()); @@ -40,7 +39,6 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc { client, user_store, fs: FakeFs::new(cx.background().clone()), - path_openers: Arc::from(path_openers), build_window_options: &build_window_options, build_workspace: &build_workspace, }) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 20da4e4800b6547b3b6a26096a3a865b7aca08b3..2b61279a2cf4fa29e85eac14d51817e27da55b24 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -43,7 +43,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.add_global_action(quit); cx.add_global_action({ move |action: &AdjustBufferFontSize, cx| { - cx.update_app_state::(|settings, cx| { + cx.update_global::(|settings, cx| { settings.buffer_font_size = (settings.buffer_font_size + action.0).max(MIN_FONT_SIZE); cx.refresh_windows(); @@ -111,7 +111,6 @@ pub fn build_workspace( languages: app_state.languages.clone(), user_store: app_state.user_store.clone(), channel_list: app_state.channel_list.clone(), - path_openers: app_state.path_openers.clone(), }; let mut workspace = Workspace::new(&workspace_params, cx); let project = workspace.project().clone(); @@ -193,7 +192,7 @@ mod tests { use theme::{Theme, ThemeRegistry, DEFAULT_THEME_NAME}; use util::test::temp_tree; use workspace::{ - open_paths, pane, ItemView, ItemViewHandle, OpenNew, Pane, SplitDirection, WorkspaceHandle, + open_paths, pane, Item, ItemHandle, OpenNew, Pane, SplitDirection, WorkspaceHandle, }; #[gpui::test] @@ -325,7 +324,7 @@ mod tests { pane.active_item().unwrap().project_path(cx), Some(file1.clone()) ); - assert_eq!(pane.item_views().count(), 1); + assert_eq!(pane.items().count(), 1); }); // Open the second entry @@ -339,7 +338,7 @@ mod tests { pane.active_item().unwrap().project_path(cx), Some(file2.clone()) ); - assert_eq!(pane.item_views().count(), 2); + assert_eq!(pane.items().count(), 2); }); // Open the first entry again. The existing pane item is activated. @@ -355,7 +354,7 @@ mod tests { pane.active_item().unwrap().project_path(cx), Some(file1.clone()) ); - assert_eq!(pane.item_views().count(), 2); + assert_eq!(pane.items().count(), 2); }); // Split the pane with the first entry, then open the second entry again. @@ -394,7 +393,7 @@ mod tests { Some(file3.clone()) ); let pane_entries = pane - .item_views() + .items() .map(|i| i.project_path(cx).unwrap()) .collect::>(); assert_eq!(pane_entries, &[file1, file2, file3]);