diff options
-rw-r--r-- | src/app.rs | 7 | ||||
-rw-r--r-- | src/command.rs | 141 |
2 files changed, 138 insertions, 10 deletions
@@ -248,6 +248,7 @@ impl<'ctx> AppState<'ctx> { | |||
248 | } | 248 | } |
249 | 249 | ||
250 | fn eval_command(&mut self) { | 250 | fn eval_command(&mut self) { |
251 | self.command_box.hist_append(); | ||
251 | match self.command_box.text.as_str() { | 252 | match self.command_box.text.as_str() { |
252 | "(save)" => { | 253 | "(save)" => { |
253 | let image = self.export(); | 254 | let image = self.export(); |
@@ -648,7 +649,11 @@ impl<'ctx> AppState<'ctx> { | |||
648 | Keycode::Delete => self.command_box.delete(), | 649 | Keycode::Delete => self.command_box.delete(), |
649 | Keycode::Left => self.command_box.backward(), | 650 | Keycode::Left => self.command_box.backward(), |
650 | Keycode::Right => self.command_box.forward(), | 651 | Keycode::Right => self.command_box.forward(), |
651 | Keycode::Return => self.eval_command(), | 652 | Keycode::Up => self.command_box.hist_prev(), |
653 | Keycode::Down => self.command_box.hist_next(), | ||
654 | Keycode::Return => { | ||
655 | self.eval_command(); | ||
656 | } | ||
652 | Keycode::Escape => { | 657 | Keycode::Escape => { |
653 | self.command_box.clear(); | 658 | self.command_box.clear(); |
654 | self.mode = Mode::Draw; | 659 | self.mode = Mode::Draw; |
diff --git a/src/command.rs b/src/command.rs index 3beb700..064d767 100644 --- a/src/command.rs +++ b/src/command.rs | |||
@@ -1,20 +1,16 @@ | |||
1 | #[derive(Debug)] | 1 | #[derive(Debug)] |
2 | pub struct CommandBox { | 2 | pub struct CommandBox { |
3 | pub enabled: bool, | 3 | pub history: History<String>, |
4 | pub history: Vec<String>, | 4 | pub hist_idx: Option<usize>, |
5 | pub text: String, | 5 | pub text: String, |
6 | pub cursor: usize, | 6 | pub cursor: usize, |
7 | } | 7 | } |
8 | 8 | ||
9 | // cursor value of 0 is behind all text | ||
10 | // cursor value of n is after n characters (insert after index n - 1) | ||
11 | // cursor value of text.len() is after all text | ||
12 | |||
13 | impl CommandBox { | 9 | impl CommandBox { |
14 | pub fn new() -> Self { | 10 | pub fn new() -> Self { |
15 | CommandBox { | 11 | CommandBox { |
16 | enabled: false, | 12 | history: History::new(64), |
17 | history: vec![], | 13 | hist_idx: None, |
18 | text: String::new(), | 14 | text: String::new(), |
19 | cursor: 0, | 15 | cursor: 0, |
20 | } | 16 | } |
@@ -26,6 +22,14 @@ impl CommandBox { | |||
26 | } | 22 | } |
27 | } | 23 | } |
28 | 24 | ||
25 | fn cursor_end(&mut self) { | ||
26 | self.cursor = self.text.len(); | ||
27 | } | ||
28 | |||
29 | fn cursor_start(&mut self) { | ||
30 | self.cursor = 0; | ||
31 | } | ||
32 | |||
29 | pub fn backward(&mut self) { | 33 | pub fn backward(&mut self) { |
30 | self.cursor = self.cursor.saturating_sub(1); | 34 | self.cursor = self.cursor.saturating_sub(1); |
31 | } | 35 | } |
@@ -56,10 +60,73 @@ impl CommandBox { | |||
56 | self.text.clear(); | 60 | self.text.clear(); |
57 | self.cursor = 0; | 61 | self.cursor = 0; |
58 | } | 62 | } |
63 | |||
64 | pub fn hist_append(&mut self) { | ||
65 | self.history.append(self.text.drain(..).collect()); | ||
66 | self.cursor_start(); | ||
67 | } | ||
68 | |||
69 | fn get_from_hist(&self) -> String { | ||
70 | let size = self.history.items.len(); | ||
71 | self.history.items[size - 1 - self.hist_idx.unwrap()].clone() | ||
72 | } | ||
73 | |||
74 | pub fn hist_prev(&mut self) { | ||
75 | if let Some(idx) = self.hist_idx { | ||
76 | if !(idx + 1 >= self.history.items.len()) { | ||
77 | self.hist_idx = Some(idx + 1); | ||
78 | self.text = self.get_from_hist(); | ||
79 | self.cursor_end(); | ||
80 | } | ||
81 | } else { | ||
82 | self.hist_idx = Some(0); | ||
83 | self.text = self.get_from_hist(); | ||
84 | self.cursor_end(); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | pub fn hist_next(&mut self) { | ||
89 | if let Some(idx) = self.hist_idx { | ||
90 | // most recent hist item, reset command box | ||
91 | if idx == 0 { | ||
92 | self.hist_idx = None; | ||
93 | self.text = "(".into(); | ||
94 | } else { | ||
95 | self.hist_idx = Some(idx - 1); | ||
96 | self.text = self.get_from_hist(); | ||
97 | } | ||
98 | self.cursor_end(); | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | |||
103 | #[derive(Debug)] | ||
104 | pub struct History<T> { | ||
105 | pub items: Vec<T>, | ||
106 | pub max_size: usize, | ||
107 | } | ||
108 | |||
109 | impl<T> History<T> { | ||
110 | pub fn new(max_size: usize) -> Self { | ||
111 | if max_size == 0 { | ||
112 | panic!(); | ||
113 | } | ||
114 | Self { | ||
115 | items: vec![], | ||
116 | max_size, | ||
117 | } | ||
118 | } | ||
119 | |||
120 | pub fn append(&mut self, item: T) { | ||
121 | if self.items.len() >= self.max_size { | ||
122 | self.items.remove(0); | ||
123 | } | ||
124 | self.items.push(item); | ||
125 | } | ||
59 | } | 126 | } |
60 | 127 | ||
61 | #[cfg(test)] | 128 | #[cfg(test)] |
62 | mod tests { | 129 | mod command_tests { |
63 | use super::*; | 130 | use super::*; |
64 | 131 | ||
65 | fn setup_with(text: &str) -> CommandBox { | 132 | fn setup_with(text: &str) -> CommandBox { |
@@ -131,4 +198,60 @@ mod tests { | |||
131 | cmd.forward(); | 198 | cmd.forward(); |
132 | assert_eq!(cmd.cursor, 1); | 199 | assert_eq!(cmd.cursor, 1); |
133 | } | 200 | } |
201 | |||
202 | #[test] | ||
203 | fn hist_append() { | ||
204 | let mut cmd = setup_with("hello"); | ||
205 | cmd.hist_append(); | ||
206 | cmd.push_str("another"); | ||
207 | cmd.hist_append(); | ||
208 | cmd.push_str("one"); | ||
209 | cmd.hist_append(); | ||
210 | assert_eq!(cmd.history.items.len(), 3); | ||
211 | } | ||
212 | |||
213 | #[test] | ||
214 | fn hist_prev() { | ||
215 | let mut cmd = setup_with("hello"); | ||
216 | cmd.hist_append(); | ||
217 | cmd.push_str("another"); | ||
218 | cmd.hist_append(); | ||
219 | cmd.push_str("one"); | ||
220 | cmd.hist_append(); | ||
221 | |||
222 | cmd.hist_prev(); | ||
223 | assert_eq!(&cmd.text, "one"); | ||
224 | cmd.hist_prev(); | ||
225 | assert_eq!(&cmd.text, "another"); | ||
226 | } | ||
227 | |||
228 | #[test] | ||
229 | fn hist_next() { | ||
230 | let mut cmd = setup_with("hello"); | ||
231 | cmd.hist_append(); | ||
232 | cmd.push_str("another"); | ||
233 | cmd.hist_append(); | ||
234 | cmd.push_str("one"); | ||
235 | cmd.hist_append(); | ||
236 | |||
237 | cmd.hist_prev(); | ||
238 | cmd.hist_prev(); | ||
239 | cmd.hist_next(); | ||
240 | assert_eq!(&cmd.text, "one"); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | #[cfg(test)] | ||
245 | mod history_tests { | ||
246 | use super::*; | ||
247 | #[test] | ||
248 | fn append() { | ||
249 | let mut h = History::<u32>::new(4); | ||
250 | h.append(5); | ||
251 | h.append(6); | ||
252 | h.append(7); | ||
253 | h.append(8); | ||
254 | h.append(9); | ||
255 | assert_eq!(h.items, vec![6, 7, 8, 9]); | ||
256 | } | ||
134 | } | 257 | } |