lex_core/lex/ast/elements/
paragraph.rs1use super::super::range::{Position, Range};
26use super::super::text_content::TextContent;
27use super::super::traits::{AstNode, Container, Visitor, VisualStructure};
28use super::annotation::Annotation;
29use super::content_item::ContentItem;
30use std::fmt;
31
32#[derive(Debug, Clone, PartialEq)]
34pub struct TextLine {
35 pub content: TextContent,
36 pub location: Range,
37}
38
39impl TextLine {
40 fn default_location() -> Range {
41 Range::new(0..0, Position::new(0, 0), Position::new(0, 0))
42 }
43 pub fn new(content: TextContent) -> Self {
44 Self {
45 content,
46 location: Self::default_location(),
47 }
48 }
49
50 pub fn at(mut self, location: Range) -> Self {
51 self.location = location;
52 self
53 }
54
55 pub fn text(&self) -> &str {
56 self.content.as_string()
57 }
58}
59
60impl AstNode for TextLine {
61 fn node_type(&self) -> &'static str {
62 "TextLine"
63 }
64
65 fn display_label(&self) -> String {
66 let text = self.text();
67 if text.chars().count() > 50 {
68 format!("{}…", text.chars().take(50).collect::<String>())
69 } else {
70 text.to_string()
71 }
72 }
73
74 fn range(&self) -> &Range {
75 &self.location
76 }
77
78 fn accept(&self, visitor: &mut dyn Visitor) {
79 visitor.visit_text_line(self);
80 visitor.leave_text_line(self);
81 }
82}
83
84impl VisualStructure for TextLine {
85 fn is_source_line_node(&self) -> bool {
86 true
87 }
88}
89
90impl fmt::Display for TextLine {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 write!(f, "TextLine('{}')", self.text())
93 }
94}
95
96#[derive(Debug, Clone, PartialEq)]
98pub struct Paragraph {
99 pub lines: Vec<ContentItem>,
101 pub annotations: Vec<Annotation>,
102 pub location: Range,
103}
104
105impl Paragraph {
106 fn default_location() -> Range {
107 Range::new(0..0, Position::new(0, 0), Position::new(0, 0))
108 }
109 pub fn new(lines: Vec<ContentItem>) -> Self {
110 debug_assert!(
111 lines
112 .iter()
113 .all(|item| matches!(item, ContentItem::TextLine(_))),
114 "Paragraph lines must be TextLine items"
115 );
116 Self {
117 lines,
118 annotations: Vec::new(),
119 location: Self::default_location(),
120 }
121 }
122 pub fn from_line(line: String) -> Self {
123 Self {
124 lines: vec![ContentItem::TextLine(TextLine::new(
125 TextContent::from_string(line, None),
126 ))],
127 annotations: Vec::new(),
128 location: Self::default_location(),
129 }
130 }
131 pub fn from_line_at(line: String, location: Range) -> Self {
133 let mut para = Self {
134 lines: vec![ContentItem::TextLine(TextLine::new(
135 TextContent::from_string(line, None),
136 ))],
137 annotations: Vec::new(),
138 location: Self::default_location(),
139 };
140 para = para.at(location);
141 para
142 }
143
144 pub fn at(mut self, location: Range) -> Self {
146 self.location = location.clone();
147 if self.lines.len() == 1 {
151 if let Some(super::content_item::ContentItem::TextLine(text_line)) =
152 self.lines.get_mut(0)
153 {
154 text_line.location = location;
155 }
156 }
157 self
158 }
159 pub fn text(&self) -> String {
160 self.lines
161 .iter()
162 .filter_map(|item| {
163 if let super::content_item::ContentItem::TextLine(tl) = item {
164 Some(tl.text().to_string())
165 } else {
166 None
167 }
168 })
169 .collect::<Vec<_>>()
170 .join("\n")
171 }
172
173 pub fn annotations(&self) -> &[Annotation] {
175 &self.annotations
176 }
177
178 pub fn annotations_mut(&mut self) -> &mut Vec<Annotation> {
180 &mut self.annotations
181 }
182
183 pub fn iter_annotations(&self) -> std::slice::Iter<'_, Annotation> {
185 self.annotations.iter()
186 }
187
188 pub fn iter_annotation_contents(&self) -> impl Iterator<Item = &ContentItem> {
190 self.annotations
191 .iter()
192 .flat_map(|annotation| annotation.children())
193 }
194}
195
196impl AstNode for Paragraph {
197 fn node_type(&self) -> &'static str {
198 "Paragraph"
199 }
200 fn display_label(&self) -> String {
201 format!("{} line(s)", self.lines.len())
202 }
203 fn range(&self) -> &Range {
204 &self.location
205 }
206
207 fn accept(&self, visitor: &mut dyn Visitor) {
208 visitor.visit_paragraph(self);
209 super::super::traits::visit_children(visitor, &self.lines);
211 visitor.leave_paragraph(self);
212 }
213}
214
215impl VisualStructure for Paragraph {
216 fn collapses_with_children(&self) -> bool {
217 true
218 }
219}
220
221impl fmt::Display for Paragraph {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 write!(f, "Paragraph({} lines)", self.lines.len())
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::super::content_item::ContentItem;
230 use super::*;
231
232 #[test]
233 fn test_paragraph_creation() {
234 let para = Paragraph::new(vec![
235 ContentItem::TextLine(TextLine::new(TextContent::from_string(
236 "Hello".to_string(),
237 None,
238 ))),
239 ContentItem::TextLine(TextLine::new(TextContent::from_string(
240 "World".to_string(),
241 None,
242 ))),
243 ]);
244 assert_eq!(para.lines.len(), 2);
245 assert_eq!(para.text(), "Hello\nWorld");
246 }
247
248 #[test]
249 fn test_paragraph() {
250 let location = Range::new(
251 0..0,
252 super::super::super::range::Position::new(0, 0),
253 super::super::super::range::Position::new(0, 5),
254 );
255 let para = Paragraph::from_line("Hello".to_string()).at(location.clone());
256
257 assert_eq!(para.location, location);
258 }
259}