diff --git a/crates/language/src/outline.rs b/crates/language/src/outline.rs index ea0c5a1f6b..1b4fad56fd 100644 --- a/crates/language/src/outline.rs +++ b/crates/language/src/outline.rs @@ -6,6 +6,8 @@ use std::{ops::Range, sync::Arc}; pub struct Outline { pub items: Vec>, candidates: Vec, + path_candidates: Vec, + path_candidate_prefixes: Vec, } #[derive(Clone, Debug)] @@ -18,6 +20,30 @@ pub struct OutlineItem { impl Outline { pub fn new(items: Vec>) -> Self { + 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(), + }); + } + Self { candidates: items .iter() @@ -28,13 +54,21 @@ impl Outline { string: item.text.clone(), }) .collect(), + path_candidates, + path_candidate_prefixes, items, } } pub async fn search(&self, query: &str, executor: Arc) -> Vec { + let query = query.trim_start(); + let is_path_query = query.contains(' '); let mut matches = fuzzy::match_strings( - &self.candidates, + if is_path_query { + &self.path_candidates + } else { + &self.candidates + }, query, true, 100, @@ -47,8 +81,19 @@ impl Outline { let mut tree_matches = Vec::new(); let mut prev_item_ix = 0; - for string_match in matches { + for mut string_match in matches { let outline_match = &self.items[string_match.candidate_id]; + + 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; + } + } + let insertion_ix = tree_matches.len(); let mut cur_depth = outline_match.depth; for (ix, item) in self.items[prev_item_ix..string_match.candidate_id]