netget/cli/
input_state.rs1use crossterm::event::{KeyCode, KeyModifiers};
31
32fn char_len(line: &str) -> usize {
34 line.chars().count()
35}
36
37fn byte_offset(line: &str, char_idx: usize) -> usize {
42 line.char_indices()
43 .nth(char_idx)
44 .map(|(offset, _)| offset)
45 .unwrap_or(line.len())
46}
47
48#[derive(Debug, Clone, Copy)]
50pub enum Direction {
51 Up,
52 Down,
53 Left,
54 Right,
55}
56
57#[derive(Debug, Clone)]
59pub struct InputState {
60 lines: Vec<String>,
62 cursor_row: usize,
64 cursor_col: usize,
67}
68
69impl Default for InputState {
70 fn default() -> Self {
71 Self {
72 lines: vec![String::new()],
73 cursor_row: 0,
74 cursor_col: 0,
75 }
76 }
77}
78
79impl InputState {
80 pub fn new() -> Self {
82 Self::default()
83 }
84
85 pub fn from_lines(lines: Vec<String>) -> Self {
87 if lines.is_empty() {
88 Self::default()
89 } else {
90 let cursor_row = lines.len() - 1;
91 let cursor_col = char_len(&lines[cursor_row]);
92 Self {
93 lines,
94 cursor_row,
95 cursor_col,
96 }
97 }
98 }
99
100 pub fn lines(&self) -> &[String] {
102 &self.lines
103 }
104
105 pub fn text(&self) -> String {
107 self.lines.join("\n")
108 }
109
110 pub fn cursor_position(&self) -> (usize, usize) {
112 (self.cursor_row, self.cursor_col)
113 }
114
115 pub fn clear(&mut self) {
117 self.lines = vec![String::new()];
118 self.cursor_row = 0;
119 self.cursor_col = 0;
120 }
121
122 pub fn insert_char(&mut self, c: char) {
124 let offset = byte_offset(&self.lines[self.cursor_row], self.cursor_col);
125 self.lines[self.cursor_row].insert(offset, c);
126 self.cursor_col += 1;
127 }
128
129 pub fn insert_newline(&mut self) {
131 let offset = byte_offset(&self.lines[self.cursor_row], self.cursor_col);
132 let rest = self.lines[self.cursor_row][offset..].to_string();
133 self.lines[self.cursor_row].truncate(offset);
134
135 self.cursor_row += 1;
136 self.lines.insert(self.cursor_row, rest);
137 self.cursor_col = 0;
138 }
139
140 pub fn delete_char(&mut self) {
142 if self.cursor_col > 0 {
143 let offset = byte_offset(&self.lines[self.cursor_row], self.cursor_col - 1);
145 self.lines[self.cursor_row].remove(offset);
146 self.cursor_col -= 1;
147 } else if self.cursor_row > 0 {
148 let current_line = self.lines.remove(self.cursor_row);
150 self.cursor_row -= 1;
151 self.cursor_col = char_len(&self.lines[self.cursor_row]);
152 self.lines[self.cursor_row].push_str(¤t_line);
153 }
154 }
155
156 pub fn delete_char_forward(&mut self) {
158 if self.cursor_col < char_len(&self.lines[self.cursor_row]) {
159 let offset = byte_offset(&self.lines[self.cursor_row], self.cursor_col);
161 self.lines[self.cursor_row].remove(offset);
162 } else if self.cursor_row < self.lines.len() - 1 {
163 let next_line = self.lines.remove(self.cursor_row + 1);
165 self.lines[self.cursor_row].push_str(&next_line);
166 }
167 }
168
169 pub fn delete_to_end_of_line(&mut self) {
171 let offset = byte_offset(&self.lines[self.cursor_row], self.cursor_col);
172 self.lines[self.cursor_row].truncate(offset);
173 }
174
175 pub fn delete_line(&mut self) {
177 self.lines[self.cursor_row].clear();
178 self.cursor_col = 0;
179 }
180
181 pub fn delete_word(&mut self) {
183 if self.cursor_col == 0 {
184 return;
185 }
186
187 let line = &mut self.lines[self.cursor_row];
188 let chars: Vec<char> = line.chars().collect();
189 let end_char = self.cursor_col.min(chars.len());
190
191 let mut new_col = end_char;
193
194 while new_col > 0 && chars[new_col - 1].is_whitespace() {
196 new_col -= 1;
197 }
198
199 while new_col > 0 && !chars[new_col - 1].is_whitespace() {
201 new_col -= 1;
202 }
203
204 let byte_start = byte_offset(line, new_col);
206 let byte_end = byte_offset(line, end_char);
207 line.replace_range(byte_start..byte_end, "");
208
209 self.cursor_col = new_col;
210 }
211
212 pub fn delete_word_forward(&mut self) {
214 let line = &mut self.lines[self.cursor_row];
215 let chars: Vec<char> = line.chars().collect();
216 if self.cursor_col >= chars.len() {
217 return;
218 }
219
220 let mut end_col = self.cursor_col;
222
223 while end_col < chars.len() && chars[end_col].is_whitespace() {
225 end_col += 1;
226 }
227
228 while end_col < chars.len() && !chars[end_col].is_whitespace() {
230 end_col += 1;
231 }
232
233 let byte_start = byte_offset(line, self.cursor_col);
235 let byte_end = byte_offset(line, end_col);
236 line.replace_range(byte_start..byte_end, "");
237 }
238
239 pub fn move_cursor_word_left(&mut self) {
241 let line = &self.lines[self.cursor_row];
242 if self.cursor_col == 0 {
243 if self.cursor_row > 0 {
245 self.cursor_row -= 1;
246 self.cursor_col = char_len(&self.lines[self.cursor_row]);
247 }
248 return;
249 }
250
251 let chars: Vec<char> = line.chars().collect();
252 let mut new_col = self.cursor_col.min(chars.len());
253
254 while new_col > 0 && chars[new_col - 1].is_whitespace() {
256 new_col -= 1;
257 }
258
259 while new_col > 0 && !chars[new_col - 1].is_whitespace() {
261 new_col -= 1;
262 }
263
264 self.cursor_col = new_col;
265 }
266
267 pub fn move_cursor_word_right(&mut self) {
269 let line = &self.lines[self.cursor_row];
270 let chars: Vec<char> = line.chars().collect();
271 if self.cursor_col >= chars.len() {
272 if self.cursor_row < self.lines.len() - 1 {
274 self.cursor_row += 1;
275 self.cursor_col = 0;
276 }
277 return;
278 }
279
280 let mut new_col = self.cursor_col;
281
282 if new_col < chars.len() && !chars[new_col].is_whitespace() {
284 while new_col < chars.len() && !chars[new_col].is_whitespace() {
285 new_col += 1;
286 }
287 }
288
289 while new_col < chars.len() && chars[new_col].is_whitespace() {
291 new_col += 1;
292 }
293
294 self.cursor_col = new_col;
295 }
296
297 pub fn move_cursor(&mut self, direction: Direction) {
299 match direction {
300 Direction::Up => {
301 if self.cursor_row > 0 {
302 self.cursor_row -= 1;
303 let line_len = char_len(&self.lines[self.cursor_row]);
305 self.cursor_col = self.cursor_col.min(line_len);
306 }
307 }
308 Direction::Down => {
309 if self.cursor_row < self.lines.len() - 1 {
310 self.cursor_row += 1;
311 let line_len = char_len(&self.lines[self.cursor_row]);
313 self.cursor_col = self.cursor_col.min(line_len);
314 }
315 }
316 Direction::Left => {
317 if self.cursor_col > 0 {
318 self.cursor_col -= 1;
319 } else if self.cursor_row > 0 {
320 self.cursor_row -= 1;
322 self.cursor_col = char_len(&self.lines[self.cursor_row]);
323 }
324 }
325 Direction::Right => {
326 let line_len = char_len(&self.lines[self.cursor_row]);
327 if self.cursor_col < line_len {
328 self.cursor_col += 1;
329 } else if self.cursor_row < self.lines.len() - 1 {
330 self.cursor_row += 1;
332 self.cursor_col = 0;
333 }
334 }
335 }
336 }
337
338 pub fn move_to_start_of_line(&mut self) {
340 self.cursor_col = 0;
341 }
342
343 pub fn move_to_end_of_line(&mut self) {
345 self.cursor_col = char_len(&self.lines[self.cursor_row]);
346 }
347
348 pub fn move_to_top(&mut self) {
350 self.cursor_row = 0;
351 self.cursor_col = 0;
352 }
353
354 pub fn move_to_bottom(&mut self) {
356 self.cursor_row = self.lines.len() - 1;
357 self.cursor_col = char_len(&self.lines[self.cursor_row]);
358 }
359
360 pub fn is_on_first_line(&self) -> bool {
362 self.cursor_row == 0
363 }
364
365 pub fn is_on_last_line(&self) -> bool {
367 self.cursor_row == self.lines.len() - 1
368 }
369
370 pub fn handle_key(&mut self, key_code: KeyCode, modifiers: KeyModifiers) -> bool {
372 match key_code {
373 KeyCode::Char(c) => {
374 if modifiers.contains(KeyModifiers::CONTROL)
376 || modifiers.contains(KeyModifiers::ALT)
377 {
378 return false;
380 }
381 self.insert_char(c);
382 true
383 }
384 KeyCode::Backspace => {
385 self.delete_char();
386 true
387 }
388 KeyCode::Delete => {
389 self.delete_char_forward();
390 true
391 }
392 KeyCode::Left => {
393 self.move_cursor(Direction::Left);
394 true
395 }
396 KeyCode::Right => {
397 self.move_cursor(Direction::Right);
398 true
399 }
400 KeyCode::Up => {
401 self.move_cursor(Direction::Up);
402 true
403 }
404 KeyCode::Down => {
405 self.move_cursor(Direction::Down);
406 true
407 }
408 KeyCode::Home => {
409 self.move_to_start_of_line();
410 true
411 }
412 KeyCode::End => {
413 self.move_to_end_of_line();
414 true
415 }
416 _ => false,
417 }
418 }
419}