diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d32bfd05fe..54b660c80c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,8 @@ - [Added restoring of last project snapshot on shortcut.][4050] - [Added contextual suggestions to argument dropdowns][4072]. Dropdowns will now contain suggestions which are based on evaluated data. +- [Added a shortcut to show internal components (private API) in the component + browser.][5582] - [Improved component browser entry filtering and sorting][5645]. The component browser will now provide suggestions matching either the component's label or the corresponding code. @@ -482,6 +484,7 @@ [4120]: https://github.com/enso-org/enso/pull/4120 [4050]: https://github.com/enso-org/enso/pull/4050 [4072]: https://github.com/enso-org/enso/pull/4072 +[5582]: https://github.com/enso-org/enso/pull/5582 [5645]: https://github.com/enso-org/enso/pull/5645 [5646]: https://github.com/enso-org/enso/pull/5646 [5656]: https://github.com/enso-org/enso/pull/5656 diff --git a/app/gui/docs/product/shortcuts.md b/app/gui/docs/product/shortcuts.md index 8db25cc611e..bf2a77c009e 100644 --- a/app/gui/docs/product/shortcuts.md +++ b/app/gui/docs/product/shortcuts.md @@ -129,6 +129,7 @@ broken and require further investigation. | ctrl + alt + shift + r | Reload the visual interface. | | ctrl + alt + 0 - 10 | Switch between debug rendering modes (0 is the normal mode). | | ctrl + alt + ` | Toggle profiling monitor (performance, memory usage, etc). | +| ctrl + alt + p | Toggle the visibility of internal components (private API) in the component browser. | | ctrl + d | Send test data to the selected node. | | ctrl + shift + enter | Push a hardcoded breadcrumb without navigating. | | ctrl + shift + arrow up | Pop a breadcrumb without navigating. | diff --git a/app/gui/src/controller/ide.rs b/app/gui/src/controller/ide.rs index ea2ea133dc5..8bfe4937c3c 100644 --- a/app/gui/src/controller/ide.rs +++ b/app/gui/src/controller/ide.rs @@ -192,6 +192,12 @@ pub trait API: Debug { // Automock macro does not work without explicit lifetimes here. #[allow(clippy::needless_lifetimes)] fn manage_projects<'a>(&'a self) -> FallibleResult<&'a dyn ManagingProjectAPI>; + + /// Return whether private entries should be visible in the component browser. + fn are_component_browser_private_entries_visible(&self) -> bool; + + /// Sets whether private entries should be visible in the component browser. + fn set_component_browser_private_entries_visibility(&self, visibility: bool); } /// A polymorphic handle of IDE controller. diff --git a/app/gui/src/controller/ide/desktop.rs b/app/gui/src/controller/ide/desktop.rs index 8639447a4a8..6f9cb48ec6a 100644 --- a/app/gui/src/controller/ide/desktop.rs +++ b/app/gui/src/controller/ide/desktop.rs @@ -38,12 +38,13 @@ const UNNAMED_PROJECT_NAME: &str = "Unnamed"; #[derive(Clone, CloneRef, Derivative)] #[derivative(Debug)] pub struct Handle { - current_project: Rc>>, + current_project: Rc>>, #[derivative(Debug = "ignore")] - project_manager: Rc, + project_manager: Rc, status_notifications: StatusNotificationPublisher, - parser: Parser, - notifications: notification::Publisher, + parser: Parser, + notifications: notification::Publisher, + component_browser_private_entries_visibility_flag: Rc>, } impl Handle { @@ -71,7 +72,15 @@ impl Handle { let status_notifications = default(); let parser = Parser::new(); let notifications = default(); - Self { current_project, project_manager, status_notifications, parser, notifications } + let component_browser_private_entries_visibility_flag = default(); + Self { + current_project, + project_manager, + status_notifications, + parser, + notifications, + component_browser_private_entries_visibility_flag, + } } /// Open project with provided name. @@ -107,6 +116,17 @@ impl API for Handle { fn manage_projects(&self) -> FallibleResult<&dyn ManagingProjectAPI> { Ok(self) } + + fn are_component_browser_private_entries_visible(&self) -> bool { + self.component_browser_private_entries_visibility_flag.get() + } + + fn set_component_browser_private_entries_visibility(&self, visibility: bool) { + debug!( + "Setting the visibility of private entries in the component browser to {visibility}." + ); + self.component_browser_private_entries_visibility_flag.set(visibility); + } } impl ManagingProjectAPI for Handle { diff --git a/app/gui/src/controller/ide/plain.rs b/app/gui/src/controller/ide/plain.rs index 4c4d3d619f8..9b3002923d9 100644 --- a/app/gui/src/controller/ide/plain.rs +++ b/app/gui/src/controller/ide/plain.rs @@ -38,8 +38,9 @@ pub struct ProjectOperationsNotSupported; #[derive(Clone, CloneRef, Debug)] pub struct Handle { pub status_notifications: StatusNotificationPublisher, - pub parser: Parser, - pub project: model::Project, + pub parser: Parser, + pub project: model::Project, + component_browser_private_entries_visibility_flag: Rc>, } impl Handle { @@ -47,7 +48,13 @@ impl Handle { pub fn new(project: model::Project) -> Self { let status_notifications = default(); let parser = Parser::new(); - Self { status_notifications, parser, project } + let component_browser_private_entries_visibility_flag = default(); + Self { + status_notifications, + parser, + project, + component_browser_private_entries_visibility_flag, + } } /// Create IDE Controller from Language Server endpoints, describing the opened project. @@ -74,7 +81,13 @@ impl Handle { .await?; let status_notifications = default(); let parser = Parser::new(); - Ok(Self { status_notifications, parser, project }) + let component_browser_private_entries_visibility_flag = default(); + Ok(Self { + status_notifications, + parser, + project, + component_browser_private_entries_visibility_flag, + }) } } @@ -96,4 +109,15 @@ impl controller::ide::API for Handle { fn manage_projects(&self) -> FallibleResult<&dyn ManagingProjectAPI> { Err(ProjectOperationsNotSupported.into()) } + + fn are_component_browser_private_entries_visible(&self) -> bool { + self.component_browser_private_entries_visibility_flag.get() + } + + fn set_component_browser_private_entries_visibility(&self, visibility: bool) { + debug!( + "Setting the visibility of private entries in the component browser to {visibility}." + ); + self.component_browser_private_entries_visibility_flag.set(visibility); + } } diff --git a/app/gui/src/controller/searcher.rs b/app/gui/src/controller/searcher.rs index 5c198bcb896..ad51bdd2a2c 100644 --- a/app/gui/src/controller/searcher.rs +++ b/app/gui/src/controller/searcher.rs @@ -1140,7 +1140,7 @@ impl Searcher { /// The current list will be set as "Loading" and Language Server will be requested for a new /// list - once it be retrieved, the new list will be set and notification will be emitted. #[profile(Debug)] - fn reload_list(&self) { + pub fn reload_list(&self) { let this_type = self.this_arg_type_for_next_completion(); let return_types = match self.data.borrow().input.next_completion_id() { CompletedFragmentId::Function => vec![], @@ -1222,10 +1222,12 @@ impl Searcher { info!("Received suggestions from Language Server."); let list = this.make_action_list(responses.iter()); let mut data = this.data.borrow_mut(); + list.update_filtering(&data.input.pattern); data.actions = Actions::Loaded { list: Rc::new(list) }; let completions = responses.iter().flat_map(|r| r.results.iter().cloned()); data.components = this.make_component_list(completions, &this_type, &return_types); + data.components.update_filtering(&data.input.pattern); } Err(err) => { let msg = "Request for completions to the Language Server returned error"; @@ -1234,6 +1236,7 @@ impl Searcher { data.actions = Actions::Error(Rc::new(err.into())); data.components = this.make_component_list(this.database.keys(), &this_type, &return_types); + data.components.update_filtering(&data.input.pattern); } } this.notifier.publish(Notification::NewActionList).await; @@ -1300,6 +1303,7 @@ impl Searcher { &self.database, module_name.as_ref(), &*favorites, + self.ide.are_component_browser_private_entries_visible(), ); add_virtual_entries_to_builder(&mut builder, this_type, return_types); builder.extend_list_and_allow_favorites_with_ids(&self.database, entry_ids); @@ -1446,8 +1450,13 @@ fn component_list_builder_with_favorites<'a>( suggestion_db: &model::SuggestionDatabase, local_scope_module: QualifiedNameRef, groups: impl IntoIterator, + private_entries_visibile: bool, ) -> component::builder::List { - let mut builder = component::builder::List::new(); + let mut builder = if private_entries_visibile { + component::builder::List::new_with_private_components() + } else { + component::builder::List::new() + }; if let Some((id, _)) = suggestion_db.lookup_by_qualified_name(local_scope_module) { builder = builder.with_local_scope_module_id(id); } @@ -1783,6 +1792,7 @@ pub mod test { ide.expect_current_project().returning_st(move || Some(current_project.clone_ref())); ide.expect_manage_projects() .returning_st(move || Err(ProjectOperationsNotSupported.into())); + ide.expect_are_component_browser_private_entries_visible().returning_st(|| false); let node_metadata_guard = default(); let breadcrumbs = Breadcrumbs::new(); let searcher = Searcher { diff --git a/app/gui/src/controller/searcher/component/builder.rs b/app/gui/src/controller/searcher/component/builder.rs index dc01cef24dc..fade188cc91 100644 --- a/app/gui/src/controller/searcher/component/builder.rs +++ b/app/gui/src/controller/searcher/component/builder.rs @@ -159,6 +159,7 @@ pub struct List { /// IDs of [`Component`]s allowed in [`component::List::favorites`] if they are also present in /// [`grouping_and_order_of_favorites`]. allowed_favorites: HashSet, + keep_private_components: bool, } impl List { @@ -167,6 +168,12 @@ impl List { default() } + /// Construct List builder without content, do not remove private components when extending + /// list. + pub fn new_with_private_components() -> Self { + List { keep_private_components: true, ..default() } + } + /// Return [`List`] with a new [`local_scope`] with its [`Group::component_id`] field set to /// `module_id`. When the [`extend_list_and_allow_favorites_with_ids`] method is called on the /// returned object, components passed to the method which have their parent module ID equal @@ -191,6 +198,7 @@ impl List { let local_scope_id = self.local_scope.component_id; let id_and_looked_up_entry = |id| Some((id, db.lookup(id).ok()?)); let ids_and_entries = entry_ids.into_iter().filter_map(id_and_looked_up_entry); + let keep_private_components = self.keep_private_components; for (id, entry) in ids_and_entries { self.allowed_favorites.insert(id); let component = Component::new_from_database_entry(id, entry.clone_ref()); @@ -202,7 +210,9 @@ impl List { let in_local_scope = parent_id == local_scope_id && local_scope_id.is_some(); let namespace = &parent_group.qualified_name.project().namespace; let in_local_namespace = namespace == LOCAL_NAMESPACE; - if !component.is_private() || in_local_scope || in_local_namespace { + let keep_private_component = + in_local_scope || in_local_namespace || keep_private_components; + if !component.is_private() || keep_private_component { parent_group.content.entries.borrow_mut().push(component.clone_ref()); component_inserted_somewhere = true; let not_module = entry.kind != Kind::Module; @@ -216,7 +226,8 @@ impl List { let project = flatten_group.project.as_ref(); let in_local_namespace = project.map(|name| name.namespace == LOCAL_NAMESPACE).unwrap_or(false); - if !component.is_private() || in_local_namespace { + let keep_private_component = in_local_namespace || keep_private_components; + if !component.is_private() || keep_private_component { flatten_group.entries.borrow_mut().push(component.clone_ref()); component_inserted_somewhere = true; } diff --git a/app/gui/src/presenter/project.rs b/app/gui/src/presenter/project.rs index 00a49ce0130..d5901b5922b 100644 --- a/app/gui/src/presenter/project.rs +++ b/app/gui/src/presenter/project.rs @@ -194,6 +194,11 @@ impl Model { }) } + fn toggle_component_browser_private_entries_visibility(&self) { + let visibility = self.ide_controller.are_component_browser_private_entries_visible(); + self.ide_controller.set_component_browser_private_entries_visibility(!visibility); + } + fn restore_project_snapshot(&self) { let controller = self.controller.clone_ref(); let breadcrumbs = self.view.graph().model.breadcrumbs.clone_ref(); @@ -362,6 +367,10 @@ impl Project { eval_ view.save_project_snapshot(model.save_project_snapshot()); eval_ view.restore_project_snapshot(model.restore_project_snapshot()); + eval_ view.toggle_component_browser_private_entries_visibility( + model.toggle_component_browser_private_entries_visibility() + ); + eval_ view.execution_context_interrupt(model.execution_context_interrupt()); eval_ view.execution_context_restart(model.execution_context_restart()); diff --git a/app/gui/src/presenter/searcher.rs b/app/gui/src/presenter/searcher.rs index 76ab0194b3b..1d5bf1c20d0 100644 --- a/app/gui/src/presenter/searcher.rs +++ b/app/gui/src/presenter/searcher.rs @@ -346,6 +346,9 @@ impl Searcher { action_list_changed <- source::<()>(); select_entry <- action_list_changed.filter(f_!(model.should_auto_select_first_action())); + + eval_ model.view.toggle_component_browser_private_entries_visibility ( + model.controller.reload_list()); } match model.view.searcher() { @@ -368,7 +371,10 @@ impl Searcher { entry_selected <- grid.active.filter_map(|&s| s?.as_entry_id()); selected_entry_changed <- entry_selected.on_change().constant(()); - grid.unhover_element <+ selected_entry_changed; + grid.unhover_element <+ any2( + &selected_entry_changed, + &model.view.toggle_component_browser_private_entries_visibility, + ); hovered_not_selected <- all_with(&grid.hovered, &grid.active, |h, s| { match (h, s) { (Some(h), Some(s)) => h != s, diff --git a/app/gui/view/component-browser/component-list-panel/src/navigator.rs b/app/gui/view/component-browser/component-list-panel/src/navigator.rs index 7ae0a9b8e7d..64862c1efc9 100644 --- a/app/gui/view/component-browser/component-list-panel/src/navigator.rs +++ b/app/gui/view/component-browser/component-list-panel/src/navigator.rs @@ -228,6 +228,7 @@ impl Navigator { section_count <- set_namespace_section_count.map( |&n: &usize| n + MIN_BOTTOM_BUTTONS_COUNT ); + section_count <- section_count.on_change(); bottom_buttons_shape <- section_count.map(|n| (*n, 1)); bottom_buttons_params <- all2(§ion_count, &style); bottom_buttons_viewport <- bottom_buttons_params.map(get_bottom_buttons_viewport); diff --git a/app/gui/view/src/project.rs b/app/gui/view/src/project.rs index 03a1f8525cb..afc30fb5831 100644 --- a/app/gui/view/src/project.rs +++ b/app/gui/view/src/project.rs @@ -81,6 +81,8 @@ ensogl::define_endpoints! { hide_graph_editor(), /// Simulates a style toggle press event. toggle_style(), + /// Toggles the visibility of private components in the component browser. + toggle_component_browser_private_entries_visibility(), /// Saves a snapshot of the current state of the project to the VCS. save_project_snapshot(), /// Restores the state of the project to the last snapshot saved to the VCS. @@ -767,6 +769,7 @@ impl application::View for View { (Press, "is_searcher_opened", "escape", "close_searcher"), (Press, "project_list_shown", "escape", "hide_project_list"), (Press, "", "cmd alt shift t", "toggle_style"), + (Press, "", "cmd alt p", "toggle_component_browser_private_entries_visibility"), (Press, "", "cmd s", "save_project_snapshot"), (Press, "", "cmd r", "restore_project_snapshot"), (Press, "", "cmd z", "undo"),