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    },
40    FootnoteDefinition {
41        content_col: usize,
42    },
43}
44
45pub(crate) struct ContainerStack {
46    pub(crate) stack: Vec<Container>,
47}
48
49const TAB_STOP: usize = 4;
50
51impl ContainerStack {
52    pub(crate) fn new() -> Self {
53        Self { stack: Vec::new() }
54    }
55
56    pub(crate) fn depth(&self) -> usize {
57        self.stack.len()
58    }
59
60    pub(crate) fn last(&self) -> Option<&Container> {
61        self.stack.last()
62    }
63
64    pub(crate) fn push(&mut self, c: Container) {
65        self.stack.push(c);
66    }
67}
68
69/// Expand tabs to columns (tab stop = 4) and return (cols, byte_offset).
70pub(crate) fn leading_indent(line: &str) -> (usize, usize) {
71    let mut cols = 0usize;
72    let mut bytes = 0usize;
73    for b in line.bytes() {
74        match b {
75            b' ' => {
76                cols += 1;
77                bytes += 1;
78            }
79            b'\t' => {
80                cols += TAB_STOP - (cols % TAB_STOP);
81                bytes += 1;
82            }
83            _ => break,
84        }
85    }
86    (cols, bytes)
87}
88
89/// Return byte index at a given column (tabs = 4).
90pub(crate) fn byte_index_at_column(line: &str, target_col: usize) -> usize {
91    let mut col = 0usize;
92    let mut idx = 0usize;
93    for (i, b) in line.bytes().enumerate() {
94        if col >= target_col {
95            return idx;
96        }
97        match b {
98            b' ' => {
99                col += 1;
100                idx = i + 1;
101            }
102            b'\t' => {
103                col += TAB_STOP - (col % TAB_STOP);
104                idx = i + 1;
105            }
106            _ => break,
107        }
108    }
109    idx
110}