floem_editor_core/
util.rs

1use core::str::FromStr;
2use std::collections::HashMap;
3
4/// If the character is an opening bracket return Some(true), if closing, return Some(false)
5pub fn matching_pair_direction(c: char) -> Option<bool> {
6    Some(match c {
7        '{' => true,
8        '}' => false,
9        '(' => true,
10        ')' => false,
11        '[' => true,
12        ']' => false,
13        _ => return None,
14    })
15}
16
17pub fn matching_char(c: char) -> Option<char> {
18    Some(match c {
19        '{' => '}',
20        '}' => '{',
21        '(' => ')',
22        ')' => '(',
23        '[' => ']',
24        ']' => '[',
25        _ => return None,
26    })
27}
28
29/// If the given character is a parenthesis, returns its matching bracket
30pub fn matching_bracket_general<R: ToStaticTextType>(char: char) -> Option<R>
31where
32    &'static str: ToStaticTextType<R>,
33{
34    let pair = match char {
35        '{' => "}",
36        '}' => "{",
37        '(' => ")",
38        ')' => "(",
39        '[' => "]",
40        ']' => "[",
41        _ => return None,
42    };
43    Some(pair.to_static())
44}
45
46pub trait ToStaticTextType<R: 'static = Self>: 'static {
47    fn to_static(self) -> R;
48}
49
50impl ToStaticTextType for &'static str {
51    #[inline]
52    fn to_static(self) -> &'static str {
53        self
54    }
55}
56
57impl ToStaticTextType<char> for &'static str {
58    #[inline]
59    fn to_static(self) -> char {
60        char::from_str(self).unwrap()
61    }
62}
63
64impl ToStaticTextType<String> for &'static str {
65    #[inline]
66    fn to_static(self) -> String {
67        self.to_string()
68    }
69}
70
71impl ToStaticTextType for char {
72    #[inline]
73    fn to_static(self) -> char {
74        self
75    }
76}
77
78impl ToStaticTextType for String {
79    #[inline]
80    fn to_static(self) -> String {
81        self
82    }
83}
84
85pub fn has_unmatched_pair(line: &str) -> bool {
86    let mut count = HashMap::new();
87    let mut pair_first = HashMap::new();
88    for c in line.chars().rev() {
89        if let Some(left) = matching_pair_direction(c) {
90            let key = if left { c } else { matching_char(c).unwrap() };
91            let pair_count = *count.get(&key).unwrap_or(&0i32);
92            pair_first.entry(key).or_insert(left);
93            if left {
94                count.insert(key, pair_count - 1);
95            } else {
96                count.insert(key, pair_count + 1);
97            }
98        }
99    }
100    for (_, pair_count) in count.iter() {
101        if *pair_count < 0 {
102            return true;
103        }
104    }
105    for (_, left) in pair_first.iter() {
106        if *left {
107            return true;
108        }
109    }
110    false
111}
112
113pub fn str_is_pair_left(c: &str) -> bool {
114    if c.chars().count() == 1 {
115        let c = c.chars().next().unwrap();
116        if matching_pair_direction(c).unwrap_or(false) {
117            return true;
118        }
119    }
120    false
121}
122
123pub fn str_matching_pair(c: &str) -> Option<char> {
124    if c.chars().count() == 1 {
125        let c = c.chars().next().unwrap();
126        return matching_char(c);
127    }
128    None
129}