makepad_code_editor/
str.rs1use 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}