Skip to main content

lex_core/lex/ast/elements/
verbatim_line.rs

1//! Verbatim line element
2//!
3//! A verbatim line represents a single line of verbatim content within a verbatim block.
4//! This is the "lead item" for verbatim blocks, similar to how sessions have titles
5//! and definitions have subjects.
6//!
7//! The verbatim line handles the indentation wall - stripping the common indentation
8//! from all content lines to preserve content integrity regardless of nesting level.
9//!
10//! Structure:
11//! - content: The raw text content of the verbatim line
12//! - location: The byte range and position information
13//!
14//! Note: Verbatim lines are typically collected as children of a VerbatimBlock, but
15//! a verbatim block can forgo content entirely (e.g., for binary markers).
16
17use super::super::range::{Position, Range};
18use super::super::text_content::TextContent;
19use super::super::traits::AstNode;
20use super::super::traits::Visitor;
21use super::super::traits::VisualStructure;
22use std::fmt;
23
24/// A verbatim line represents a single line of verbatim content
25#[derive(Debug, Clone, PartialEq)]
26pub struct VerbatimLine {
27    pub content: TextContent,
28    pub location: Range,
29}
30
31impl VerbatimLine {
32    fn default_location() -> Range {
33        Range::new(0..0, Position::new(0, 0), Position::new(0, 0))
34    }
35
36    pub fn new(content: String) -> Self {
37        Self {
38            content: TextContent::from_string(content, None),
39            location: Self::default_location(),
40        }
41    }
42
43    pub fn from_text_content(content: TextContent) -> Self {
44        Self {
45            content,
46            location: Self::default_location(),
47        }
48    }
49
50    /// Preferred builder
51    pub fn at(mut self, location: Range) -> Self {
52        self.location = location;
53        self
54    }
55}
56
57impl AstNode for VerbatimLine {
58    fn node_type(&self) -> &'static str {
59        "VerbatimLine"
60    }
61
62    fn display_label(&self) -> String {
63        let content_text = self.content.as_string();
64        if content_text.chars().count() > 50 {
65            format!("{}…", content_text.chars().take(50).collect::<String>())
66        } else {
67            content_text.to_string()
68        }
69    }
70
71    fn range(&self) -> &Range {
72        &self.location
73    }
74
75    fn accept(&self, visitor: &mut dyn Visitor) {
76        visitor.visit_verbatim_line(self);
77        // VerbatimLine has no children - it's a leaf node
78        visitor.leave_verbatim_line(self);
79    }
80}
81
82impl VisualStructure for VerbatimLine {
83    fn is_source_line_node(&self) -> bool {
84        true
85    }
86}
87
88impl fmt::Display for VerbatimLine {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "VerbatimLine({} chars)", self.content.as_string().len())
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_verbatim_line_creation() {
100        let line = VerbatimLine::new("    code line".to_string());
101        assert_eq!(line.content.as_string(), "    code line");
102    }
103
104    #[test]
105    fn test_verbatim_line_with_location() {
106        let location = Range::new(0..12, Position::new(1, 0), Position::new(1, 12));
107        let line = VerbatimLine::new("    code line".to_string()).at(location.clone());
108        assert_eq!(line.location, location);
109    }
110}