Improve searching of component browser entries (#5645)

Closes #5102

This PR improves searching entries in the component browser. Now the searcher input is also matched to the code that a component would generate, and the best match of the two is used for filtering and sorting the components in the component browser.

https://user-images.githubusercontent.com/117099775/219328904-c7a067d5-4998-4ee5-8475-d4974cd7bff5.mp4

#### Entry name formatting

Additionally, the component entry's displayed name format is changed to show the method's name first, followed by the type name in parentheses. This formatting fits better in the narrow columns of the component browser.
This commit is contained in:
Galin Bajlekov 2023-02-17 00:21:37 +01:00 committed by GitHub
parent f53696eda4
commit b771728701
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 4 deletions

View File

@ -92,6 +92,9 @@
- [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.
- [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.
#### EnsoGL (rendering engine)
@ -473,6 +476,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
[5645]: https://github.com/enso-org/enso/pull/5645
[5646]: https://github.com/enso-org/enso/pull/5646
#### Enso Compiler

View File

@ -152,12 +152,36 @@ impl Component {
///
/// It should be called each time the filtering pattern changes.
pub fn update_matching_info(&self, pattern: impl Str) {
// Match the input pattern to the component label.
let label = self.label();
let matches = fuzzly::matches(&label, pattern.as_ref());
let subsequence = matches.and_option_from(|| {
let label_matches = fuzzly::matches(&label, pattern.as_ref());
let label_subsequence = label_matches.and_option_from(|| {
let metric = fuzzly::metric::default();
fuzzly::find_best_subsequence(label, pattern, metric)
fuzzly::find_best_subsequence(label, pattern.as_ref(), metric)
});
// Match the input pattern to the code to be inserted.
let code = match &self.data {
Data::FromDatabase { entry, .. } => entry.code_to_insert(true).to_string(),
Data::Virtual { snippet } => snippet.code.to_string(),
};
let code_matches = fuzzly::matches(&code, pattern.as_ref());
let code_subsequence = code_matches.and_option_from(|| {
let metric = fuzzly::metric::default();
fuzzly::find_best_subsequence(code, pattern.as_ref(), metric)
});
// Pick the best match score of the two, use only the character indices matching the label.
let subsequence = match (label_subsequence, code_subsequence) {
(Some(label), Some(code)) => {
let score = label.score.max(code.score);
Some(fuzzly::Subsequence { score, ..label })
}
(None, Some(code)) => Some(fuzzly::Subsequence { indices: Vec::new(), ..code }),
(Some(label), None) => Some(label),
(None, None) => None,
};
*self.match_info.borrow_mut() = match subsequence {
Some(subsequence) => MatchInfo::Matches { subsequence },
None => MatchInfo::DoesNotMatch,
@ -192,7 +216,7 @@ impl Display for Component {
let self_type_not_here = self_type_ref.filter(|t| *t != &entry.defined_in);
if let Some(self_type) = self_type_not_here {
let self_name = self_type.name().from_case(Case::Snake).to_case(Case::Title);
write!(f, "{self_name} {entry_name}")
write!(f, "{entry_name} ({self_name})")
} else {
write!(f, "{entry_name}")
}