@@ -1864,11 +1864,15 @@ impl BufferSnapshot {
let item_node = mat.nodes_for_capture_index(item_capture_ix).next()?;
let range = item_node.start_byte()..item_node.end_byte();
let mut text = String::new();
+ let mut name_ranges = Vec::new();
let mut highlight_ranges = Vec::new();
for capture in mat.captures {
+ let node_is_name;
if capture.index == name_capture_ix {
+ node_is_name = true;
} else if capture.index == context_capture_ix {
+ node_is_name = false;
} else {
continue;
}
@@ -1877,6 +1881,18 @@ impl BufferSnapshot {
if !text.is_empty() {
text.push(' ');
}
+ if node_is_name {
+ let mut start = text.len();
+ let end = start + range.len();
+
+ // When multiple names are captured, then the matcheable text
+ // includes the whitespace in between the names.
+ if !name_ranges.is_empty() {
+ start -= 1;
+ }
+
+ name_ranges.push(start..end);
+ }
let mut offset = range.start;
chunks.seek(offset);
@@ -1911,6 +1927,7 @@ impl BufferSnapshot {
range: self.anchor_after(range.start)..self.anchor_before(range.end),
text,
highlight_ranges,
+ name_ranges,
})
})
.collect::<Vec<_>>();
@@ -16,44 +16,49 @@ pub struct OutlineItem<T> {
pub range: Range<T>,
pub text: String,
pub highlight_ranges: Vec<(Range<usize>, HighlightStyle)>,
+ pub name_ranges: Vec<Range<usize>>,
}
impl<T> Outline<T> {
pub fn new(items: Vec<OutlineItem<T>>) -> Self {
+ let mut candidates = Vec::new();
let mut path_candidates = Vec::new();
let mut path_candidate_prefixes = Vec::new();
- let mut item_text = String::new();
- let mut stack = Vec::new();
+ let mut path_text = String::new();
+ let mut path_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.depth < path_stack.len() {
+ path_stack.truncate(item.depth);
+ path_text.truncate(path_stack.last().copied().unwrap_or(0));
}
- if !item_text.is_empty() {
- item_text.push(' ');
+ if !path_text.is_empty() {
+ path_text.push(' ');
}
- path_candidate_prefixes.push(item_text.len());
- item_text.push_str(&item.text);
- stack.push(item_text.len());
+ path_candidate_prefixes.push(path_text.len());
+ path_text.push_str(&item.text);
+ path_stack.push(path_text.len());
+
+ let candidate_text = item
+ .name_ranges
+ .iter()
+ .map(|range| &item.text[range.start as usize..range.end as usize])
+ .collect::<String>();
path_candidates.push(StringMatchCandidate {
id,
- string: item_text.clone(),
- char_bag: item_text.as_str().into(),
+ char_bag: path_text.as_str().into(),
+ string: path_text.clone(),
+ });
+ candidates.push(StringMatchCandidate {
+ id,
+ char_bag: candidate_text.as_str().into(),
+ string: candidate_text,
});
}
Self {
- candidates: items
- .iter()
- .enumerate()
- .map(|(id, item)| StringMatchCandidate {
- id,
- char_bag: item.text.as_str().into(),
- string: item.text.clone(),
- })
- .collect(),
+ candidates,
path_candidates,
path_candidate_prefixes,
items,
@@ -93,6 +98,17 @@ impl<T> Outline<T> {
for position in &mut string_match.positions {
*position -= prefix_len;
}
+ } else {
+ let mut name_ranges = outline_match.name_ranges.iter();
+ let mut name_range = name_ranges.next().unwrap();
+ let mut preceding_ranges_len = 0;
+ for position in &mut string_match.positions {
+ while *position >= preceding_ranges_len + name_range.len() as usize {
+ preceding_ranges_len += name_range.len();
+ name_range = name_ranges.next().unwrap();
+ }
+ *position = name_range.start as usize + (*position - preceding_ranges_len);
+ }
}
let insertion_ix = tree_matches.len();
@@ -365,29 +365,34 @@ async fn test_outline(mut cx: gpui::TestAppContext) {
]
);
+ // Without space, we only match on names
assert_eq!(
search(&outline, "oon", &cx).await,
&[
- ("mod module", vec![]), // included as the parent of a match
- ("enum LoginState", vec![]), // included as the parent of a match
- ("LoggingOn", vec![1, 7, 8]), // matches
- ("impl Eq for Person", vec![9, 16, 17]), // matches part of the context
- ("impl Drop for Person", vec![11, 18, 19]), // matches in two disjoint names
+ ("mod module", vec![]), // included as the parent of a match
+ ("enum LoginState", vec![]), // included as the parent of a match
+ ("LoggingOn", vec![1, 7, 8]), // matches
+ ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
]
);
+
assert_eq!(
search(&outline, "dp p", &cx).await,
- &[("impl Drop for Person", vec![5, 8, 9, 14])]
+ &[
+ ("impl Drop for Person", vec![5, 8, 9, 14]),
+ ("fn drop", vec![]),
+ ]
);
assert_eq!(
search(&outline, "dpn", &cx).await,
- &[("impl Drop for Person", vec![5, 8, 19])]
+ &[("impl Drop for Person", vec![5, 14, 19])]
);
assert_eq!(
- search(&outline, "impl", &cx).await,
+ search(&outline, "impl ", &cx).await,
&[
- ("impl Eq for Person", vec![0, 1, 2, 3]),
- ("impl Drop for Person", vec![0, 1, 2, 3])
+ ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
+ ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
+ ("fn drop", vec![]),
]
);