1use crossterm::event::KeyCode;
4
5#[derive(Debug, Clone, Default, PartialEq)]
9pub struct LineEdit {
10 pub text: String,
11 pub caret: usize,
12}
13
14impl From<&str> for LineEdit {
15 fn from(s: &str) -> Self {
16 s.to_string().into()
17 }
18}
19
20impl From<String> for LineEdit {
21 fn from(text: String) -> Self {
22 LineEdit {
23 caret: text.len(),
24 text,
25 }
26 }
27}
28
29impl std::ops::Deref for LineEdit {
30 type Target = str;
31 fn deref(&self) -> &str {
32 &self.text
33 }
34}
35
36impl LineEdit {
37 pub fn insert(&mut self, s: &str) {
39 self.text.insert_str(self.caret, s);
40 self.caret += s.len();
41 }
42
43 pub fn replace(&mut self, start: usize, s: &str) {
45 self.text.replace_range(start..self.caret, s);
46 self.caret = start + s.len();
47 }
48
49 fn prev(&self) -> usize {
50 self.text[..self.caret]
51 .chars()
52 .next_back()
53 .map_or(self.caret, |c| self.caret - c.len_utf8())
54 }
55
56 fn next(&self) -> usize {
57 self.text[self.caret..]
58 .chars()
59 .next()
60 .map_or(self.caret, |c| self.caret + c.len_utf8())
61 }
62
63 pub fn key(&mut self, code: KeyCode) -> bool {
65 let before = self.text.len();
66 match code {
67 KeyCode::Char(c) => self.insert(c.encode_utf8(&mut [0; 4])),
68 KeyCode::Backspace => {
69 let p = self.prev();
70 self.text.replace_range(p..self.caret, "");
71 self.caret = p;
72 }
73 KeyCode::Delete => {
74 let n = self.next();
75 self.text.replace_range(self.caret..n, "");
76 }
77 KeyCode::Left => self.caret = self.prev(),
78 KeyCode::Right => self.caret = self.next(),
79 KeyCode::Home => self.caret = 0,
80 KeyCode::End => self.caret = self.text.len(),
81 _ => {}
82 }
83 self.text.len() != before
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn caret_edits_on_char_boundaries() {
93 let mut e = LineEdit::from("aé");
94 assert_eq!(e.caret, 3);
95 assert!(!e.key(KeyCode::Left));
96 assert_eq!(e.caret, 1, "stepped over the 2-byte é");
97 assert!(e.key(KeyCode::Char('x')));
98 assert_eq!(&*e, "axé");
99 assert!(!e.key(KeyCode::Home));
100 assert!(!e.key(KeyCode::Backspace), "nothing before the caret");
101 assert!(e.key(KeyCode::Delete));
102 assert_eq!(&*e, "xé");
103 assert!(!e.key(KeyCode::End));
104 assert!(!e.key(KeyCode::Delete), "nothing after the caret");
105 assert!(!e.key(KeyCode::Right));
106 assert!(e.key(KeyCode::Backspace));
107 assert_eq!((e.text.as_str(), e.caret), ("x", 1));
108 assert!(!e.key(KeyCode::F(5)));
109 e.caret = 0;
110 e.replace(0, "SUM(");
111 assert_eq!((e.text.as_str(), e.caret), ("SUM(x", 4));
112 }
113}