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 as_file(&self) -> Option<&FileDeclaration> {
59 match self {
60 Declaration::Buffer { .. } => None,
61 Declaration::File { declaration, .. } => Some(declaration),
62 }
63 }
64
65 pub fn project_entry_id(&self) -> ProjectEntryId {
66 match self {
67 Declaration::File {
68 project_entry_id, ..
69 } => *project_entry_id,
70 Declaration::Buffer {
71 project_entry_id, ..
72 } => *project_entry_id,
73 }
74 }
75
76 pub fn item_range(&self) -> Range<usize> {
77 match self {
78 Declaration::File { declaration, .. } => declaration.item_range.clone(),
79 Declaration::Buffer { declaration, .. } => declaration.item_range.clone(),
80 }
81 }
82
83 pub fn item_text(&self) -> (Cow<'_, str>, bool) {
84 match self {
85 Declaration::File { declaration, .. } => (
86 declaration.text.as_ref().into(),
87 declaration.text_is_truncated,
88 ),
89 Declaration::Buffer {
90 rope, declaration, ..
91 } => (
92 rope.chunks_in_range(declaration.item_range.clone())
93 .collect::<Cow<str>>(),
94 declaration.item_range_is_truncated,
95 ),
96 }
97 }
98
99 pub fn signature_text(&self) -> (Cow<'_, str>, bool) {
100 match self {
101 Declaration::File { declaration, .. } => (
102 declaration.text[self.signature_range_in_item_text()].into(),
103 declaration.signature_is_truncated,
104 ),
105 Declaration::Buffer {
106 rope, declaration, ..
107 } => (
108 rope.chunks_in_range(declaration.signature_range.clone())
109 .collect::<Cow<str>>(),
110 declaration.signature_range_is_truncated,
111 ),
112 }
113 }
114
115 pub fn signature_range(&self) -> Range<usize> {
116 match self {
117 Declaration::File { declaration, .. } => declaration.signature_range.clone(),
118 Declaration::Buffer { declaration, .. } => declaration.signature_range.clone(),
119 }
120 }
121
122 pub fn signature_range_in_item_text(&self) -> Range<usize> {
123 let signature_range = self.signature_range();
124 let item_range = self.item_range();
125 signature_range.start.saturating_sub(item_range.start)
126 ..(signature_range.end.saturating_sub(item_range.start)).min(item_range.len())
127 }
128}
129
130fn expand_range_to_line_boundaries_and_truncate(
131 range: &Range<usize>,
132 limit: usize,
133 rope: &Rope,
134) -> (Range<usize>, bool) {
135 let mut point_range = rope.offset_to_point(range.start)..rope.offset_to_point(range.end);
136 point_range.start.column = 0;
137 point_range.end.row += 1;
138 point_range.end.column = 0;
139
140 let mut item_range =
141 rope.point_to_offset(point_range.start)..rope.point_to_offset(point_range.end);
142 let is_truncated = item_range.len() > limit;
143 if is_truncated {
144 item_range.end = item_range.start + limit;
145 }
146 item_range.end = rope.clip_offset(item_range.end, Bias::Left);
147 (item_range, is_truncated)
148}
149
150#[derive(Debug, Clone)]
151pub struct FileDeclaration {
152 pub parent: Option<DeclarationId>,
153 pub identifier: Identifier,
154 /// offset range of the declaration in the file, expanded to line boundaries and truncated
155 pub item_range: Range<usize>,
156 /// text of `item_range`
157 pub text: Arc<str>,
158 /// whether `text` was truncated
159 pub text_is_truncated: bool,
160 /// offset range of the signature in the file, expanded to line boundaries and truncated
161 pub signature_range: Range<usize>,
162 /// whether `signature` was truncated
163 pub signature_is_truncated: bool,
164}
165
166impl FileDeclaration {
167 pub fn from_outline(declaration: OutlineDeclaration, rope: &Rope) -> FileDeclaration {
168 let (item_range_in_file, text_is_truncated) = expand_range_to_line_boundaries_and_truncate(
169 &declaration.item_range,
170 ITEM_TEXT_TRUNCATION_LENGTH,
171 rope,
172 );
173
174 let (mut signature_range_in_file, mut signature_is_truncated) =
175 expand_range_to_line_boundaries_and_truncate(
176 &declaration.signature_range,
177 ITEM_TEXT_TRUNCATION_LENGTH,
178 rope,
179 );
180
181 if signature_range_in_file.start < item_range_in_file.start {
182 signature_range_in_file.start = item_range_in_file.start;
183 signature_is_truncated = true;
184 }
185 if signature_range_in_file.end > item_range_in_file.end {
186 signature_range_in_file.end = item_range_in_file.end;
187 signature_is_truncated = true;
188 }
189
190 FileDeclaration {
191 parent: None,
192 identifier: declaration.identifier,
193 signature_range: signature_range_in_file,
194 signature_is_truncated,
195 text: rope
196 .chunks_in_range(item_range_in_file.clone())
197 .collect::<String>()
198 .into(),
199 text_is_truncated,
200 item_range: item_range_in_file,
201 }
202 }
203}
204
205#[derive(Debug, Clone)]
206pub struct BufferDeclaration {
207 pub parent: Option<DeclarationId>,
208 pub identifier: Identifier,
209 pub item_range: Range<usize>,
210 pub item_range_is_truncated: bool,
211 pub signature_range: Range<usize>,
212 pub signature_range_is_truncated: bool,
213}
214
215impl BufferDeclaration {
216 pub fn from_outline(declaration: OutlineDeclaration, rope: &Rope) -> Self {
217 let (item_range, item_range_is_truncated) = expand_range_to_line_boundaries_and_truncate(
218 &declaration.item_range,
219 ITEM_TEXT_TRUNCATION_LENGTH,
220 rope,
221 );
222 let (signature_range, signature_range_is_truncated) =
223 expand_range_to_line_boundaries_and_truncate(
224 &declaration.signature_range,
225 ITEM_TEXT_TRUNCATION_LENGTH,
226 rope,
227 );
228 Self {
229 parent: None,
230 identifier: declaration.identifier,
231 item_range,
232 item_range_is_truncated,
233 signature_range,
234 signature_range_is_truncated,
235 }
236 }
237}