declaration.rs

  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}