vizia_core/text/
editable_text.rs
1#![allow(dead_code)]
2
3use std::{borrow::Cow, ops::Range};
4
5use unicode_segmentation::{GraphemeCursor, UnicodeSegmentation};
6
7pub trait EditableText: Sized {
8 fn edit(&mut self, range: Range<usize>, new: impl Into<Self>);
11
12 fn slice(&self, range: Range<usize>) -> Option<Cow<str>>;
14
15 fn len(&self) -> usize;
17
18 fn prev_word_offset(&self, offset: usize) -> Option<usize>;
20
21 fn next_word_offset(&self, offset: usize) -> Option<usize>;
23
24 fn prev_grapheme_offset(&self, offset: usize) -> Option<usize>;
26
27 fn next_grapheme_offset(&self, offset: usize) -> Option<usize>;
29
30 fn current_grapheme_offset(&self, offset: usize) -> usize;
31
32 fn prev_codepoint_offset(&self, offset: usize) -> Option<usize>;
34
35 fn next_codepoint_offset(&self, offset: usize) -> Option<usize>;
37
38 fn prev_codepoint(&self, offset: usize) -> Option<char>;
39
40 fn preceding_line_break(&self, offset: usize) -> usize;
42
43 fn next_line_break(&self, offset: usize) -> usize;
45
46 fn is_empty(&self) -> bool;
48
49 fn from_str(s: &str) -> Self;
51}
52
53impl EditableText for String {
54 fn edit(&mut self, range: Range<usize>, new: impl Into<Self>) {
55 self.replace_range(range, &new.into());
56 }
57
58 fn slice(&self, range: Range<usize>) -> Option<Cow<str>> {
59 self.get(range).map(Cow::from)
60 }
61
62 fn len(&self) -> usize {
63 self.len()
64 }
65
66 fn prev_grapheme_offset(&self, from: usize) -> Option<usize> {
67 let mut c = GraphemeCursor::new(from, self.len(), true);
68 c.prev_boundary(self, 0).unwrap()
69 }
70
71 fn next_grapheme_offset(&self, from: usize) -> Option<usize> {
72 let mut c = GraphemeCursor::new(from, self.len(), true);
73 c.next_boundary(self, 0).unwrap()
74 }
75
76 fn current_grapheme_offset(&self, from: usize) -> usize {
77 if from == self.len() {
78 self.graphemes(true).count()
79 } else {
80 let mut current = self.graphemes(true).count();
81
82 let mut iter = self.grapheme_indices(true).peekable();
83 let mut count = 0;
84 while let Some((i, _)) = iter.next() {
85 let ni = if let Some(next) = iter.peek() { next.0 } else { self.len() };
86
87 if from >= i && from < ni {
88 current = count;
89 break;
90 }
91
92 count += 1;
93 }
94
95 current
96 }
97 }
98
99 fn prev_codepoint_offset(&self, current_pos: usize) -> Option<usize> {
100 if current_pos == 0 {
101 None
102 } else {
103 let mut len = 1;
104 while !self.is_char_boundary(current_pos - len) {
105 len += 1;
106 }
107
108 Some(current_pos - len)
109 }
110 }
111
112 fn next_codepoint_offset(&self, current_pos: usize) -> Option<usize> {
113 if current_pos == self.len() {
114 None
115 } else {
116 let b = self.as_bytes()[current_pos];
117 Some(current_pos + len_utf8_from_first_byte(b))
118 }
119 }
120
121 fn prev_word_offset(&self, from: usize) -> Option<usize> {
122 let mut offset = from;
123 let mut passed_alphanumeric = false;
124 for prev_grapheme in self.get(0..from)?.graphemes(true).rev() {
125 let is_alphanumeric = prev_grapheme.chars().next()?.is_alphanumeric();
126 if is_alphanumeric {
127 passed_alphanumeric = true;
128 } else if passed_alphanumeric {
129 return Some(offset);
130 }
131 offset -= prev_grapheme.len();
132 }
133 None
134 }
135
136 fn next_word_offset(&self, from: usize) -> Option<usize> {
137 let mut offset = from;
138 let mut passed_alphanumeric = false;
139 for next_grapheme in self.get(from..)?.graphemes(true) {
140 let is_alphanumeric = next_grapheme.chars().next()?.is_alphanumeric();
141 if is_alphanumeric {
142 passed_alphanumeric = true;
143 } else if passed_alphanumeric {
144 return Some(offset);
145 }
146 offset += next_grapheme.len();
147 }
148 Some(self.len())
149 }
150
151 fn is_empty(&self) -> bool {
152 self.is_empty()
153 }
154
155 fn from_str(s: &str) -> Self {
156 s.to_string()
157 }
158
159 fn preceding_line_break(&self, from: usize) -> usize {
160 let mut offset = from;
161
162 for byte in self.get(0..from).unwrap_or("").bytes().rev() {
163 if byte == 0x0a {
164 return offset;
165 }
166 offset -= 1;
167 }
168
169 0
170 }
171
172 fn next_line_break(&self, from: usize) -> usize {
173 let mut offset = from;
174
175 for char in self.get(from..).unwrap_or("").bytes() {
176 if char == 0x0a {
177 return offset;
178 }
179 offset += 1;
180 }
181
182 self.len()
183 }
184
185 fn prev_codepoint(&self, offset: usize) -> Option<char> {
186 if let Some(prev) = self.prev_codepoint_offset(offset) {
187 self[prev..].chars().next()
188 } else {
189 None
190 }
191 }
192}
193
194pub fn len_utf8_from_first_byte(b: u8) -> usize {
195 match b {
196 b if b < 0x80 => 1,
197 b if b < 0xe0 => 2,
198 b if b < 0xf0 => 3,
199 _ => 4,
200 }
201}