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