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