1use unicode_segmentation::UnicodeSegmentation;
2
3#[derive(Clone, Default)]
6pub struct Input {
7 symbols: Vec<String>,
9 cursor_index: usize,
11}
12
13impl Input {
14 const RIGHT_GAP: usize = 4;
15
16 pub fn reset(&mut self) {
18 self.symbols.clear();
19 self.cursor_index = 0;
20 }
21
22 #[must_use]
24 pub fn index(&self) -> usize {
25 self.cursor_index
26 }
27
28 #[must_use]
29 pub fn len(&self) -> usize {
30 self.symbols.len()
31 }
32
33 #[must_use]
34 pub fn is_empty(&self) -> bool {
35 self.symbols.is_empty()
36 }
37
38 #[must_use]
40 pub fn string(&self) -> String {
41 self.symbols.join("")
42 }
43
44 #[inline]
60 fn left_index(&self, available_space: usize) -> usize {
61 if self.input_is_short_enough(available_space) {
62 0
63 } else {
64 (self.index() + Self::RIGHT_GAP).saturating_sub(available_space)
65 }
66 }
67
68 #[inline]
69 fn input_is_short_enough(&self, available_space: usize) -> bool {
70 self.len() <= available_space
71 }
72
73 #[inline]
76 pub fn display_index(&self, available_space: usize) -> usize {
77 self.index()
78 .saturating_sub(self.left_index(available_space))
79 }
80
81 pub fn scrolled_string(&self, available_space: usize) -> String {
84 if self.input_is_short_enough(available_space) {
85 self.string()
86 } else {
87 self.symbols
88 .iter()
89 .skip(self.left_index(available_space))
90 .take(available_space)
91 .map(|s| s.to_string())
92 .collect::<Vec<_>>()
93 .join("")
94 }
95 }
96
97 #[must_use]
99 pub fn password(&self) -> String {
100 "*".repeat(self.len())
101 }
102
103 pub fn insert(&mut self, c: char) {
105 self.symbols.insert(self.cursor_index, String::from(c));
106 self.cursor_index += 1;
107 }
108
109 pub fn insert_string(&mut self, pasted: &str) {
111 UnicodeSegmentation::graphemes(pasted, true)
112 .collect::<Vec<&str>>()
113 .iter()
114 .map(|s| (*s).to_string())
115 .for_each(|s| {
116 self.symbols.insert(self.cursor_index, s);
117 self.cursor_index += 1;
118 })
119 }
120 pub fn cursor_start(&mut self) {
122 self.cursor_index = 0;
123 }
124
125 pub fn cursor_end(&mut self) {
127 self.cursor_index = self.len();
128 }
129
130 pub fn cursor_left(&mut self) {
132 if self.cursor_index > 0 {
133 self.cursor_index -= 1;
134 }
135 }
136
137 pub fn cursor_right(&mut self) {
139 if self.cursor_index < self.len() {
140 self.cursor_index += 1;
141 }
142 }
143
144 pub fn cursor_move(&mut self, index: usize) {
148 if index <= self.len() {
149 self.cursor_index = index
150 } else {
151 self.cursor_end()
152 }
153 }
154
155 pub fn delete_char_left(&mut self) {
157 if self.cursor_index > 0 && !self.symbols.is_empty() {
158 self.symbols.remove(self.cursor_index - 1);
159 self.cursor_index -= 1;
160 }
161 }
162
163 pub fn delete_chars_right(&mut self) {
165 self.symbols = self
166 .symbols
167 .iter()
168 .take(self.cursor_index)
169 .map(std::string::ToString::to_string)
170 .collect();
171 }
172
173 pub fn delete_line(&mut self) {
174 self.symbols = vec![];
175 self.cursor_index = 0;
176 }
177
178 pub fn delete_left(&mut self) {
182 while self.cursor_index > 0 {
183 self.symbols.remove(self.cursor_index.saturating_sub(1));
184 self.cursor_index -= 1;
185 if self.is_empty() || is_separator(&self.symbols[self.cursor_index.saturating_sub(1)]) {
186 break;
187 }
188 }
189 }
190
191 pub fn replace(&mut self, content: &str) {
201 self.symbols = UnicodeSegmentation::graphemes(content, true)
202 .collect::<Vec<&str>>()
203 .iter()
204 .map(|s| (*s).to_string())
205 .collect();
206 self.cursor_end();
207 }
208
209 pub fn next_word(&mut self) {
213 while self.cursor_index < self.symbols.len() {
214 self.cursor_index += 1;
215 if self.cursor_index == self.symbols.len()
216 || is_separator(&self.symbols[self.cursor_index])
217 {
218 break;
219 }
220 }
221 }
222
223 pub fn previous_word(&mut self) {
227 while self.cursor_index > 0 {
228 self.cursor_index -= 1;
229 if self.cursor_index == 0
230 || is_separator(&self.symbols[self.cursor_index.saturating_sub(1)])
231 {
232 break;
233 }
234 }
235 }
236}
237
238#[rustfmt::skip]
239#[inline]
240fn is_separator(word: &str) -> bool {
241 matches!(word, " " | "\t" | "/" | "\\" | "." | "," | ";" | "!" | "?" | "%" | "_" | "-" | "+" | "*" | "(" | ")" | "{" | "}" | "[" | "]")
242}