dioxus_autofmt/
indent.rs

1#[derive(Clone, Copy, PartialEq, Eq, Debug)]
2pub enum IndentType {
3    Spaces,
4    Tabs,
5}
6
7#[derive(Debug, Clone)]
8pub struct IndentOptions {
9    width: usize,
10    indent_string: String,
11    split_line_attributes: bool,
12}
13
14impl IndentOptions {
15    pub fn new(typ: IndentType, width: usize, split_line_attributes: bool) -> Self {
16        assert_ne!(width, 0, "Cannot have an indent width of 0");
17        Self {
18            width,
19            indent_string: match typ {
20                IndentType::Tabs => "\t".into(),
21                IndentType::Spaces => " ".repeat(width),
22            },
23            split_line_attributes,
24        }
25    }
26
27    /// Gets a string containing one indent worth of whitespace
28    pub fn indent_str(&self) -> &str {
29        &self.indent_string
30    }
31
32    /// Computes the line length in characters, counting tabs as the indent width.
33    pub fn line_length(&self, line: &str) -> usize {
34        line.chars()
35            .map(|ch| if ch == '\t' { self.width } else { 1 })
36            .sum()
37    }
38
39    /// Estimates how many times the line has been indented.
40    pub fn count_indents(&self, mut line: &str) -> usize {
41        let mut indent = 0;
42        while !line.is_empty() {
43            // Try to count tabs
44            let num_tabs = line.chars().take_while(|ch| *ch == '\t').count();
45            if num_tabs > 0 {
46                indent += num_tabs;
47                line = &line[num_tabs..];
48                continue;
49            }
50
51            // Try to count spaces
52            let num_spaces = line.chars().take_while(|ch| *ch == ' ').count();
53            if num_spaces >= self.width {
54                // Intentionally floor here to take only the amount of space that matches an indent
55                let num_space_indents = num_spaces / self.width;
56                indent += num_space_indents;
57                line = &line[num_space_indents * self.width..];
58                continue;
59            }
60
61            // Line starts with either non-indent characters or an unevent amount of spaces,
62            // so no more indent remains.
63            break;
64        }
65        indent
66    }
67
68    pub fn split_line_attributes(&self) -> bool {
69        self.split_line_attributes
70    }
71}
72
73impl Default for IndentOptions {
74    fn default() -> Self {
75        Self::new(IndentType::Spaces, 4, false)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn count_indents() {
85        assert_eq!(
86            IndentOptions::new(IndentType::Spaces, 4, false).count_indents("no indentation here!"),
87            0
88        );
89        assert_eq!(
90            IndentOptions::new(IndentType::Spaces, 4, false).count_indents("    v += 2"),
91            1
92        );
93        assert_eq!(
94            IndentOptions::new(IndentType::Spaces, 4, false).count_indents("        v += 2"),
95            2
96        );
97        assert_eq!(
98            IndentOptions::new(IndentType::Spaces, 4, false).count_indents("          v += 2"),
99            2
100        );
101        assert_eq!(
102            IndentOptions::new(IndentType::Spaces, 4, false).count_indents("\t\tv += 2"),
103            2
104        );
105        assert_eq!(
106            IndentOptions::new(IndentType::Spaces, 4, false).count_indents("\t\t  v += 2"),
107            2
108        );
109        assert_eq!(
110            IndentOptions::new(IndentType::Spaces, 2, false).count_indents("    v += 2"),
111            2
112        );
113    }
114}