xref: /Held/src/utils/ui/mode/normal.rs (revision fcc6ced0d18453415ce4b88b047a4e8e424fc693)
1 // 为了不影响 Command 模式的功能,参考Vim源码单独实现临时的 Normal 模式用于演示
2 
3 // Normal模式下的状态机
4 
5 // 在 input_data() 中处理输入的数据,根据输入的数据进行状态转移,
6 // 具体而言,根据输入数据及当前状态来更新状态机的参数,如命令字符,重复次数等
7 
8 // 在 handle() 中处理状态的转移,根据状态的变化执行相应的操作
9 // handle() 是真正作用于 ui 和 buffer 的地方,可以在这里调用 buffer 的方法,更新 ui 的显示
10 // 因此,NormalState 提供给 handle() 的参数应该具有足够的一般性,以适应不同的需求
11 
12 // 在 exit() 中处理状态的退出,清空状态
13 
14 // 由此为 ndw,ndd 等命令的实现提供了多重字符匹配之外的方案:
15 // 状态机的下一个状态仅由输入 + 当前状态决定,避免了对输入的全局匹配
16 
17 // 另:静态 HashMap 比起 match 性能更好是真的吗?后续是否考虑更换为 HashMap?
18 // 另:在现有框架下,增加一个新功能,需要在 input_data() 中增加一个分支,handle() 中增加一个分支
19 // 是否有更简便的代码结构?
20 
21 // 参考:https://github.com/neovim/neovim/blob/master/src/nvim/normal.c#L89
22 
23 use lazy_static::lazy_static;
24 
25 use crate::utils::ui::event::KeyEventCallback;
26 use crate::utils::ui::event::WarpUiCallBackType;
27 use crate::utils::ui::uicore::UiCore;
28 use crate::utils::ui::uicore::CONTENT_WINSIZE;
29 use std::io;
30 use std::sync::{Mutex, MutexGuard};
31 
32 use super::common::CommonOp;
33 use super::mode::ModeType;
34 
35 #[derive(Debug)]
36 #[allow(dead_code)]
37 pub enum BufOpArg {
38     Around,    // 操作引号内乃至引号的内容
39     Inside,    // 操作引号内的内容
40     Line,      // 操作整行
41     Word,      // 操作单词
42     WordEnd,   // 操作单词的末尾
43     WordBegin, // 操作单词的开头
44     Block,     // 操作块
45 }
46 
47 #[derive(Debug)]
48 pub struct NormalState {
49     pub cmdchar: Option<char>,
50     pub count: Option<usize>,
51     pub count0: bool,
52     pub start_pos: Option<(u16, u16)>,
53     pub end_pos: Option<(u16, u16)>,
54     pub cmdbuf: Vec<u8>,
55     pub buf_op_arg: Option<BufOpArg>,
56 }
57 
58 impl CommonOp for NormalState {}
59 
60 lazy_static! {
61     static ref NORMALSTATE: Mutex<NormalState> = Mutex::new(NormalState {
62         cmdchar: None,       // 命令开头的字符,通常决定了一类功能,如dw,dd系列命令
63         count: None,         // 命令的重复次数,如3j,4k
64         count0: false,       // 是否将0作为命令的一部分,在normal模式下,0是一个独立的命令,也可能是一个数字的一部分
65         start_pos: None,     // 作用区域的起始位置
66         end_pos: None,       // 作用区域的结束位置
67         cmdbuf: Vec::new(),  // 用于存储输入的命令,可以与状态的显示通用?
68         buf_op_arg: None // 用于指定操作的区域,如daw,diw
69     });
70 }
71 
72 #[derive(Debug)]
73 pub(crate) struct Normal;
74 impl Normal {
75     pub fn new() -> Self {
76         Self {}
77     }
78 }
79 
80 impl KeyEventCallback for Normal {
81     fn backspace(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
82         return Ok(WarpUiCallBackType::None);
83     }
84     fn esc(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
85         return Ok(WarpUiCallBackType::ChangMode(ModeType::Command));
86     }
87 
88     fn enter(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
89         return Ok(WarpUiCallBackType::None);
90     }
91     fn tab(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
92         return Ok(WarpUiCallBackType::None);
93     }
94     fn input_data(
95         &self,
96         ui: &mut MutexGuard<UiCore>,
97         data: &[u8],
98     ) -> io::Result<WarpUiCallBackType> {
99         let mut normal_state = NORMALSTATE.lock().unwrap();
100         normal_state.cmdbuf.extend_from_slice(data);
101         match data {
102             b"h" => {
103                 normal_state.on_h_clicked();
104             }
105             b"j" => {
106                 normal_state.on_j_clicked();
107             }
108             b"k" => {
109                 normal_state.on_k_clicked();
110             }
111             b"l" => {
112                 normal_state.on_l_clicked();
113             }
114             b"i" => {
115                 normal_state.on_i_clicked();
116             }
117             b"d" => {
118                 normal_state.on_d_clicked();
119             }
120             [b'1'..=b'9'] => {
121                 normal_state.on_nonzero_clicked(data);
122             }
123             b"0" => {
124                 normal_state.on_zero_clicked();
125             }
126             b"w" => {
127                 normal_state.on_w_clicked();
128             }
129             b"g" => {
130                 normal_state.on_g_clicked(ui);
131             }
132             b"G" => {
133                 normal_state.on_G_clicked(ui);
134             }
135             b"b" => {
136                 normal_state.on_b_clicked(ui);
137             }
138             b":" => {
139                 if normal_state.cmdchar.is_none() {
140                     ui.cursor.store_pos();
141                     return Ok(WarpUiCallBackType::ChangMode(ModeType::LastLine));
142                 }
143             }
144             b"$" => {
145                 normal_state.on_dollar_clicked();
146             }
147             b"e" => {
148                 normal_state.on_e_clicked(ui);
149             }
150             b"f" => {
151                 normal_state.on_f_clicked();
152             }
153             b"F" => {
154                 normal_state.on_F_clicked();
155             }
156             b"x" => {
157                 normal_state.on_x_clicked();
158             }
159             _ => {}
160         }
161         return normal_state.handle(ui);
162     }
163 }
164 
165 impl KeyEventCallback for NormalState {
166     fn backspace(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
167         ui.cursor.move_left(1)?;
168         return Ok(WarpUiCallBackType::None);
169     }
170 
171     fn esc(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
172         return Ok(WarpUiCallBackType::ChangMode(ModeType::Command));
173     }
174 
175     fn enter(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
176         return Ok(WarpUiCallBackType::None);
177     }
178 
179     fn tab(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
180         return Ok(WarpUiCallBackType::None);
181     }
182 
183     fn input_data(
184         &self,
185         _ui: &mut MutexGuard<UiCore>,
186         _data: &[u8],
187     ) -> io::Result<WarpUiCallBackType> {
188         return Ok(WarpUiCallBackType::None);
189     }
190 }
191 impl NormalState {
192     pub fn reset(&mut self) {
193         self.cmdchar = None;
194         self.count = None;
195         self.count0 = false;
196         self.start_pos = None;
197         self.end_pos = None;
198         self.cmdbuf.clear();
199         self.buf_op_arg = None;
200     }
201 
202     pub fn exec_0_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
203         ui.cursor.move_to_columu(0)?;
204         self.reset();
205         return Ok(WarpUiCallBackType::None);
206     }
207 
208     pub fn on_h_clicked(&mut self) {
209         if self.cmdchar.is_none() {
210             self.cmdchar = Some('h');
211         }
212     }
213     /// 向左移动数列
214     pub fn exec_h_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
215         let old_x = ui.cursor.x();
216         let exec_count = match self.count {
217             Some(count) => count.min(old_x as usize),
218             None => {
219                 if old_x == 0 {
220                     0
221                 } else {
222                     1
223                 }
224             } // 如果在第一列,不再向左移动,防止溢出
225         };
226         let new_x = old_x - exec_count as u16;
227         ui.cursor.move_to_columu(new_x)?;
228         self.reset();
229         return Ok(WarpUiCallBackType::None);
230     }
231 
232     pub fn on_j_clicked(&mut self) {
233         self.cmdchar = Some('j');
234     }
235     /// 向下移动数行
236     pub fn exec_j_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
237         let old_y = ui.cursor.y();
238         let old_abs_y = old_y + ui.buffer.offset() as u16;
239         // 限制最大移动行数
240         let exec_count = match self.count {
241             Some(count) => count.min(ui.buffer.line_count() - old_abs_y as usize - 1),
242             None => 1, // goto_line 会自动处理最大值
243         };
244         let old_offset = ui.buffer.offset();
245         let new_y = ui.buffer.goto_line(old_abs_y as usize + exec_count);
246         let new_linesize = ui.buffer.get_linesize(new_y);
247         let new_x = if new_linesize < ui.cursor.x() {
248             // 如果新行的长度小于原来的x坐标,将光标移动到新行的最后一个字符
249             new_linesize - 1
250         } else {
251             ui.cursor.x()
252         };
253         ui.cursor.move_to(new_x, new_y)?;
254         ui.cursor.highlight(Some(old_y))?;
255         // 如果移动后,buffer的offset发生了变化,需要重新渲染
256         if ui.buffer.offset() != old_offset {
257             ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
258         }
259         self.reset();
260         return Ok(WarpUiCallBackType::None);
261     }
262     pub fn on_k_clicked(&mut self) {
263         if self.cmdchar.is_none() {
264             self.cmdchar = Some('k');
265         }
266     }
267 
268     /// 向上移动数行
269     pub fn exec_k_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
270         let old_y = ui.cursor.y();
271         let old_abs_y = old_y + ui.buffer.offset() as u16;
272         // 限制最大移动行数
273         let exec_count = match self.count {
274             Some(count) => count.min(old_y as usize + ui.buffer.offset()),
275             None => {
276                 if old_abs_y == 0 {
277                     0
278                 } else {
279                     1
280                 }
281             } // 如果在第一行,不再向上移动,防止溢出
282         };
283         let to_line = old_abs_y as usize - exec_count;
284         let old_offset = ui.buffer.offset();
285         let new_y = ui.buffer.goto_line(to_line);
286         let new_linesize = ui.buffer.get_linesize(new_y);
287         let new_x = if new_linesize < ui.cursor.x() {
288             // 如果新行的长度小于原来的x坐标,将光标移动到新行的最后一个字符
289             new_linesize - 1
290         } else {
291             ui.cursor.x()
292         };
293         ui.cursor.move_to(new_x, new_y)?;
294         ui.cursor.highlight(Some(old_y))?;
295         // 如果移动后,buffer的offset发生了变化,需要重新渲染
296         if old_offset != ui.buffer.offset() {
297             ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
298         }
299         self.reset();
300         return Ok(WarpUiCallBackType::None);
301     }
302 
303     pub fn on_l_clicked(&mut self) {
304         if self.cmdchar.is_none() {
305             self.cmdchar = Some('l');
306         }
307     }
308 
309     /// 向右移动数列
310     pub fn exec_l_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
311         let old_x = ui.cursor.x();
312         let linesize = ui.buffer.get_linesize(ui.cursor.y()) as usize;
313         let max_count = linesize - old_x as usize - 1;
314         let exec_count = match self.count {
315             Some(count) => count.min(max_count),
316             None => {
317                 if old_x == linesize as u16 - 1 {
318                     0
319                 } else {
320                     1
321                 }
322             }
323         };
324         let new_x = old_x + exec_count as u16;
325         ui.cursor.move_to_columu(new_x)?;
326         self.reset();
327         return Ok(WarpUiCallBackType::None);
328     }
329 
330     pub fn on_i_clicked(&mut self) {
331         if self.cmdchar.is_none() {
332             self.cmdchar = Some('i');
333         }
334     }
335     pub fn exec_i_cmd(&mut self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
336         self.exit()?;
337         return Ok(WarpUiCallBackType::ChangMode(ModeType::Insert));
338     }
339 
340     /// 处理输入的非零数字
341     pub fn on_nonzero_clicked(&mut self, data: &[u8]) {
342         let count = self.count;
343         if count.is_none() {
344             // 如果count为空,将第一个输入的数字作为count
345             let count = data[0] - b'0';
346             self.count = Some(count as usize);
347         } else {
348             // 如果count不为空,将输入的数字添加到count的末尾
349             let mut count = count.unwrap();
350             count = count * 10 + (data[0] - b'0') as usize;
351             self.count = Some(count);
352         }
353         self.count0 = true; // 将后续输入的0作为执行次数的一部分
354     }
355 
356     /// 处理输入的0
357     pub fn on_zero_clicked(&mut self) {
358         // 如果0是命令的一部分,不再处理
359         if !self.count0 && self.cmdchar.is_none() {
360             self.cmdchar = Some('0');
361             self.count0 = true;
362         }
363         let count = self.count;
364         // 如果输入的是0,且count不为空,将count扩大10倍
365         if count.is_some() {
366             let mut count = count.unwrap();
367             count = count * 10;
368             self.count = Some(count);
369         }
370     }
371 
372     /// 处理输入的d
373     pub fn on_d_clicked(&mut self) {
374         match self.cmdchar {
375             None => {
376                 // 处理d
377                 self.cmdchar = Some('d');
378             }
379             Some('d') => {
380                 // 处理dd
381                 self.buf_op_arg = Some(BufOpArg::Line);
382             }
383             _ => {
384                 self.reset();
385             }
386         }
387     }
388 
389     pub fn exec_d_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
390         let count = match self.count {
391             Some(count) => count as u16,
392             None => 1,
393         };
394         match self.buf_op_arg {
395             Some(BufOpArg::Line) => {
396                 // 删除行
397                 let result = self
398                     .remove_n_line(ui, count)
399                     .map(|_| WarpUiCallBackType::None);
400                 self.reset();
401                 return result;
402             }
403             Some(BufOpArg::Word) => {
404                 // 删除单词
405                 for _ in 0..count {
406                     self.remove_word(ui)?;
407                 }
408                 self.reset();
409                 return Ok(WarpUiCallBackType::None);
410             }
411             _ => {
412                 return Ok(WarpUiCallBackType::None);
413             }
414         }
415     }
416 
417     pub fn on_w_clicked(&mut self) {
418         if self.cmdchar.is_none() {
419             // 按单词移动
420             self.cmdchar = Some('w');
421         } else {
422             // 按单词操作,具体由self.cmdchar决定
423             self.buf_op_arg = Some(BufOpArg::Word);
424         }
425     }
426 
427     pub fn exec_w_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
428         let count = match self.count {
429             Some(count) => count,
430             None => 1,
431         };
432         for _ in 0..count {
433             self.jump_to_next_word(ui)?;
434         }
435         self.reset();
436         return Ok(WarpUiCallBackType::None);
437     }
438 
439     fn on_g_clicked(&mut self, ui: &mut MutexGuard<UiCore>) {
440         if self.cmdchar.is_none() {
441             self.cmdchar = Some('g');
442         } else {
443             let first_line_size = ui.buffer.get_linesize(0);
444             self.end_pos = Some((ui.cursor.x().min(first_line_size - 1), 0));
445         }
446     }
447 
448     fn exec_g_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
449         let end_pos = self.end_pos;
450         if end_pos.is_none() {
451             return Ok(WarpUiCallBackType::None);
452         }
453         let old_y = ui.cursor.y();
454         let (x, y) = end_pos.unwrap();
455         let y = ui.buffer.goto_line(y.into());
456         ui.cursor.move_to(x as u16, y as u16)?;
457         ui.render_content(y, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
458         ui.cursor.highlight(Some(old_y))?;
459         self.reset();
460         return Ok(WarpUiCallBackType::None);
461     }
462 
463     #[allow(non_snake_case)]
464     fn on_G_clicked(&mut self, _ui: &mut MutexGuard<UiCore>) {
465         if self.cmdchar.is_none() {
466             self.cmdchar = Some('G');
467         }
468     }
469 
470     #[allow(non_snake_case)]
471     fn exec_G_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
472         let lineidx = match self.count {
473             Some(count) => count - 1,
474             None => ui.buffer.line_count() - 1,
475         };
476         self.move_to_line(ui, lineidx as u16)?;
477         self.reset();
478         return Ok(WarpUiCallBackType::None);
479     }
480 
481     fn on_b_clicked(&mut self, ui: &mut MutexGuard<UiCore>) {
482         if self.cmdchar.is_none() {
483             self.cmdchar = Some('b');
484         } else {
485             self.buf_op_arg = Some(BufOpArg::WordBegin);
486         }
487         let count = match self.count {
488             Some(count) => count,
489             None => 1,
490         };
491         let mut pos = (ui.cursor.x(), ui.cursor.y() + ui.buffer.offset() as u16);
492         for _ in 0..count {
493             pos = self.locate_prevw_begin(ui, pos.0, pos.1);
494         }
495         self.end_pos = Some(pos);
496     }
497 
498     fn exec_b_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
499         let end_pos = self.end_pos.unwrap();
500         self.move_to_line(ui, end_pos.1)?;
501         ui.cursor.move_to_columu(end_pos.0)?;
502         self.reset();
503         return Ok(WarpUiCallBackType::None);
504     }
505 
506     fn on_dollar_clicked(&mut self) {
507         if self.cmdchar.is_none() {
508             self.cmdchar = Some('$');
509         }
510     }
511 
512     fn exec_dollar_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
513         let line_end = ui.buffer.get_linesize(ui.cursor.y()) as u16 - 1;
514         ui.cursor.move_to_columu(line_end)?;
515         self.reset();
516         return Ok(WarpUiCallBackType::None);
517     }
518 
519     fn on_e_clicked(&mut self, ui: &mut MutexGuard<UiCore>) {
520         if self.cmdchar.is_none() {
521             self.cmdchar = Some('e');
522         } else {
523             self.buf_op_arg = Some(BufOpArg::WordEnd);
524         }
525         let count = match self.count {
526             Some(count) => count,
527             None => 1,
528         };
529         let mut pos = (ui.cursor.x(), ui.cursor.y());
530         for _ in 0..count {
531             pos = self.locate_nextw_ending(ui, pos.0, pos.1);
532         }
533         self.end_pos = Some(pos);
534     }
535 
536     fn exec_e_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
537         let end_pos = self.end_pos;
538         if end_pos.is_none() {
539             return Ok(WarpUiCallBackType::None);
540         }
541         let end_pos = end_pos.unwrap();
542         self.move_to_line(ui, end_pos.1)?;
543         ui.cursor.move_to_columu(end_pos.0)?;
544         self.reset();
545         return Ok(WarpUiCallBackType::None);
546     }
547 
548     fn on_f_clicked(&mut self) {
549         if self.cmdchar.is_none() {
550             self.cmdchar = Some('f');
551         }
552     }
553 
554     fn exec_f_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
555         if self.cmdbuf.len() < 2 {
556             return Ok(WarpUiCallBackType::None);
557         }
558         let to_find = self.cmdbuf.last().unwrap().clone() as char;
559         let old_x = ui.cursor.x();
560         let old_y = ui.cursor.y();
561         let line =
562             String::from_utf8_lossy(&ui.buffer.get_line(old_y)[old_x as usize..]).to_string();
563         let pos = line.find(to_find);
564         if pos.is_none() {
565             return Ok(WarpUiCallBackType::None);
566         }
567         ui.cursor
568             .move_to_columu((old_x + pos.unwrap() as u16) as u16)?;
569         self.reset();
570         return Ok(WarpUiCallBackType::None);
571     }
572 
573     #[allow(non_snake_case)]
574     fn on_F_clicked(&mut self) {
575         if self.cmdchar.is_none() {
576             self.cmdchar = Some('F');
577         }
578     }
579 
580     #[allow(non_snake_case)]
581     fn exec_F_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
582         if self.cmdbuf.len() < 2 {
583             return Ok(WarpUiCallBackType::None);
584         }
585         let to_find = self.cmdbuf.last().unwrap().clone() as char;
586         let old_x = ui.cursor.x();
587         let old_y = ui.cursor.y();
588         let line =
589             String::from_utf8_lossy(&ui.buffer.get_line(old_y)[..old_x as usize]).to_string();
590         let pos = line.rfind(to_find);
591         if pos.is_none() {
592             return Ok(WarpUiCallBackType::None);
593         }
594         ui.cursor.move_to_columu(pos.unwrap() as u16)?;
595         self.reset();
596         return Ok(WarpUiCallBackType::None);
597     }
598 
599     fn on_x_clicked(&mut self) {
600         if self.cmdchar.is_none() {
601             self.cmdchar = Some('x');
602         }
603     }
604 
605     fn exec_x_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
606         let y = ui.cursor.y();
607         let x = ui.cursor.x();
608         if x < ui.buffer.get_linesize(y) - 1 {
609             ui.buffer.remove_char(x, y);
610             ui.render_content(y, 1)?;
611         }
612         return Ok(WarpUiCallBackType::None);
613     }
614 }
615 
616 pub trait StateMachine {
617     fn handle(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType>;
618     fn exit(&mut self) -> io::Result<()>;
619 }
620 
621 impl StateMachine for NormalState {
622     fn handle(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
623         if self.cmdchar.is_none() {
624             return Ok(WarpUiCallBackType::None);
625         }
626         match self.cmdchar.unwrap() {
627             'h' => self.exec_h_cmd(ui),
628             'j' => self.exec_j_cmd(ui),
629             'k' => self.exec_k_cmd(ui),
630             'l' => self.exec_l_cmd(ui),
631             'i' => self.exec_i_cmd(ui),
632             '0' => self.exec_0_cmd(ui),
633             'd' => self.exec_d_cmd(ui),
634             'w' => self.exec_w_cmd(ui),
635             'g' => self.exec_g_cmd(ui),
636             'G' => self.exec_G_cmd(ui),
637             'b' => self.exec_b_cmd(ui),
638             '$' => self.exec_dollar_cmd(ui),
639             'e' => self.exec_e_cmd(ui),
640             'f' => self.exec_f_cmd(ui),
641             'F' => self.exec_F_cmd(ui),
642             'x' => self.exec_x_cmd(ui),
643             _ => return Ok(WarpUiCallBackType::None),
644         }
645     }
646 
647     fn exit(&mut self) -> io::Result<()> {
648         self.reset();
649         Ok(())
650     }
651 }
652