glyph_parser/
indent_tracker.rs1use std::collections::VecDeque;
4
5pub struct IndentTracker {
6 indent_stack: Vec<usize>,
7 pending_tokens: VecDeque<IndentToken>,
8 at_line_start: bool,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum IndentToken {
13 Indent,
14 Dedent,
15}
16
17impl Default for IndentTracker {
18 fn default() -> Self {
19 Self {
20 indent_stack: vec![0],
21 pending_tokens: VecDeque::new(),
22 at_line_start: true,
23 }
24 }
25}
26
27impl IndentTracker {
28 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn handle_newline(&mut self) {
33 self.at_line_start = true;
34 }
35
36 pub fn handle_whitespace(&mut self, spaces: usize) -> Option<VecDeque<IndentToken>> {
37 if !self.at_line_start {
38 return None;
39 }
40
41 self.at_line_start = false;
42 let current_indent = *self.indent_stack.last().unwrap_or(&0);
43
44 if spaces > current_indent {
45 self.indent_stack.push(spaces);
47 self.pending_tokens.push_back(IndentToken::Indent);
48 } else if spaces < current_indent {
49 while let Some(&level) = self.indent_stack.last() {
51 if level <= spaces {
52 break;
53 }
54 self.indent_stack.pop();
55 self.pending_tokens.push_back(IndentToken::Dedent);
56 }
57 }
58 if self.pending_tokens.is_empty() {
61 None
62 } else {
63 Some(self.pending_tokens.drain(..).collect())
64 }
65 }
66
67 pub fn handle_non_whitespace(&mut self) -> Option<VecDeque<IndentToken>> {
68 if self.at_line_start {
69 self.at_line_start = false;
70 self.handle_whitespace(0)
72 } else {
73 None
74 }
75 }
76
77 pub fn finish(&mut self) -> VecDeque<IndentToken> {
78 while self.indent_stack.len() > 1 {
80 self.indent_stack.pop();
81 self.pending_tokens.push_back(IndentToken::Dedent);
82 }
83 self.pending_tokens.drain(..).collect()
84 }
85}