1use language::{BufferSnapshot, LanguageId, SyntaxMapMatches};
2use std::{cmp::Reverse, ops::Range, sync::Arc};
3
4// TODO:
5//
6// * how to handle multiple name captures? for now last one wins
7//
8// * annotation ranges
9//
10// * new "signature" capture for outline queries
11//
12// * Check parent behavior of "int x, y = 0" declarations in a test
13
14pub struct OutlineDeclaration {
15 pub parent_index: Option<usize>,
16 pub identifier: Identifier,
17 pub item_range: Range<usize>,
18 pub signature_range: Range<usize>,
19}
20
21#[derive(Debug, Clone, Eq, PartialEq, Hash)]
22pub struct Identifier {
23 pub name: Arc<str>,
24 pub language_id: LanguageId,
25}
26
27pub fn declarations_in_buffer(buffer: &BufferSnapshot) -> Vec<OutlineDeclaration> {
28 declarations_overlapping_range(0..buffer.len(), buffer)
29}
30
31pub fn declarations_overlapping_range(
32 range: Range<usize>,
33 buffer: &BufferSnapshot,
34) -> Vec<OutlineDeclaration> {
35 let mut declarations = OutlineIterator::new(range, buffer).collect::<Vec<_>>();
36 declarations.sort_unstable_by_key(|item| (item.item_range.start, Reverse(item.item_range.end)));
37
38 let mut parent_stack: Vec<(usize, Range<usize>)> = Vec::new();
39 for (index, declaration) in declarations.iter_mut().enumerate() {
40 while let Some((top_parent_index, top_parent_range)) = parent_stack.last() {
41 if declaration.item_range.start >= top_parent_range.end {
42 parent_stack.pop();
43 } else {
44 declaration.parent_index = Some(*top_parent_index);
45 break;
46 }
47 }
48 parent_stack.push((index, declaration.item_range.clone()));
49 }
50 declarations
51}
52
53/// Iterates outline items without being ordered w.r.t. nested items and without populating
54/// `parent`.
55pub struct OutlineIterator<'a> {
56 buffer: &'a BufferSnapshot,
57 matches: SyntaxMapMatches<'a>,
58}
59
60impl<'a> OutlineIterator<'a> {
61 pub fn new(range: Range<usize>, buffer: &'a BufferSnapshot) -> Self {
62 let matches = buffer.syntax.matches(range, &buffer.text, |grammar| {
63 grammar.outline_config.as_ref().map(|c| &c.query)
64 });
65
66 Self { buffer, matches }
67 }
68}
69
70impl<'a> Iterator for OutlineIterator<'a> {
71 type Item = OutlineDeclaration;
72
73 fn next(&mut self) -> Option<Self::Item> {
74 while let Some(mat) = self.matches.peek() {
75 let config = self.matches.grammars()[mat.grammar_index]
76 .outline_config
77 .as_ref()
78 .unwrap();
79
80 let mut name_range = None;
81 let mut item_range = None;
82 let mut signature_start = None;
83 let mut signature_end = None;
84
85 let mut add_to_signature = |range: Range<usize>| {
86 if signature_start.is_none() {
87 signature_start = Some(range.start);
88 }
89 signature_end = Some(range.end);
90 };
91
92 for capture in mat.captures {
93 let range = capture.node.byte_range();
94 if capture.index == config.name_capture_ix {
95 name_range = Some(range.clone());
96 add_to_signature(range);
97 } else if Some(capture.index) == config.context_capture_ix
98 || Some(capture.index) == config.extra_context_capture_ix
99 {
100 add_to_signature(range);
101 } else if capture.index == config.item_capture_ix {
102 item_range = Some(range.clone());
103 }
104 }
105
106 let language_id = mat.language.id();
107 self.matches.advance();
108
109 if let Some(name_range) = name_range
110 && let Some(item_range) = item_range
111 && let Some(signature_start) = signature_start
112 && let Some(signature_end) = signature_end
113 {
114 let name = self
115 .buffer
116 .text_for_range(name_range)
117 .collect::<String>()
118 .into();
119
120 return Some(OutlineDeclaration {
121 identifier: Identifier { name, language_id },
122 item_range: item_range,
123 signature_range: signature_start..signature_end,
124 parent_index: None,
125 });
126 }
127 }
128 None
129 }
130}