Skip to main content

panache_parser/parser/utils/
container_stack.rs

1use super::list_item_buffer::ListItemBuffer;
2use super::text_buffer::{ParagraphBuffer, TextBuffer};
3use crate::parser::blocks::lists::ListMarker;
4
5#[derive(Debug, Clone)]
6pub(crate) enum Container {
7    BlockQuote {
8        // No special tracking needed
9    },
10    Alert {
11        blockquote_depth: usize,
12    },
13    FencedDiv {
14        // No special tracking needed - closed by fence marker
15    },
16    List {
17        marker: ListMarker,
18        base_indent_cols: usize,
19        has_blank_between_items: bool, // Track if list is loose (blank lines between items)
20    },
21    ListItem {
22        content_col: usize,
23        buffer: ListItemBuffer, // Buffer for list item content
24    },
25    DefinitionList {
26        // Definition lists don't need special tracking
27    },
28    DefinitionItem {
29        // No special tracking needed
30    },
31    Definition {
32        content_col: usize,
33        plain_open: bool,
34        plain_buffer: TextBuffer, // Buffer for accumulating PLAIN content
35    },
36    Paragraph {
37        buffer: ParagraphBuffer, // Interleaved buffer for paragraph content with markers
38        open_inline_math_envs: Vec<String>,
39        open_display_math_dollar_count: Option<usize>,
40    },
41    FootnoteDefinition {
42        content_col: usize,
43    },
44}
45
46pub(crate) struct ContainerStack {
47    pub(crate) stack: Vec<Container>,
48}
49
50const TAB_STOP: usize = 4;
51
52impl ContainerStack {
53    pub(crate) fn new() -> Self {
54        Self { stack: Vec::new() }
55    }
56
57    pub(crate) fn depth(&self) -> usize {
58        self.stack.len()
59    }
60
61    pub(crate) fn last(&self) -> Option<&Container> {
62        self.stack.last()
63    }
64
65    pub(crate) fn push(&mut self, c: Container) {
66        self.stack.push(c);
67    }
68}
69
70/// Expand tabs to columns (tab stop = 4) and return (cols, byte_offset).
71pub(crate) fn leading_indent(line: &str) -> (usize, usize) {
72    let mut cols = 0usize;
73    let mut bytes = 0usize;
74    for b in line.bytes() {
75        match b {
76            b' ' => {
77                cols += 1;
78                bytes += 1;
79            }
80            b'\t' => {
81                cols += TAB_STOP - (cols % TAB_STOP);
82                bytes += 1;
83            }
84            _ => break,
85        }
86    }
87    (cols, bytes)
88}
89
90/// Return byte index at a given column (tabs = 4).
91pub(crate) fn byte_index_at_column(line: &str, target_col: usize) -> usize {
92    let mut col = 0usize;
93    let mut idx = 0usize;
94    for (i, b) in line.bytes().enumerate() {
95        if col >= target_col {
96            return idx;
97        }
98        match b {
99            b' ' => {
100                col += 1;
101                idx = i + 1;
102            }
103            b'\t' => {
104                col += TAB_STOP - (col % TAB_STOP);
105                idx = i + 1;
106            }
107            _ => break,
108        }
109    }
110    idx
111}