lex_core/lex/ast/elements/
paragraph.rs1use super::super::range::{Position, Range};
26use super::super::text_content::TextContent;
27use super::super::traits::{AstNode, Container, TextNode, 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 TextNode for Paragraph {
216 fn text(&self) -> String {
217 self.lines
218 .iter()
219 .filter_map(|item| {
220 if let super::content_item::ContentItem::TextLine(tl) = item {
221 Some(tl.text().to_string())
222 } else {
223 None
224 }
225 })
226 .collect::<Vec<_>>()
227 .join("\n")
228 }
229 fn lines(&self) -> &[TextContent] {
230 &[]
233 }
234}
235
236impl VisualStructure for Paragraph {
237 fn collapses_with_children(&self) -> bool {
238 true
239 }
240}
241
242impl fmt::Display for Paragraph {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 write!(f, "Paragraph({} lines)", self.lines.len())
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::super::content_item::ContentItem;
251 use super::*;
252
253 #[test]
254 fn test_paragraph_creation() {
255 let para = Paragraph::new(vec![
256 ContentItem::TextLine(TextLine::new(TextContent::from_string(
257 "Hello".to_string(),
258 None,
259 ))),
260 ContentItem::TextLine(TextLine::new(TextContent::from_string(
261 "World".to_string(),
262 None,
263 ))),
264 ]);
265 assert_eq!(para.lines.len(), 2);
266 assert_eq!(para.text(), "Hello\nWorld");
267 }
268
269 #[test]
270 fn test_paragraph() {
271 let location = Range::new(
272 0..0,
273 super::super::super::range::Position::new(0, 0),
274 super::super::super::range::Position::new(0, 5),
275 );
276 let para = Paragraph::from_line("Hello".to_string()).at(location.clone());
277
278 assert_eq!(para.location, location);
279 }
280}