panache_parser/parser/blocks/
paragraphs.rs1use crate::options::ParserOptions;
7use crate::syntax::SyntaxKind;
8use rowan::GreenNodeBuilder;
9
10use crate::parser::blocks::raw_blocks::{extract_environment_name, is_inline_math_environment};
11use crate::parser::utils::container_stack::{Container, ContainerStack};
12use crate::parser::utils::text_buffer::ParagraphBuffer;
13
14fn update_display_math_dollar_state(
15 line_no_newline: &str,
16 open_display_math_dollar_count: &mut Option<usize>,
17) {
18 let trimmed = line_no_newline.trim();
19 if trimmed.len() < 2 {
20 return;
21 }
22
23 let run_len = trimmed.bytes().take_while(|b| *b == b'$').count();
24 if run_len < 2 {
25 return;
26 }
27
28 let rest = &trimmed[run_len..];
29 let is_pure_dollars = rest.is_empty();
30 let is_quarto_equation_attr_closer = rest.starts_with(char::is_whitespace) && {
31 let attr = rest.trim_start();
32 attr.starts_with('{') && attr.ends_with('}')
33 };
34
35 if let Some(open_len) = *open_display_math_dollar_count {
36 if (is_pure_dollars || is_quarto_equation_attr_closer) && run_len >= open_len {
37 *open_display_math_dollar_count = None;
38 }
39 } else if is_pure_dollars {
40 *open_display_math_dollar_count = Some(run_len);
41 }
42}
43
44fn extract_end_environment_name(line: &str) -> Option<&str> {
45 let trimmed = line.trim_start();
46 if !trimmed.starts_with("\\end{") {
47 return None;
48 }
49 let rest = &trimmed[5..];
50 let close = rest.find('}')?;
51 let name = &rest[..close];
52 if name.is_empty() {
53 return None;
54 }
55 Some(name)
56}
57
58pub(in crate::parser) fn start_paragraph_if_needed(
60 containers: &mut ContainerStack,
61 builder: &mut GreenNodeBuilder<'static>,
62) {
63 if !matches!(containers.last(), Some(Container::Paragraph { .. })) {
64 builder.start_node(SyntaxKind::PARAGRAPH.into());
65 containers.push(Container::Paragraph {
66 buffer: ParagraphBuffer::new(),
67 open_inline_math_envs: Vec::new(),
68 open_display_math_dollar_count: None,
69 });
70 }
71}
72
73pub(in crate::parser) fn append_paragraph_line(
75 containers: &mut ContainerStack,
76 _builder: &mut GreenNodeBuilder<'static>,
77 line: &str,
78 _config: &ParserOptions,
79) {
80 if let Some(Container::Paragraph {
83 buffer,
84 open_inline_math_envs,
85 open_display_math_dollar_count,
86 }) = containers.stack.last_mut()
87 {
88 buffer.push_text(line);
89
90 let line_no_newline = line.trim_end_matches(&['\r', '\n'][..]);
91 update_display_math_dollar_state(line_no_newline, open_display_math_dollar_count);
96 if let Some(env_name) = extract_environment_name(line_no_newline)
97 && is_inline_math_environment(&env_name)
98 {
99 open_inline_math_envs.push(env_name);
100 return;
101 }
102
103 if let Some(end_name) = extract_end_environment_name(line_no_newline)
104 && open_inline_math_envs
105 .last()
106 .is_some_and(|open| open == end_name)
107 {
108 open_inline_math_envs.pop();
109 }
110 }
111}
112
113pub(in crate::parser) fn append_paragraph_marker(
119 containers: &mut ContainerStack,
120 leading_spaces: usize,
121 has_trailing_space: bool,
122) {
123 if let Some(Container::Paragraph { buffer, .. }) = containers.stack.last_mut() {
124 buffer.push_marker(leading_spaces, has_trailing_space);
125 }
126}
127
128pub(in crate::parser) fn has_open_inline_math_environment(containers: &ContainerStack) -> bool {
129 matches!(
130 containers.last(),
131 Some(Container::Paragraph {
132 open_inline_math_envs,
133 ..
134 }) if !open_inline_math_envs.is_empty()
135 )
136}
137
138pub(in crate::parser) fn has_open_display_math_dollars(containers: &ContainerStack) -> bool {
139 matches!(
140 containers.last(),
141 Some(Container::Paragraph {
142 open_display_math_dollar_count,
143 ..
144 }) if open_display_math_dollar_count.is_some()
145 )
146}
147
148pub(in crate::parser) fn current_content_col(containers: &ContainerStack) -> usize {
150 containers
151 .stack
152 .iter()
153 .rev()
154 .find_map(|c| match c {
155 Container::ListItem { content_col, .. } => Some(*content_col),
156 Container::FootnoteDefinition { content_col, .. } => Some(*content_col),
157 _ => None,
158 })
159 .unwrap_or(0)
160}