2022-01-13 14:10:29 +00:00
|
|
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
2022-01-13 22:46:15 +00:00
|
|
|
use gpui::{executor::Background, fonts::HighlightStyle};
|
2022-01-13 22:04:25 +00:00
|
|
|
use std::{ops::Range, sync::Arc};
|
2022-01-13 14:10:29 +00:00
|
|
|
|
2022-01-13 02:17:19 +00:00
|
|
|
#[derive(Debug)]
|
2022-01-13 17:24:00 +00:00
|
|
|
pub struct Outline<T> {
|
|
|
|
pub items: Vec<OutlineItem<T>>,
|
2022-01-13 14:10:29 +00:00
|
|
|
candidates: Vec<StringMatchCandidate>,
|
2022-01-14 16:02:04 +00:00
|
|
|
path_candidates: Vec<StringMatchCandidate>,
|
|
|
|
path_candidate_prefixes: Vec<usize>,
|
2022-01-13 14:10:29 +00:00
|
|
|
}
|
2022-01-13 02:17:19 +00:00
|
|
|
|
2022-01-13 11:01:11 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2022-01-13 17:24:00 +00:00
|
|
|
pub struct OutlineItem<T> {
|
2022-01-13 10:35:43 +00:00
|
|
|
pub depth: usize,
|
2022-01-13 17:24:00 +00:00
|
|
|
pub range: Range<T>,
|
2022-01-13 02:17:19 +00:00
|
|
|
pub text: String,
|
2022-01-14 02:09:54 +00:00
|
|
|
pub highlight_ranges: Vec<(Range<usize>, HighlightStyle)>,
|
2022-01-13 02:17:19 +00:00
|
|
|
}
|
2022-01-13 14:10:29 +00:00
|
|
|
|
2022-01-13 17:24:00 +00:00
|
|
|
impl<T> Outline<T> {
|
|
|
|
pub fn new(items: Vec<OutlineItem<T>>) -> Self {
|
2022-01-14 16:02:04 +00:00
|
|
|
let mut path_candidates = Vec::new();
|
|
|
|
let mut path_candidate_prefixes = Vec::new();
|
|
|
|
let mut item_text = String::new();
|
|
|
|
let mut stack = Vec::new();
|
|
|
|
|
|
|
|
for (id, item) in items.iter().enumerate() {
|
|
|
|
if item.depth < stack.len() {
|
|
|
|
stack.truncate(item.depth);
|
|
|
|
item_text.truncate(stack.last().copied().unwrap_or(0));
|
|
|
|
}
|
|
|
|
if !item_text.is_empty() {
|
|
|
|
item_text.push(' ');
|
|
|
|
}
|
|
|
|
path_candidate_prefixes.push(item_text.len());
|
|
|
|
item_text.push_str(&item.text);
|
|
|
|
stack.push(item_text.len());
|
|
|
|
|
|
|
|
path_candidates.push(StringMatchCandidate {
|
|
|
|
id,
|
|
|
|
string: item_text.clone(),
|
|
|
|
char_bag: item_text.as_str().into(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-01-13 14:10:29 +00:00
|
|
|
Self {
|
|
|
|
candidates: items
|
|
|
|
.iter()
|
2022-01-14 13:55:03 +00:00
|
|
|
.enumerate()
|
|
|
|
.map(|(id, item)| StringMatchCandidate {
|
|
|
|
id,
|
2022-01-14 10:09:02 +00:00
|
|
|
char_bag: item.text.as_str().into(),
|
|
|
|
string: item.text.clone(),
|
2022-01-13 14:10:29 +00:00
|
|
|
})
|
|
|
|
.collect(),
|
2022-01-14 16:02:04 +00:00
|
|
|
path_candidates,
|
|
|
|
path_candidate_prefixes,
|
2022-01-13 14:10:29 +00:00
|
|
|
items,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 22:04:25 +00:00
|
|
|
pub async fn search(&self, query: &str, executor: Arc<Background>) -> Vec<StringMatch> {
|
2022-01-14 16:02:04 +00:00
|
|
|
let query = query.trim_start();
|
|
|
|
let is_path_query = query.contains(' ');
|
2022-01-13 22:04:25 +00:00
|
|
|
let mut matches = fuzzy::match_strings(
|
2022-01-14 16:02:04 +00:00
|
|
|
if is_path_query {
|
|
|
|
&self.path_candidates
|
|
|
|
} else {
|
|
|
|
&self.candidates
|
|
|
|
},
|
2022-01-13 14:10:29 +00:00
|
|
|
query,
|
|
|
|
true,
|
|
|
|
100,
|
|
|
|
&Default::default(),
|
2022-01-14 13:55:03 +00:00
|
|
|
executor.clone(),
|
2022-01-13 22:04:25 +00:00
|
|
|
)
|
|
|
|
.await;
|
2022-01-14 13:55:03 +00:00
|
|
|
matches.sort_unstable_by_key(|m| m.candidate_id);
|
2022-01-13 14:10:29 +00:00
|
|
|
|
|
|
|
let mut tree_matches = Vec::new();
|
|
|
|
|
|
|
|
let mut prev_item_ix = 0;
|
2022-01-14 16:02:04 +00:00
|
|
|
for mut string_match in matches {
|
2022-01-14 13:55:03 +00:00
|
|
|
let outline_match = &self.items[string_match.candidate_id];
|
2022-01-14 16:02:04 +00:00
|
|
|
|
|
|
|
if is_path_query {
|
|
|
|
let prefix_len = self.path_candidate_prefixes[string_match.candidate_id];
|
|
|
|
string_match
|
|
|
|
.positions
|
|
|
|
.retain(|position| *position >= prefix_len);
|
|
|
|
for position in &mut string_match.positions {
|
|
|
|
*position -= prefix_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 08:12:30 +00:00
|
|
|
let insertion_ix = tree_matches.len();
|
2022-01-13 15:59:52 +00:00
|
|
|
let mut cur_depth = outline_match.depth;
|
2022-01-14 13:55:03 +00:00
|
|
|
for (ix, item) in self.items[prev_item_ix..string_match.candidate_id]
|
2022-01-13 14:10:29 +00:00
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2022-01-13 15:59:52 +00:00
|
|
|
.rev()
|
2022-01-13 14:10:29 +00:00
|
|
|
{
|
2022-01-13 15:59:52 +00:00
|
|
|
if cur_depth == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-01-13 14:10:29 +00:00
|
|
|
let candidate_index = ix + prev_item_ix;
|
2022-01-13 15:59:52 +00:00
|
|
|
if item.depth == cur_depth - 1 {
|
2022-01-14 08:12:30 +00:00
|
|
|
tree_matches.insert(
|
|
|
|
insertion_ix,
|
|
|
|
StringMatch {
|
2022-01-14 13:55:03 +00:00
|
|
|
candidate_id: candidate_index,
|
2022-01-14 08:12:30 +00:00
|
|
|
score: Default::default(),
|
|
|
|
positions: Default::default(),
|
|
|
|
string: Default::default(),
|
|
|
|
},
|
|
|
|
);
|
2022-01-13 15:59:52 +00:00
|
|
|
cur_depth -= 1;
|
2022-01-13 14:10:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 13:55:03 +00:00
|
|
|
prev_item_ix = string_match.candidate_id + 1;
|
2022-01-13 14:10:29 +00:00
|
|
|
tree_matches.push(string_match);
|
|
|
|
}
|
|
|
|
|
|
|
|
tree_matches
|
|
|
|
}
|
|
|
|
}
|