panache_parser/parser/utils/
list_item_buffer.rs1use crate::config::Config;
7use crate::parser::blocks::headings::{emit_atx_heading, try_parse_atx_heading};
8use crate::parser::utils::inline_emission;
9use crate::syntax::SyntaxKind;
10use rowan::GreenNodeBuilder;
11
12#[derive(Debug, Clone)]
14pub(crate) enum ListItemContent {
15 Text(String),
17}
18
19#[derive(Debug, Default, Clone)]
27pub(crate) struct ListItemBuffer {
28 segments: Vec<ListItemContent>,
30}
31
32impl ListItemBuffer {
33 pub(crate) fn new() -> Self {
35 Self {
36 segments: Vec::new(),
37 }
38 }
39
40 pub(crate) fn push_text(&mut self, text: impl Into<String>) {
42 let text = text.into();
43 if text.is_empty() {
44 return;
45 }
46 self.segments.push(ListItemContent::Text(text));
47 }
48
49 pub(crate) fn is_empty(&self) -> bool {
51 self.segments.is_empty()
52 }
53
54 pub(crate) fn segment_count(&self) -> usize {
56 self.segments.len()
57 }
58
59 pub(crate) fn has_blank_lines_between_content(&self) -> bool {
64 log::trace!(
65 "has_blank_lines_between_content: segments={} result=false",
66 self.segments.len()
67 );
68
69 false
70 }
71
72 fn get_text_for_parsing(&self) -> String {
74 let mut result = String::new();
75 for segment in &self.segments {
76 let ListItemContent::Text(text) = segment;
77 result.push_str(text);
78 }
79 result
80 }
81
82 pub(crate) fn emit_as_block(
87 &self,
88 builder: &mut GreenNodeBuilder<'static>,
89 use_paragraph: bool,
90 config: &Config,
91 ) {
92 if self.is_empty() {
93 return;
94 }
95
96 let text = self.get_text_for_parsing();
98
99 if !text.is_empty() {
100 let line_without_newline = text
101 .strip_suffix("\r\n")
102 .or_else(|| text.strip_suffix('\n'));
103 if let Some(line) = line_without_newline
104 && !line.contains('\n')
105 && !line.contains('\r')
106 && let Some(level) = try_parse_atx_heading(line)
107 {
108 emit_atx_heading(builder, &text, level, config);
109 return;
110 }
111 }
112
113 let block_kind = if use_paragraph {
114 SyntaxKind::PARAGRAPH
115 } else {
116 SyntaxKind::PLAIN
117 };
118
119 builder.start_node(block_kind.into());
120
121 if !text.is_empty() {
122 inline_emission::emit_inlines(builder, &text, config);
123 }
124
125 builder.finish_node(); }
127
128 pub(crate) fn clear(&mut self) {
130 self.segments.clear();
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_new_buffer_is_empty() {
140 let buffer = ListItemBuffer::new();
141 assert!(buffer.is_empty());
142 assert!(!buffer.has_blank_lines_between_content());
143 }
144
145 #[test]
146 fn test_push_single_text() {
147 let mut buffer = ListItemBuffer::new();
148 buffer.push_text("Hello, world!");
149 assert!(!buffer.is_empty());
150 assert!(!buffer.has_blank_lines_between_content());
151 assert_eq!(buffer.get_text_for_parsing(), "Hello, world!");
152 }
153
154 #[test]
155 fn test_push_multiple_text_segments() {
156 let mut buffer = ListItemBuffer::new();
157 buffer.push_text("Line 1\n");
158 buffer.push_text("Line 2\n");
159 buffer.push_text("Line 3");
160 assert_eq!(buffer.get_text_for_parsing(), "Line 1\nLine 2\nLine 3");
161 }
162
163 #[test]
164 fn test_clear_buffer() {
165 let mut buffer = ListItemBuffer::new();
166 buffer.push_text("Some text");
167 assert!(!buffer.is_empty());
168
169 buffer.clear();
170 assert!(buffer.is_empty());
171 assert_eq!(buffer.get_text_for_parsing(), "");
172 }
173
174 #[test]
175 fn test_empty_text_ignored() {
176 let mut buffer = ListItemBuffer::new();
177 buffer.push_text("");
178 assert!(buffer.is_empty());
179 }
180}