From 84c549494925ea4d5bfdd14e60ce641ae47ec127 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 31 Oct 2023 12:26:49 +0200 Subject: [PATCH 1/4] Fix prettier errors around Zed's settings.json/keymap.json files Ports https://github.com/zed-industries/zed/pull/3191 to zed2 Deals with zed-industries/community#2191 Fix Zed starting too many prettier installations in the beginning, and not being able to format the config files. --- crates/prettier2/src/prettier2.rs | 8 +- crates/project2/src/project2.rs | 121 +++++++++++++++++++++++++----- crates/project2/src/worktree.rs | 12 +-- 3 files changed, 113 insertions(+), 28 deletions(-) diff --git a/crates/prettier2/src/prettier2.rs b/crates/prettier2/src/prettier2.rs index 6b13429eec4bacf1e4de6409a0f4a2e22bdd0277..a71bf1a8b03c2117d44387d2502c65f7a766aefe 100644 --- a/crates/prettier2/src/prettier2.rs +++ b/crates/prettier2/src/prettier2.rs @@ -44,6 +44,9 @@ pub const PRETTIER_SERVER_JS: &str = include_str!("./prettier_server.js"); const PRETTIER_PACKAGE_NAME: &str = "prettier"; const TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME: &str = "prettier-plugin-tailwindcss"; +#[cfg(any(test, feature = "test-support"))] +pub const FORMAT_SUFFIX: &str = "\nformatted by test prettier"; + impl Prettier { pub const CONFIG_FILE_NAMES: &'static [&'static str] = &[ ".prettierrc", @@ -60,9 +63,6 @@ impl Prettier { ".editorconfig", ]; - #[cfg(any(test, feature = "test-support"))] - pub const FORMAT_SUFFIX: &str = "\nformatted by test prettier"; - pub async fn locate( starting_path: Option, fs: Arc, @@ -328,7 +328,7 @@ impl Prettier { #[cfg(any(test, feature = "test-support"))] Self::Test(_) => Ok(buffer .update(cx, |buffer, cx| { - let formatted_text = buffer.text() + Self::FORMAT_SUFFIX; + let formatted_text = buffer.text() + FORMAT_SUFFIX; buffer.diff(formatted_text, cx) })? .await), diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index fdc1a2a0ebc55d0744392cee14cf4580af0fd2ce..fe062a78a4256d63a9869fb8f47396f7655e575f 100644 --- a/crates/project2/src/project2.rs +++ b/crates/project2/src/project2.rs @@ -54,7 +54,7 @@ use lsp_command::*; use node_runtime::NodeRuntime; use parking_lot::Mutex; use postage::watch; -use prettier2::{LocateStart, Prettier, PRETTIER_SERVER_FILE, PRETTIER_SERVER_JS}; +use prettier2::{LocateStart, Prettier}; use project_settings::{LspSettings, ProjectSettings}; use rand::prelude::*; use search::SearchQuery; @@ -80,16 +80,15 @@ use std::{ time::{Duration, Instant}, }; use terminals::Terminals; -use text::{Anchor, LineEnding, Rope}; +use text::Anchor; use util::{ - debug_panic, defer, - http::HttpClient, - merge_json_value_into, - paths::{DEFAULT_PRETTIER_DIR, LOCAL_SETTINGS_RELATIVE_PATH}, - post_inc, ResultExt, TryFutureExt as _, + debug_panic, defer, http::HttpClient, merge_json_value_into, + paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, TryFutureExt as _, }; pub use fs2::*; +#[cfg(any(test, feature = "test-support"))] +pub use prettier2::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX; pub use worktree::*; const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4; @@ -163,12 +162,22 @@ pub struct Project { copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, node: Option>, + // TODO kb uncomment + // #[cfg(not(any(test, feature = "test-support")))] + default_prettier: Option, prettier_instances: HashMap< (Option, PathBuf), Shared, Arc>>>, >, } +// TODO kb uncomment +// #[cfg(not(any(test, feature = "test-support")))] +struct DefaultPrettier { + installation_process: Option>>, + installed_plugins: HashSet<&'static str>, +} + struct DelayedDebounced { task: Option>, cancel_channel: Option>, @@ -679,6 +688,9 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(), node: Some(node), + // TODO kb uncomment + // #[cfg(not(any(test, feature = "test-support")))] + default_prettier: None, prettier_instances: HashMap::default(), } }) @@ -780,6 +792,9 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(), node: None, + // TODO kb uncomment + // #[cfg(not(any(test, feature = "test-support")))] + default_prettier: None, prettier_instances: HashMap::default(), }; for worktree in worktrees { @@ -8553,8 +8568,21 @@ impl Project { } } + // TODO kb uncomment + // #[cfg(any(test, feature = "test-support"))] + // fn install_default_formatters( + // &mut self, + // _: Option, + // _: &Language, + // _: &LanguageSettings, + // _: &mut ModelContext, + // ) -> Task> { + // Task::ready(Ok(())) + // } + + // #[cfg(not(any(test, feature = "test-support")))] fn install_default_formatters( - &self, + &mut self, worktree: Option, new_language: &Language, language_settings: &LanguageSettings, @@ -8583,22 +8611,76 @@ impl Project { return Task::ready(Ok(())); }; - let default_prettier_dir = DEFAULT_PRETTIER_DIR.as_path(); + let mut plugins_to_install = prettier_plugins; + let (mut install_success_tx, mut install_success_rx) = + futures::channel::mpsc::channel::>(1); + let new_installation_process = cx + .spawn(|this, mut cx| async move { + if let Some(installed_plugins) = install_success_rx.next().await { + this.update(&mut cx, |this, _| { + let default_prettier = + this.default_prettier + .get_or_insert_with(|| DefaultPrettier { + installation_process: None, + installed_plugins: HashSet::default(), + }); + if !installed_plugins.is_empty() { + log::info!("Installed new prettier plugins: {installed_plugins:?}"); + default_prettier.installed_plugins.extend(installed_plugins); + } + }) + .ok(); + } + }) + .shared(); + let previous_installation_process = + if let Some(default_prettier) = &mut self.default_prettier { + plugins_to_install + .retain(|plugin| !default_prettier.installed_plugins.contains(plugin)); + if plugins_to_install.is_empty() { + return Task::ready(Ok(())); + } + std::mem::replace( + &mut default_prettier.installation_process, + Some(new_installation_process.clone()), + ) + } else { + None + }; + + let default_prettier_dir = util::paths::DEFAULT_PRETTIER_DIR.as_path(); let already_running_prettier = self .prettier_instances .get(&(worktree, default_prettier_dir.to_path_buf())) .cloned(); - let fs = Arc::clone(&self.fs); - cx.executor() - .spawn(async move { - let prettier_wrapper_path = default_prettier_dir.join(PRETTIER_SERVER_FILE); + cx.spawn_on_main(move |this, mut cx| async move { + if let Some(previous_installation_process) = previous_installation_process { + previous_installation_process.await; + } + let mut everything_was_installed = false; + this.update(&mut cx, |this, _| { + match &mut this.default_prettier { + Some(default_prettier) => { + plugins_to_install + .retain(|plugin| !default_prettier.installed_plugins.contains(plugin)); + everything_was_installed = plugins_to_install.is_empty(); + }, + None => this.default_prettier = Some(DefaultPrettier { installation_process: Some(new_installation_process), installed_plugins: HashSet::default() }), + } + })?; + if everything_was_installed { + return Ok(()); + } + + cx.spawn(move |_| async move { + let prettier_wrapper_path = default_prettier_dir.join(prettier2::PRETTIER_SERVER_FILE); // method creates parent directory if it doesn't exist - fs.save(&prettier_wrapper_path, &Rope::from(PRETTIER_SERVER_JS), LineEnding::Unix).await - .with_context(|| format!("writing {PRETTIER_SERVER_FILE} file at {prettier_wrapper_path:?}"))?; + fs.save(&prettier_wrapper_path, &text::Rope::from(prettier2::PRETTIER_SERVER_JS), text::LineEnding::Unix).await + .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier2::PRETTIER_SERVER_FILE))?; let packages_to_versions = future::try_join_all( - prettier_plugins + plugins_to_install .iter() .chain(Some(&"prettier")) .map(|package_name| async { @@ -8619,15 +8701,18 @@ impl Project { (package.as_str(), version.as_str()) }).collect::>(); node.npm_install_packages(default_prettier_dir, &borrowed_packages).await.context("fetching formatter packages")?; + let installed_packages = !plugins_to_install.is_empty(); + install_success_tx.try_send(plugins_to_install).ok(); - if !prettier_plugins.is_empty() { + if !installed_packages { if let Some(prettier) = already_running_prettier { prettier.await.map_err(|e| anyhow::anyhow!("Default prettier startup await failure: {e:#}"))?.clear_cache().await.context("clearing default prettier cache after plugins install")?; } } anyhow::Ok(()) - }) + }).await + }) } } diff --git a/crates/project2/src/worktree.rs b/crates/project2/src/worktree.rs index d73769cc0004e213eeced33eabc6cf739be1e448..045fd7953ef7afe68b05ec5570dbc569fea9b109 100644 --- a/crates/project2/src/worktree.rs +++ b/crates/project2/src/worktree.rs @@ -2659,12 +2659,12 @@ impl language2::File for File { impl language2::LocalFile for File { fn abs_path(&self, cx: &AppContext) -> PathBuf { - self.worktree - .read(cx) - .as_local() - .unwrap() - .abs_path - .join(&self.path) + let worktree_path = &self.worktree.read(cx).as_local().unwrap().abs_path; + if self.path.as_ref() == Path::new("") { + worktree_path.to_path_buf() + } else { + worktree_path.join(&self.path) + } } fn load(&self, cx: &AppContext) -> Task> { From 6a3974ddbb1bb39c1b719238bf10a03c4081a95e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 31 Oct 2023 13:08:18 +0200 Subject: [PATCH 2/4] Remove TODOs --- crates/project2/src/project2.rs | 35 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index fe062a78a4256d63a9869fb8f47396f7655e575f..4f422bb0e2c46a2c792250e63f9fa169cdf3fcea 100644 --- a/crates/project2/src/project2.rs +++ b/crates/project2/src/project2.rs @@ -162,8 +162,7 @@ pub struct Project { copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, node: Option>, - // TODO kb uncomment - // #[cfg(not(any(test, feature = "test-support")))] + #[cfg(not(any(test, feature = "test-support")))] default_prettier: Option, prettier_instances: HashMap< (Option, PathBuf), @@ -171,8 +170,7 @@ pub struct Project { >, } -// TODO kb uncomment -// #[cfg(not(any(test, feature = "test-support")))] +#[cfg(not(any(test, feature = "test-support")))] struct DefaultPrettier { installation_process: Option>>, installed_plugins: HashSet<&'static str>, @@ -688,8 +686,7 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(), node: Some(node), - // TODO kb uncomment - // #[cfg(not(any(test, feature = "test-support")))] + #[cfg(not(any(test, feature = "test-support")))] default_prettier: None, prettier_instances: HashMap::default(), } @@ -792,8 +789,7 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(), node: None, - // TODO kb uncomment - // #[cfg(not(any(test, feature = "test-support")))] + #[cfg(not(any(test, feature = "test-support")))] default_prettier: None, prettier_instances: HashMap::default(), }; @@ -8568,19 +8564,18 @@ impl Project { } } - // TODO kb uncomment - // #[cfg(any(test, feature = "test-support"))] - // fn install_default_formatters( - // &mut self, - // _: Option, - // _: &Language, - // _: &LanguageSettings, - // _: &mut ModelContext, - // ) -> Task> { - // Task::ready(Ok(())) - // } + #[cfg(any(test, feature = "test-support"))] + fn install_default_formatters( + &mut self, + _: Option, + _: &Language, + _: &LanguageSettings, + _: &mut ModelContext, + ) -> Task> { + Task::ready(Ok(())) + } - // #[cfg(not(any(test, feature = "test-support")))] + #[cfg(not(any(test, feature = "test-support")))] fn install_default_formatters( &mut self, worktree: Option, From 7b6514b178732124cb6b7ecc01f1ca73a713d968 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Oct 2023 16:16:30 +0100 Subject: [PATCH 3/4] Simplify `AnyView` Co-Authored-By: Nathan Sobo --- crates/gpui2/src/app.rs | 2 +- crates/gpui2/src/interactive.rs | 2 +- crates/gpui2/src/view.rs | 168 ++++++++++-------------- crates/gpui2/src/window.rs | 2 +- crates/storybook2/src/story_selector.rs | 76 +++++------ 5 files changed, 112 insertions(+), 138 deletions(-) diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 88edcb457fd553ed801ecb07c357dc1d6f9c9bf9..a49b2aaa1c5d1c380fadc1fab13e5b116e5d719c 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -829,7 +829,7 @@ impl MainThread { let handle = WindowHandle::new(id); let mut window = Window::new(handle.into(), options, cx); let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window)); - window.root_view.replace(root_view.into_any()); + window.root_view.replace(root_view.into()); cx.windows.get_mut(id).unwrap().replace(window); handle }) diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index 317d7cea61dd917ceab09fd9205b7ea7dfd6990c..5a37c3ee7abafb30bc1c8aa132b0793c30d0d92e 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -328,7 +328,7 @@ pub trait StatefulInteractive: StatelessInteractive { ); self.stateful_interaction().drag_listener = Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag { - view: listener(view_state, cx).into_any(), + view: listener(view_state, cx).into(), cursor_offset, })); self diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index 3c3ad034b45232ca131acb78dfb2642ece21f282..e702fcb2745d43464238dcb127f5857952aaaee7 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -4,7 +4,7 @@ use crate::{ ViewContext, VisualContext, WeakModel, WindowContext, }; use anyhow::{Context, Result}; -use std::{any::TypeId, marker::PhantomData, sync::Arc}; +use std::{any::TypeId, marker::PhantomData}; pub trait Render: 'static + Sized { type Element: Element + 'static + Send; @@ -18,12 +18,6 @@ pub struct View { impl Sealed for View {} -impl View { - pub fn into_any(self) -> AnyView { - AnyView(Arc::new(self)) - } -} - impl Entity for View { type Weak = WeakView; @@ -265,94 +259,110 @@ where } } -#[derive(Clone)] -pub struct AnyView(Arc); +#[derive(Clone, Debug)] +pub struct AnyView { + model: AnyModel, + initialize: fn(&Self, &mut WindowContext) -> AnyBox, + layout: fn(&Self, &mut AnyBox, &mut WindowContext) -> LayoutId, + paint: fn(&Self, &mut AnyBox, &mut WindowContext), +} impl AnyView { - pub fn downcast(self) -> Result, AnyView> { - self.0 - .model() - .downcast() - .map(|model| View { model }) - .map_err(|_| self) + pub fn downcast(self) -> Result, Self> { + match self.model.downcast() { + Ok(model) => Ok(View { model }), + Err(model) => Err(Self { + model, + initialize: self.initialize, + layout: self.layout, + paint: self.paint, + }), + } } pub(crate) fn entity_type(&self) -> TypeId { - self.0.entity_type() + self.model.entity_type } pub(crate) fn draw(&self, available_space: Size, cx: &mut WindowContext) { - let mut rendered_element = self.0.initialize(cx); - let layout_id = self.0.layout(&mut rendered_element, cx); + let mut rendered_element = (self.initialize)(self, cx); + let layout_id = (self.layout)(self, &mut rendered_element, cx); cx.window .layout_engine .compute_layout(layout_id, available_space); - let bounds = cx.window.layout_engine.layout_bounds(layout_id); - self.0.paint(bounds, &mut rendered_element, cx); + (self.paint)(self, &mut rendered_element, cx); } } -impl Component for AnyView { - fn render(self) -> AnyElement { - AnyElement::new(EraseAnyViewState { - view: self, - parent_view_state_type: PhantomData, - }) +impl Component for AnyView { + fn render(self) -> AnyElement { + AnyElement::new(self) + } +} + +impl From> for AnyView { + fn from(value: View) -> Self { + AnyView { + model: value.model.into_any(), + initialize: |view, cx| { + cx.with_element_id(view.model.entity_id, |_, cx| { + let view = view.clone().downcast::().unwrap(); + Box::new(AnyElement::new( + view.update(cx, |view, cx| Render::render(view, cx)), + )) + }) + }, + layout: |view, element, cx| { + cx.with_element_id(view.model.entity_id, |_, cx| { + let view = view.clone().downcast::().unwrap(); + let element = element.downcast_mut::>().unwrap(); + view.update(cx, |view, cx| element.layout(view, cx)) + }) + }, + paint: |view, element, cx| { + cx.with_element_id(view.model.entity_id, |_, cx| { + let view = view.clone().downcast::().unwrap(); + let element = element.downcast_mut::>().unwrap(); + view.update(cx, |view, cx| element.paint(view, cx)) + }) + }, + } } } -impl Element<()> for AnyView { +impl Element for AnyView { type ElementState = AnyBox; - fn id(&self) -> Option { - Some(ElementId::View(self.0.entity_id())) + fn id(&self) -> Option { + Some(self.model.entity_id.into()) } fn initialize( &mut self, - _: &mut (), - _: Option, - cx: &mut ViewContext<()>, + _view_state: &mut ParentViewState, + _element_state: Option, + cx: &mut ViewContext, ) -> Self::ElementState { - self.0.initialize(cx) + (self.initialize)(self, cx) } fn layout( &mut self, - _: &mut (), - element: &mut Self::ElementState, - cx: &mut ViewContext<()>, + _view_state: &mut ParentViewState, + rendered_element: &mut Self::ElementState, + cx: &mut ViewContext, ) -> LayoutId { - self.0.layout(element, cx) + (self.layout)(self, rendered_element, cx) } fn paint( &mut self, - bounds: Bounds, - _: &mut (), - element: &mut AnyBox, - cx: &mut ViewContext<()>, + _bounds: Bounds, + _view_state: &mut ParentViewState, + rendered_element: &mut Self::ElementState, + cx: &mut ViewContext, ) { - self.0.paint(bounds, element, cx) - } -} - -impl std::fmt::Debug for AnyView { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.debug(f) - } -} - -struct EraseAnyViewState { - view: AnyView, - parent_view_state_type: PhantomData, -} - -unsafe impl Send for EraseAnyViewState {} - -impl Component for EraseAnyViewState { - fn render(self) -> AnyElement { - AnyElement::new(self) + (self.paint)(self, rendered_element, cx) } } @@ -367,39 +377,3 @@ where (self)(cx) } } - -impl Element for EraseAnyViewState { - type ElementState = AnyBox; - - fn id(&self) -> Option { - Element::id(&self.view) - } - - fn initialize( - &mut self, - _: &mut ParentV, - _: Option, - cx: &mut ViewContext, - ) -> Self::ElementState { - self.view.0.initialize(cx) - } - - fn layout( - &mut self, - _: &mut ParentV, - element: &mut Self::ElementState, - cx: &mut ViewContext, - ) -> LayoutId { - self.view.0.layout(element, cx) - } - - fn paint( - &mut self, - bounds: Bounds, - _: &mut ParentV, - element: &mut Self::ElementState, - cx: &mut ViewContext, - ) { - self.view.0.paint(bounds, element, cx) - } -} diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 7898ac34fce4f8162b47c7f4083f11f58bbea62e..3997a3197f9cc1685b3f455665a8d54852bb8b8c 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -967,7 +967,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { self.window.mouse_position = position; if self.active_drag.is_none() { self.active_drag = Some(AnyDrag { - view: self.build_view(|_| files).into_any(), + view: self.build_view(|_| files).into(), cursor_offset: position, }); } diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 30d31f8cf3bea8a4f77c37b8c0988fd4a50771fb..968309bd8a0cee2f00cb005cf0a486c51940e31f 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -28,17 +28,17 @@ pub enum ElementStory { impl ElementStory { pub fn story(&self, cx: &mut WindowContext) -> AnyView { match self { - Self::Colors => cx.build_view(|_| ColorsStory).into_any(), - Self::Avatar => cx.build_view(|_| AvatarStory).into_any(), - Self::Button => cx.build_view(|_| ButtonStory).into_any(), - Self::Details => cx.build_view(|_| DetailsStory).into_any(), - Self::Focus => FocusStory::view(cx).into_any(), - Self::Icon => cx.build_view(|_| IconStory).into_any(), - Self::Input => cx.build_view(|_| InputStory).into_any(), - Self::Label => cx.build_view(|_| LabelStory).into_any(), - Self::Scroll => ScrollStory::view(cx).into_any(), - Self::Text => TextStory::view(cx).into_any(), - Self::ZIndex => cx.build_view(|_| ZIndexStory).into_any(), + Self::Colors => cx.build_view(|_| ColorsStory).into(), + Self::Avatar => cx.build_view(|_| AvatarStory).into(), + Self::Button => cx.build_view(|_| ButtonStory).into(), + Self::Details => cx.build_view(|_| DetailsStory).into(), + Self::Focus => FocusStory::view(cx).into(), + Self::Icon => cx.build_view(|_| IconStory).into(), + Self::Input => cx.build_view(|_| InputStory).into(), + Self::Label => cx.build_view(|_| LabelStory).into(), + Self::Scroll => ScrollStory::view(cx).into(), + Self::Text => TextStory::view(cx).into(), + Self::ZIndex => cx.build_view(|_| ZIndexStory).into(), } } } @@ -77,32 +77,32 @@ pub enum ComponentStory { impl ComponentStory { pub fn story(&self, cx: &mut WindowContext) -> AnyView { match self { - Self::AssistantPanel => cx.build_view(|_| ui::AssistantPanelStory).into_any(), - Self::Buffer => cx.build_view(|_| ui::BufferStory).into_any(), - Self::Breadcrumb => cx.build_view(|_| ui::BreadcrumbStory).into_any(), - Self::ChatPanel => cx.build_view(|_| ui::ChatPanelStory).into_any(), - Self::CollabPanel => cx.build_view(|_| ui::CollabPanelStory).into_any(), - Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into_any(), - Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into_any(), - Self::Facepile => cx.build_view(|_| ui::FacepileStory).into_any(), - Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into_any(), - Self::LanguageSelector => cx.build_view(|_| ui::LanguageSelectorStory).into_any(), - Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into_any(), - Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into_any(), - Self::Palette => cx.build_view(|cx| ui::PaletteStory).into_any(), - Self::Panel => cx.build_view(|cx| ui::PanelStory).into_any(), - Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into_any(), - Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into_any(), - Self::Tab => cx.build_view(|_| ui::TabStory).into_any(), - Self::TabBar => cx.build_view(|_| ui::TabBarStory).into_any(), - Self::Terminal => cx.build_view(|_| ui::TerminalStory).into_any(), - Self::ThemeSelector => cx.build_view(|_| ui::ThemeSelectorStory).into_any(), - Self::Toast => cx.build_view(|_| ui::ToastStory).into_any(), - Self::Toolbar => cx.build_view(|_| ui::ToolbarStory).into_any(), - Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into_any(), - Self::Copilot => cx.build_view(|_| ui::CopilotModalStory).into_any(), - Self::TitleBar => ui::TitleBarStory::view(cx).into_any(), - Self::Workspace => ui::WorkspaceStory::view(cx).into_any(), + Self::AssistantPanel => cx.build_view(|_| ui::AssistantPanelStory).into(), + Self::Buffer => cx.build_view(|_| ui::BufferStory).into(), + Self::Breadcrumb => cx.build_view(|_| ui::BreadcrumbStory).into(), + Self::ChatPanel => cx.build_view(|_| ui::ChatPanelStory).into(), + Self::CollabPanel => cx.build_view(|_| ui::CollabPanelStory).into(), + Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into(), + Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into(), + Self::Facepile => cx.build_view(|_| ui::FacepileStory).into(), + Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into(), + Self::LanguageSelector => cx.build_view(|_| ui::LanguageSelectorStory).into(), + Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into(), + Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into(), + Self::Palette => cx.build_view(|cx| ui::PaletteStory).into(), + Self::Panel => cx.build_view(|cx| ui::PanelStory).into(), + Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into(), + Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into(), + Self::Tab => cx.build_view(|_| ui::TabStory).into(), + Self::TabBar => cx.build_view(|_| ui::TabBarStory).into(), + Self::Terminal => cx.build_view(|_| ui::TerminalStory).into(), + Self::ThemeSelector => cx.build_view(|_| ui::ThemeSelectorStory).into(), + Self::Toast => cx.build_view(|_| ui::ToastStory).into(), + Self::Toolbar => cx.build_view(|_| ui::ToolbarStory).into(), + Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into(), + Self::Copilot => cx.build_view(|_| ui::CopilotModalStory).into(), + Self::TitleBar => ui::TitleBarStory::view(cx).into(), + Self::Workspace => ui::WorkspaceStory::view(cx).into(), } } } @@ -149,7 +149,7 @@ impl StorySelector { match self { Self::Element(element_story) => element_story.story(cx), Self::Component(component_story) => component_story.story(cx), - Self::KitchenSink => KitchenSinkStory::view(cx).into_any(), + Self::KitchenSink => KitchenSinkStory::view(cx).into(), } } } From 0aa9c6b61d28e8506fc49ef3e9117d0fc933fb39 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Oct 2023 16:19:46 +0100 Subject: [PATCH 4/4] Introduce `AnyWeakView` Co-Authored-By: Nathan Sobo --- crates/gpui2/src/view.rs | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index e702fcb2745d43464238dcb127f5857952aaaee7..ff5f10e722445b421de3b297d166b79d641e4719 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -1,7 +1,7 @@ use crate::{ - private::Sealed, AnyBox, AnyElement, AnyModel, AppContext, AvailableSpace, BorrowWindow, - Bounds, Component, Element, ElementId, Entity, EntityId, LayoutId, Model, Pixels, Size, - ViewContext, VisualContext, WeakModel, WindowContext, + private::Sealed, AnyBox, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, + BorrowWindow, Bounds, Component, Element, ElementId, Entity, EntityId, LayoutId, Model, Pixels, + Size, ViewContext, VisualContext, WeakModel, WindowContext, }; use anyhow::{Context, Result}; use std::{any::TypeId, marker::PhantomData}; @@ -262,12 +262,21 @@ where #[derive(Clone, Debug)] pub struct AnyView { model: AnyModel, - initialize: fn(&Self, &mut WindowContext) -> AnyBox, - layout: fn(&Self, &mut AnyBox, &mut WindowContext) -> LayoutId, - paint: fn(&Self, &mut AnyBox, &mut WindowContext), + initialize: fn(&AnyView, &mut WindowContext) -> AnyBox, + layout: fn(&AnyView, &mut AnyBox, &mut WindowContext) -> LayoutId, + paint: fn(&AnyView, &mut AnyBox, &mut WindowContext), } impl AnyView { + pub fn downgrade(&self) -> AnyWeakView { + AnyWeakView { + model: self.model.downgrade(), + initialize: self.initialize, + layout: self.layout, + paint: self.paint, + } + } + pub fn downcast(self) -> Result, Self> { match self.model.downcast() { Ok(model) => Ok(View { model }), @@ -366,6 +375,25 @@ impl Element for AnyView { } } +pub struct AnyWeakView { + model: AnyWeakModel, + initialize: fn(&AnyView, &mut WindowContext) -> AnyBox, + layout: fn(&AnyView, &mut AnyBox, &mut WindowContext) -> LayoutId, + paint: fn(&AnyView, &mut AnyBox, &mut WindowContext), +} + +impl AnyWeakView { + pub fn upgrade(&self) -> Option { + let model = self.model.upgrade()?; + Some(AnyView { + model, + initialize: self.initialize, + layout: self.layout, + paint: self.paint, + }) + } +} + impl Render for T where T: 'static + FnMut(&mut WindowContext) -> E,