@@ -1667,6 +1667,31 @@ impl BufferSnapshot {
}
pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
+ self.outline_items_containing(0..self.len(), theme)
+ .map(Outline::new)
+ }
+
+ pub fn symbols_containing<T: ToOffset>(
+ &self,
+ position: T,
+ theme: Option<&SyntaxTheme>,
+ ) -> Option<Vec<OutlineItem<Anchor>>> {
+ let position = position.to_offset(&self);
+ let mut items = self.outline_items_containing(position - 1..position + 1, theme)?;
+ let mut prev_depth = None;
+ items.retain(|item| {
+ let result = prev_depth.map_or(true, |prev_depth| item.depth > prev_depth);
+ prev_depth = Some(item.depth);
+ result
+ });
+ Some(items)
+ }
+
+ fn outline_items_containing(
+ &self,
+ range: Range<usize>,
+ theme: Option<&SyntaxTheme>,
+ ) -> Option<Vec<OutlineItem<Anchor>>> {
let tree = self.tree.as_ref()?;
let grammar = self
.language
@@ -1674,6 +1699,7 @@ impl BufferSnapshot {
.and_then(|language| language.grammar.as_ref())?;
let mut cursor = QueryCursorHandle::new();
+ cursor.set_byte_range(range);
let matches = cursor.matches(
&grammar.outline_query,
tree.root_node(),
@@ -1766,12 +1792,7 @@ impl BufferSnapshot {
})
})
.collect::<Vec<_>>();
-
- if items.is_empty() {
- None
- } else {
- Some(Outline::new(items))
- }
+ Some(items)
}
pub fn enclosing_bracket_ranges<T: ToOffset>(
@@ -282,36 +282,6 @@ async fn test_reparse(cx: &mut gpui::TestAppContext) {
#[gpui::test]
async fn test_outline(cx: &mut gpui::TestAppContext) {
- let language = Arc::new(
- rust_lang()
- .with_outline_query(
- r#"
- (struct_item
- "struct" @context
- name: (_) @name) @item
- (enum_item
- "enum" @context
- name: (_) @name) @item
- (enum_variant
- name: (_) @name) @item
- (field_declaration
- name: (_) @name) @item
- (impl_item
- "impl" @context
- trait: (_) @name
- "for" @context
- type: (_) @name) @item
- (function_item
- "fn" @context
- name: (_) @name) @item
- (mod_item
- "mod" @context
- name: (_) @name) @item
- "#,
- )
- .unwrap(),
- );
-
let text = r#"
struct Person {
name: String,
@@ -339,7 +309,8 @@ async fn test_outline(cx: &mut gpui::TestAppContext) {
"#
.unindent();
- let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
+ let buffer =
+ cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
let outline = buffer
.read_with(cx, |buffer, _| buffer.snapshot().outline(None))
.unwrap();
@@ -413,6 +384,93 @@ async fn test_outline(cx: &mut gpui::TestAppContext) {
}
}
+#[gpui::test]
+async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
+ let text = r#"
+ impl Person {
+ fn one() {
+ 1
+ }
+
+ fn two() {
+ 2
+ }fn three() {
+ 3
+ }
+ }
+ "#
+ .unindent();
+
+ let buffer =
+ cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
+ let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
+
+ // point is at the start of an item
+ assert_eq!(
+ symbols_containing(Point::new(1, 4), &snapshot),
+ vec![
+ (
+ "impl Person".to_string(),
+ Point::new(0, 0)..Point::new(10, 1)
+ ),
+ ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
+ ]
+ );
+
+ // point is in the middle of an item
+ assert_eq!(
+ symbols_containing(Point::new(2, 8), &snapshot),
+ vec![
+ (
+ "impl Person".to_string(),
+ Point::new(0, 0)..Point::new(10, 1)
+ ),
+ ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
+ ]
+ );
+
+ // point is at the end of an item
+ assert_eq!(
+ symbols_containing(Point::new(3, 5), &snapshot),
+ vec![
+ (
+ "impl Person".to_string(),
+ Point::new(0, 0)..Point::new(10, 1)
+ ),
+ ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
+ ]
+ );
+
+ // point is in between two adjacent items
+ assert_eq!(
+ symbols_containing(Point::new(7, 5), &snapshot),
+ vec![
+ (
+ "impl Person".to_string(),
+ Point::new(0, 0)..Point::new(10, 1)
+ ),
+ ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
+ ]
+ );
+
+ fn symbols_containing<'a>(
+ position: Point,
+ snapshot: &'a BufferSnapshot,
+ ) -> Vec<(String, Range<Point>)> {
+ snapshot
+ .symbols_containing(position, None)
+ .unwrap()
+ .into_iter()
+ .map(|item| {
+ (
+ item.text,
+ item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
+ )
+ })
+ .collect()
+ }
+}
+
#[gpui::test]
fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
let buffer = cx.add_model(|cx| {
@@ -851,6 +909,35 @@ fn rust_lang() -> Language {
"#,
)
.unwrap()
+ .with_outline_query(
+ r#"
+ (struct_item
+ "struct" @context
+ name: (_) @name) @item
+ (enum_item
+ "enum" @context
+ name: (_) @name) @item
+ (enum_variant
+ name: (_) @name) @item
+ (field_declaration
+ name: (_) @name) @item
+ (impl_item
+ "impl" @context
+ type: (_) @name) @item
+ (impl_item
+ "impl" @context
+ trait: (_) @name
+ "for" @context
+ type: (_) @name) @item
+ (function_item
+ "fn" @context
+ name: (_) @name) @item
+ (mod_item
+ "mod" @context
+ name: (_) @name) @item
+ "#,
+ )
+ .unwrap()
}
fn empty(point: Point) -> Range<Point> {