makepad_code_editor/
str.rs

1use crate::char::CharExt;
2
3pub trait StrExt {
4    fn column_count(&self) -> usize;
5    fn indent_level(&self, indent_column_count: usize) -> usize;
6    fn next_indent_level(&self, indent_column_count: usize) -> usize;
7    fn prev_indent_level(&self, indent_column_count: usize) -> usize;
8    fn indent(&self) -> Option<&str>;
9    fn longest_common_prefix(&self, other: &str) -> &str;
10    fn graphemes(&self) -> Graphemes<'_>;
11    fn grapheme_indices(&self) -> GraphemeIndices<'_>;
12    fn split_whitespace_boundaries(&self) -> SplitWhitespaceBoundaries<'_>;
13}
14
15impl StrExt for str {
16    fn column_count(&self) -> usize {
17        self.chars().map(|char| char.column_count()).sum()
18    }
19
20    fn indent_level(&self, indent_column_count: usize) -> usize {
21        self.indent().unwrap_or("").column_count() / indent_column_count
22    }
23
24    fn next_indent_level(&self, indent_column_count: usize) -> usize {
25        (self.indent().unwrap_or("").column_count() + indent_column_count) / indent_column_count
26    }
27
28    fn prev_indent_level(&self, indent_column_count: usize) -> usize {
29        self.indent().unwrap_or("").column_count().saturating_sub(1) / indent_column_count
30    }
31
32    fn indent(&self) -> Option<&str> {
33        self.char_indices()
34            .find(|(_, char)| !char.is_whitespace())
35            .map(|(index, _)| &self[..index])
36    }
37
38    fn longest_common_prefix(&self, other: &str) -> &str {
39        &self[..self
40            .char_indices()
41            .zip(other.chars())
42            .find(|((_, char_0), char_1)| char_0 == char_1)
43            .map(|((index, _), _)| index)
44            .unwrap_or_else(|| self.len().min(other.len()))]
45    }
46
47    fn graphemes(&self) -> Graphemes<'_> {
48        Graphemes { string: self }
49    }
50
51    fn grapheme_indices(&self) -> GraphemeIndices<'_> {
52        GraphemeIndices {
53            graphemes: self.graphemes(),
54            start: self.as_ptr() as usize,
55        }
56    }
57
58    fn split_whitespace_boundaries(&self) -> SplitWhitespaceBoundaries<'_> {
59        SplitWhitespaceBoundaries { string: self }
60    }
61}
62
63#[derive(Clone, Debug)]
64pub struct Graphemes<'a> {
65    string: &'a str,
66}
67
68impl<'a> Iterator for Graphemes<'a> {
69    type Item = &'a str;
70
71    fn next(&mut self) -> Option<Self::Item> {
72        if self.string.is_empty() {
73            return None;
74        }
75        let mut end = 1;
76        while !self.string.is_char_boundary(end) {
77            end += 1;
78        }
79        let (grapheme, string) = self.string.split_at(end);
80        self.string = string;
81        Some(grapheme)
82    }
83}
84
85impl<'a> DoubleEndedIterator for Graphemes<'a> {
86    fn next_back(&mut self) -> Option<Self::Item> {
87        if self.string.is_empty() {
88            return None;
89        }
90        let mut start = self.string.len() - 1;
91        while !self.string.is_char_boundary(start) {
92            start -= 1;
93        }
94        let (string, grapheme) = self.string.split_at(start);
95        self.string = string;
96        Some(grapheme)
97    }
98}
99
100#[derive(Clone, Debug)]
101pub struct GraphemeIndices<'a> {
102    graphemes: Graphemes<'a>,
103    start: usize,
104}
105
106impl<'a> Iterator for GraphemeIndices<'a> {
107    type Item = (usize, &'a str);
108
109    fn next(&mut self) -> Option<Self::Item> {
110        let grapheme = self.graphemes.next()?;
111        Some((grapheme.as_ptr() as usize - self.start, grapheme))
112    }
113}
114
115impl<'a> DoubleEndedIterator for GraphemeIndices<'a> {
116    fn next_back(&mut self) -> Option<Self::Item> {
117        let grapheme = self.graphemes.next_back()?;
118        Some((grapheme.as_ptr() as usize - self.start, grapheme))
119    }
120}
121
122#[derive(Clone, Debug)]
123pub struct SplitWhitespaceBoundaries<'a> {
124    string: &'a str,
125}
126
127impl<'a> Iterator for SplitWhitespaceBoundaries<'a> {
128    type Item = &'a str;
129
130    fn next(&mut self) -> Option<Self::Item> {
131        if self.string.is_empty() {
132            return None;
133        }
134        let mut prev_char_is_whitespace = None;
135        let index = self
136            .string
137            .char_indices()
138            .find_map(|(index, next_char)| {
139                let next_char_is_whitespace = next_char.is_whitespace();
140                let is_whitespace_boundary = prev_char_is_whitespace
141                    .map_or(false, |prev_char_is_whitespace| {
142                        prev_char_is_whitespace != next_char_is_whitespace
143                    });
144                prev_char_is_whitespace = Some(next_char_is_whitespace);
145                if is_whitespace_boundary {
146                    Some(index)
147                } else {
148                    None
149                }
150            })
151            .unwrap_or(self.string.len());
152        let (string_0, string_1) = self.string.split_at(index);
153        self.string = string_1;
154        Some(string_0)
155    }
156}