Show default per-kind icons for all entries in Component Browser. (#3587)

Show default icons for all entries in the Component Browser. The icons are assigned to each entry depending on its kind.

https://www.pivotaltracker.com/story/show/182584326

#### Visuals

See below for a video showing entries of 5 different kinds in the Component Browser, each having a different icon. When watching the video, please note that the following are preexisting, known issues, not introduced by this PR:
- Selection is misaligned when hovering the mouse over the "new" component in the "Mcdbg Group 1" group - reported as issue 2 [in comments to PR 3530](https://github.com/enso-org/enso/pull/3530#pullrequestreview-1034223437).
- [Names of Modules and Atoms displayed in Component Browser start with a small letter.](https://www.pivotaltracker.com/story/show/182745386)




https://user-images.githubusercontent.com/273837/179016109-c3ebab5a-0205-4b44-85b8-df3129edd75d.mov

# Important Notes
- A new derive macro `ForEachVariant` is defined and added to the `enso-prelude` crate.
This commit is contained in:
Mateusz Czapliński 2022-07-22 01:57:41 +02:00 committed by GitHub
parent f699a64c33
commit 07df7fabf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 116 additions and 3 deletions

View File

@ -132,7 +132,7 @@ impl<'a> IntoIterator for &'a QualifiedName {
// ============= // =============
/// A type of suggestion entry. /// A type of suggestion entry.
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq, ForEachVariant)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum Kind { pub enum Kind {
Atom, Atom,

View File

@ -157,6 +157,12 @@ impl ide_view::searcher::DocumentationProvider for Action {
} }
} }
// ===========================
// === provider::Component ===
// ===========================
/// Component Provider getting entries from a [`controller::searcher::component::Group`]. /// Component Provider getting entries from a [`controller::searcher::component::Group`].
#[derive(Clone, CloneRef, Debug)] #[derive(Clone, CloneRef, Debug)]
pub struct Component { pub struct Component {
@ -170,23 +176,40 @@ impl Component {
} }
} }
macro_rules! kind_to_icon {
([ $( $variant:ident ),* ] $kind:ident) => {
{
use component_group_view::icon::Id;
use suggestion_database::entry::Kind;
match $kind {
$( Kind::$variant => Id::$variant, )*
}
}
}
}
impl list_view::entry::ModelProvider<component_group_view::Entry> for Component { impl list_view::entry::ModelProvider<component_group_view::Entry> for Component {
fn entry_count(&self) -> usize { fn entry_count(&self) -> usize {
self.group.matched_items.get() self.group.matched_items.get()
} }
fn get(&self, id: usize) -> Option<component_group_view::entry::Model> { fn get(&self, id: usize) -> Option<component_group_view::entry::Model> {
use suggestion_database::entry::for_each_kind_variant;
let component = self.group.get_entry(id)?; let component = self.group.get_entry(id)?;
let match_info = component.match_info.borrow(); let match_info = component.match_info.borrow();
let label = component.label(); let label = component.label();
let highlighted = bytes_of_matched_letters(&*match_info, &label); let highlighted = bytes_of_matched_letters(&*match_info, &label);
let kind = component.suggestion.kind;
Some(component_group_view::entry::Model { Some(component_group_view::entry::Model {
icon: component_group_view::icon::Id::AddColumn, icon: for_each_kind_variant!(kind_to_icon(kind)),
highlighted_text: list_view::entry::GlyphHighlightedLabelModel { label, highlighted }, highlighted_text: list_view::entry::GlyphHighlightedLabelModel { label, highlighted },
}) })
} }
} }
// === Component Provider helpers ===
fn bytes_of_matched_letters(match_info: &MatchInfo, label: &str) -> Vec<text::Range<text::Bytes>> { fn bytes_of_matched_letters(match_info: &MatchInfo, label: &str) -> Vec<text::Range<text::Bytes>> {
if let MatchInfo::Matches { subsequence } = match_info { if let MatchInfo::Matches { subsequence } = match_info {
let mut char_iter = label.char_indices().enumerate(); let mut char_iter = label.char_indices().enumerate();
@ -211,6 +234,12 @@ fn bytes_of_matched_letters(match_info: &MatchInfo, label: &str) -> Vec<text::Ra
} }
} }
// ===========================
// === Converter functions ===
// ===========================
/// Get [`LabeledAnyModelProvider`] for given component group. /// Get [`LabeledAnyModelProvider`] for given component group.
pub fn from_component_group( pub fn from_component_group(
group: &controller::searcher::component::Group, group: &controller::searcher::component::Group,

View File

@ -1,4 +1,11 @@
//! This macro defines set of common macros which are useful across different projects. //! This module defines set of common macros which are useful across different projects.
// ==============
// === Export ===
// ==============
pub use enso_shapely::ForEachVariant;

View File

@ -0,0 +1,44 @@
//! This module contains the [`derive`] function (implementing the [`crate::ForEachVariant`] derive
//! macro) as well as its helper functions.
use inflector::cases::snakecase::to_snake_case;
use proc_macro2::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::Token;
// ======================
// === ForEachVariant ===
// ======================
/// Implementation of the `ForEachVariant` derive macro. For details, see the documentation of the
/// [`crate::derive_for_each_variant`] function.
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let decl = syn::parse_macro_input!(input as syn::DeriveInput);
let ret = match decl.data {
syn::Data::Enum(ref e) => derive_for_enum(&decl, e),
_ => panic!("The `ForEachVariant` derive macro only works on enums."),
};
proc_macro::TokenStream::from(ret)
}
fn derive_for_enum(decl: &syn::DeriveInput, data: &syn::DataEnum) -> TokenStream {
let enum_name = &decl.ident;
let enum_snake_name = to_snake_case(&enum_name.to_string());
let macro_name = quote::format_ident!("for_each_{}_variant", enum_snake_name);
let variant_names: Punctuated<_, Token![,]> = data.variants.iter().map(|v| &v.ident).collect();
quote! {
/// Calls `f!` passing to it a comma-separated list of names of variants of [`#enum_name`]
/// enclosed in square brackets. The extra `args` are passed to `f!` verbatim after the
/// closing square bracket. For more details, see the documentation of the
/// [`ForEachVariant`] derive macro.
#[macro_export]
macro_rules! #macro_name {
( $f:ident($( $args:tt )*) ) => { $f!([ #variant_names ] $($args)*) }
}
pub(crate) use #macro_name;
}
}

View File

@ -23,6 +23,7 @@ extern crate proc_macro;
mod derive_clone_ref; mod derive_clone_ref;
mod derive_entry_point; mod derive_entry_point;
mod derive_for_each_variant;
mod derive_iterator; mod derive_iterator;
mod derive_no_clone; mod derive_no_clone;
mod overlappable; mod overlappable;
@ -114,6 +115,38 @@ pub fn derive_no_clone(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
derive_no_clone::derive(input) derive_no_clone::derive(input)
} }
/// Implements the `ForEachVariant` derive macro which creates a helper for iterating over each
/// variant of an enum at compile time. The derive panics if used on non-enum types.
///
/// The derive creates a macro (hereafter called loop-macro) named `for_each_NAME_variant` where
/// `NAME` is replaced with the name of the enum converted to snake case. The loop-macro takes a
/// name of another macro (hereafter called iterator-macro) as an argument followed by a
/// parenthesized list of extra arguments. The loop-macro expands to a call of the iterator-macro
/// with a list of comma-separated names of the enum variants wrapped in square brackets, followed
/// by the extra arguments defined above.
///
/// For example, the following code:
/// ```no_compile
/// #[derive(ForEachVariant)]
/// pub enum FooBar {
/// Foo,
/// Bar,
/// }
/// ```
/// results in the following macro being defined:
/// ```
/// #[macro_export]
/// macro_rules! for_each_foo_bar_variant {
/// ( $f:ident($( $args:tt )*) ) => { $f!([Foo, Bar] $($args)*) }
/// }
///
/// pub(crate) use for_each_foo_bar_variant;
/// ```
#[proc_macro_derive(ForEachVariant)]
pub fn derive_for_each_variant(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
derive_for_each_variant::derive(input)
}
/// Exposes the function as an application entry point. Entry points are alternative application /// Exposes the function as an application entry point. Entry points are alternative application
/// running modes that you can access by adding `?entry=` to the end of the application URL. /// running modes that you can access by adding `?entry=` to the end of the application URL.
#[proc_macro_attribute] #[proc_macro_attribute]