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 project_entry_id(&self) -> ProjectEntryId {
45 match self {
46 Declaration::File {
47 project_entry_id, ..
48 } => *project_entry_id,
49 Declaration::Buffer {
50 project_entry_id, ..
51 } => *project_entry_id,
52 }
53 }
54
55 pub fn item_text(&self) -> (Cow<'_, str>, bool) {
56 match self {
57 Declaration::File { declaration, .. } => (
58 declaration.text.as_ref().into(),
59 declaration.text_is_truncated,
60 ),
61 Declaration::Buffer {
62 rope, declaration, ..
63 } => (
64 rope.chunks_in_range(declaration.item_range.clone())
65 .collect::<Cow<str>>(),
66 declaration.item_range_is_truncated,
67 ),
68 }
69 }
70
71 pub fn signature_text(&self) -> (Cow<'_, str>, bool) {
72 match self {
73 Declaration::File { declaration, .. } => (
74 declaration.text[declaration.signature_range_in_text.clone()].into(),
75 declaration.signature_is_truncated,
76 ),
77 Declaration::Buffer {
78 rope, declaration, ..
79 } => (
80 rope.chunks_in_range(declaration.signature_range.clone())
81 .collect::<Cow<str>>(),
82 declaration.signature_range_is_truncated,
83 ),
84 }
85 }
86}
87
88fn expand_range_to_line_boundaries_and_truncate(
89 range: &Range<usize>,
90 limit: usize,
91 rope: &Rope,
92) -> (Range<usize>, bool) {
93 let mut point_range = rope.offset_to_point(range.start)..rope.offset_to_point(range.end);
94 point_range.start.column = 0;
95 point_range.end.row += 1;
96 point_range.end.column = 0;
97
98 let mut item_range =
99 rope.point_to_offset(point_range.start)..rope.point_to_offset(point_range.end);
100 let is_truncated = item_range.len() > limit;
101 if is_truncated {
102 item_range.end = item_range.start + limit;
103 }
104 item_range.end = rope.clip_offset(item_range.end, Bias::Left);
105 (item_range, is_truncated)
106}
107
108#[derive(Debug, Clone)]
109pub struct FileDeclaration {
110 pub parent: Option<DeclarationId>,
111 pub identifier: Identifier,
112 /// offset range of the declaration in the file, expanded to line boundaries and truncated
113 pub item_range_in_file: Range<usize>,
114 /// text of `item_range_in_file`
115 pub text: Arc<str>,
116 /// whether `text` was truncated
117 pub text_is_truncated: bool,
118 /// offset range of the signature within `text`
119 pub signature_range_in_text: Range<usize>,
120 /// whether `signature` was truncated
121 pub signature_is_truncated: bool,
122}
123
124impl FileDeclaration {
125 pub fn from_outline(declaration: OutlineDeclaration, rope: &Rope) -> FileDeclaration {
126 let (item_range_in_file, text_is_truncated) = expand_range_to_line_boundaries_and_truncate(
127 &declaration.item_range,
128 ITEM_TEXT_TRUNCATION_LENGTH,
129 rope,
130 );
131
132 // TODO: consider logging if unexpected
133 let signature_start = declaration
134 .signature_range
135 .start
136 .saturating_sub(item_range_in_file.start);
137 let mut signature_end = declaration
138 .signature_range
139 .end
140 .saturating_sub(item_range_in_file.start);
141 let signature_is_truncated = signature_end > item_range_in_file.len();
142 if signature_is_truncated {
143 signature_end = item_range_in_file.len();
144 }
145
146 FileDeclaration {
147 parent: None,
148 identifier: declaration.identifier,
149 signature_range_in_text: signature_start..signature_end,
150 signature_is_truncated,
151 text: rope
152 .chunks_in_range(item_range_in_file.clone())
153 .collect::<String>()
154 .into(),
155 text_is_truncated,
156 item_range_in_file,
157 }
158 }
159}
160
161#[derive(Debug, Clone)]
162pub struct BufferDeclaration {
163 pub parent: Option<DeclarationId>,
164 pub identifier: Identifier,
165 pub item_range: Range<usize>,
166 pub item_range_is_truncated: bool,
167 pub signature_range: Range<usize>,
168 pub signature_range_is_truncated: bool,
169}
170
171impl BufferDeclaration {
172 pub fn from_outline(declaration: OutlineDeclaration, rope: &Rope) -> Self {
173 let (item_range, item_range_is_truncated) = expand_range_to_line_boundaries_and_truncate(
174 &declaration.item_range,
175 ITEM_TEXT_TRUNCATION_LENGTH,
176 rope,
177 );
178 let (signature_range, signature_range_is_truncated) =
179 expand_range_to_line_boundaries_and_truncate(
180 &declaration.signature_range,
181 ITEM_TEXT_TRUNCATION_LENGTH,
182 rope,
183 );
184 Self {
185 parent: None,
186 identifier: declaration.identifier,
187 item_range,
188 item_range_is_truncated,
189 signature_range,
190 signature_range_is_truncated,
191 }
192 }
193}