1use language::LanguageId;
2use project::ProjectEntryId;
3use std::borrow::Cow;
4use std::ops::Range;
5use std::sync::Arc;
6use text::{Bias, BufferId, Rope};
7
8use crate::outline::OutlineDeclaration;
9
10#[derive(Debug, Clone, Eq, PartialEq, Hash)]
11pub struct Identifier {
12 pub name: Arc<str>,
13 pub language_id: LanguageId,
14}
15
16slotmap::new_key_type! {
17 pub struct DeclarationId;
18}
19
20#[derive(Debug, Clone)]
21pub enum Declaration {
22 File {
23 project_entry_id: ProjectEntryId,
24 declaration: FileDeclaration,
25 },
26 Buffer {
27 project_entry_id: ProjectEntryId,
28 buffer_id: BufferId,
29 rope: Rope,
30 declaration: BufferDeclaration,
31 },
32}
33
34const ITEM_TEXT_TRUNCATION_LENGTH: usize = 1024;
35
36impl Declaration {
37 pub fn identifier(&self) -> &Identifier {
38 match self {
39 Declaration::File { declaration, .. } => &declaration.identifier,
40 Declaration::Buffer { declaration, .. } => &declaration.identifier,
41 }
42 }
43
44 pub fn parent(&self) -> Option<DeclarationId> {
45 match self {
46 Declaration::File { declaration, .. } => declaration.parent,
47 Declaration::Buffer { declaration, .. } => declaration.parent,
48 }
49 }
50
51 pub fn as_buffer(&self) -> Option<&BufferDeclaration> {
52 match self {
53 Declaration::File { .. } => None,
54 Declaration::Buffer { declaration, .. } => Some(declaration),
55 }
56 }
57
58 pub fn project_entry_id(&self) -> ProjectEntryId {
59 match self {
60 Declaration::File {
61 project_entry_id, ..
62 } => *project_entry_id,
63 Declaration::Buffer {
64 project_entry_id, ..
65 } => *project_entry_id,
66 }
67 }
68
69 pub fn item_range(&self) -> Range<usize> {
70 match self {
71 Declaration::File { declaration, .. } => declaration.item_range.clone(),
72 Declaration::Buffer { declaration, .. } => declaration.item_range.clone(),
73 }
74 }
75
76 pub fn item_text(&self) -> (Cow<'_, str>, bool) {
77 match self {
78 Declaration::File { declaration, .. } => (
79 declaration.text.as_ref().into(),
80 declaration.text_is_truncated,
81 ),
82 Declaration::Buffer {
83 rope, declaration, ..
84 } => (
85 rope.chunks_in_range(declaration.item_range.clone())
86 .collect::<Cow<str>>(),
87 declaration.item_range_is_truncated,
88 ),
89 }
90 }
91
92 pub fn signature_text(&self) -> (Cow<'_, str>, bool) {
93 match self {
94 Declaration::File { declaration, .. } => (
95 declaration.text[self.signature_range_in_item_text()].into(),
96 declaration.signature_is_truncated,
97 ),
98 Declaration::Buffer {
99 rope, declaration, ..
100 } => (
101 rope.chunks_in_range(declaration.signature_range.clone())
102 .collect::<Cow<str>>(),
103 declaration.signature_range_is_truncated,
104 ),
105 }
106 }
107
108 pub fn signature_range(&self) -> Range<usize> {
109 match self {
110 Declaration::File { declaration, .. } => declaration.signature_range.clone(),
111 Declaration::Buffer { declaration, .. } => declaration.signature_range.clone(),
112 }
113 }
114
115 pub fn signature_range_in_item_text(&self) -> Range<usize> {
116 let signature_range = self.signature_range();
117 let item_range = self.item_range();
118 signature_range.start.saturating_sub(item_range.start)
119 ..(signature_range.end.saturating_sub(item_range.start)).min(item_range.len())
120 }
121}
122
123fn expand_range_to_line_boundaries_and_truncate(
124 range: &Range<usize>,
125 limit: usize,
126 rope: &Rope,
127) -> (Range<usize>, bool) {
128 let mut point_range = rope.offset_to_point(range.start)..rope.offset_to_point(range.end);
129 point_range.start.column = 0;
130 point_range.end.row += 1;
131 point_range.end.column = 0;
132
133 let mut item_range =
134 rope.point_to_offset(point_range.start)..rope.point_to_offset(point_range.end);
135 let is_truncated = item_range.len() > limit;
136 if is_truncated {
137 item_range.end = item_range.start + limit;
138 }
139 item_range.end = rope.clip_offset(item_range.end, Bias::Left);
140 (item_range, is_truncated)
141}
142
143#[derive(Debug, Clone)]
144pub struct FileDeclaration {
145 pub parent: Option<DeclarationId>,
146 pub identifier: Identifier,
147 /// offset range of the declaration in the file, expanded to line boundaries and truncated
148 pub item_range: Range<usize>,
149 /// text of `item_range`
150 pub text: Arc<str>,
151 /// whether `text` was truncated
152 pub text_is_truncated: bool,
153 /// offset range of the signature in the file, expanded to line boundaries and truncated
154 pub signature_range: Range<usize>,
155 /// whether `signature` was truncated
156 pub signature_is_truncated: bool,
157}
158
159impl FileDeclaration {
160 pub fn from_outline(declaration: OutlineDeclaration, rope: &Rope) -> FileDeclaration {
161 let (item_range_in_file, text_is_truncated) = expand_range_to_line_boundaries_and_truncate(
162 &declaration.item_range,
163 ITEM_TEXT_TRUNCATION_LENGTH,
164 rope,
165 );
166
167 let (mut signature_range_in_file, mut signature_is_truncated) =
168 expand_range_to_line_boundaries_and_truncate(
169 &declaration.signature_range,
170 ITEM_TEXT_TRUNCATION_LENGTH,
171 rope,
172 );
173
174 if signature_range_in_file.start < item_range_in_file.start {
175 signature_range_in_file.start = item_range_in_file.start;
176 signature_is_truncated = true;
177 }
178 if signature_range_in_file.end > item_range_in_file.end {
179 signature_range_in_file.end = item_range_in_file.end;
180 signature_is_truncated = true;
181 }
182
183 FileDeclaration {
184 parent: None,
185 identifier: declaration.identifier,
186 signature_range: signature_range_in_file,
187 signature_is_truncated,
188 text: rope
189 .chunks_in_range(item_range_in_file.clone())
190 .collect::<String>()
191 .into(),
192 text_is_truncated,
193 item_range: item_range_in_file,
194 }
195 }
196}
197
198#[derive(Debug, Clone)]
199pub struct BufferDeclaration {
200 pub parent: Option<DeclarationId>,
201 pub identifier: Identifier,
202 pub item_range: Range<usize>,
203 pub item_range_is_truncated: bool,
204 pub signature_range: Range<usize>,
205 pub signature_range_is_truncated: bool,
206}
207
208impl BufferDeclaration {
209 pub fn from_outline(declaration: OutlineDeclaration, rope: &Rope) -> Self {
210 let (item_range, item_range_is_truncated) = expand_range_to_line_boundaries_and_truncate(
211 &declaration.item_range,
212 ITEM_TEXT_TRUNCATION_LENGTH,
213 rope,
214 );
215 let (signature_range, signature_range_is_truncated) =
216 expand_range_to_line_boundaries_and_truncate(
217 &declaration.signature_range,
218 ITEM_TEXT_TRUNCATION_LENGTH,
219 rope,
220 );
221 Self {
222 parent: None,
223 identifier: declaration.identifier,
224 item_range,
225 item_range_is_truncated,
226 signature_range,
227 signature_range_is_truncated,
228 }
229 }
230}