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 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}