xref: /Held/src/config/lastline_cmd.rs (revision 5dacf00fa267a9ac7a4794894f9162d71b69c2b8)
1 use std::{collections::HashMap, sync::MutexGuard};
2 
3 use lazy_static::lazy_static;
4 
5 use crate::utils::{
6     buffer::LineState,
7     ui::{
8         event::WarpUiCallBackType,
9         uicore::{UiCore, APP_INFO, CONTENT_WINSIZE},
10         InfoLevel,
11     },
12 };
13 
14 lazy_static! {
15     static ref COMMAND: HashMap<&'static str, fn(&mut MutexGuard<UiCore>, &str) -> WarpUiCallBackType> = {
16         let mut map: HashMap<&str, fn(&mut MutexGuard<UiCore>, &str) -> WarpUiCallBackType> =
17             HashMap::new();
18         map.insert(":q!", LastLineCommand::force_exit);
19         map.insert(":q", LastLineCommand::exit_without_store);
20         map.insert(":wq", LastLineCommand::exit);
21 
22         // 跳转
23         map.insert(":goto", LastLineCommand::goto);
24         map.insert(":gt", LastLineCommand::goto);
25 
26         // 标记或锁定
27         map.insert(":flag", LastLineCommand::flag);
28         map.insert(":lock", LastLineCommand::lock);
29         map.insert(":unflag", LastLineCommand::unflag);
30         map.insert(":unlock", LastLineCommand::unlock);
31 
32         map.insert(":delete", LastLineCommand::delete_lines);
33         map.insert(":dl", LastLineCommand::delete_lines);
34 
35         map
36     };
37 }
38 
39 const EDITED_NO_STORE: &'static str = "Changes have not been saved";
40 const NOT_FOUNT_CMD: &'static str = "Command Not Fount";
41 
42 #[derive(Debug)]
43 pub struct LastLineCommand {
44     /// Command
45     pub command: String,
46     pub args: Vec<String>,
47 }
48 
49 /// 提供给用户的命令行功能
50 impl LastLineCommand {
51     pub fn process(ui: &mut MutexGuard<UiCore>, cmd: String) -> WarpUiCallBackType {
52         let args = cmd
53             .splitn(2, |x: char| x.is_ascii_whitespace())
54             .collect::<Vec<_>>();
55 
56         if let Some(func) = COMMAND.get(args[0]) {
57             let ret = if args.len() == 1 {
58                 func(ui, "")
59             } else {
60                 func(ui, &args[1])
61             };
62 
63             ret
64         } else {
65             let mut info = APP_INFO.lock().unwrap();
66             info.level = InfoLevel::Info;
67             info.info = NOT_FOUNT_CMD.to_string();
68             return WarpUiCallBackType::None;
69         }
70     }
71 
72     const fn is_split_char(x: char) -> bool {
73         x == ',' || x == ';' || x == ':' || x == '/' || x.is_ascii_whitespace()
74     }
75 
76     fn force_exit(_ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType {
77         WarpUiCallBackType::Exit(false)
78     }
79 
80     fn exit_without_store(ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType {
81         if ui.edited() {
82             // 编辑过但不保存?
83             // 更新警示信息
84             let mut info = APP_INFO.lock().unwrap();
85             info.level = InfoLevel::Warn;
86             info.info = EDITED_NO_STORE.to_string();
87             return WarpUiCallBackType::None;
88         }
89         WarpUiCallBackType::Exit(false)
90     }
91 
92     fn exit(_ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType {
93         WarpUiCallBackType::Exit(true)
94     }
95 
96     fn goto(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
97         if args.is_empty() {
98             let mut info = APP_INFO.lock().unwrap();
99             info.level = InfoLevel::Info;
100             info.info = "Useage: {goto}|{gt} {row}{' '|','|';'|':'|'/'}{col}".to_string();
101             return WarpUiCallBackType::None;
102         }
103         let (y, x) = {
104             let a = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
105 
106             if a.len() == 1 {
107                 (u16::from_str_radix(a[0], 10), Ok(1))
108             } else {
109                 (u16::from_str_radix(a[0], 10), u16::from_str_radix(a[1], 10))
110             }
111         };
112 
113         if y.is_err() {
114             let mut info = APP_INFO.lock().unwrap();
115             info.level = InfoLevel::Info;
116             info.info = "Useage: goto {row}({' '|','|';'|':'|'/'}{col})".to_string();
117             return WarpUiCallBackType::None;
118         }
119 
120         let buf_line_max = ui.buffer.line_count() as u16;
121         let content_line_max = CONTENT_WINSIZE.read().unwrap().rows;
122         let mut y = y.unwrap().min(buf_line_max);
123         let mut x = x.unwrap_or(1).min(ui.buffer.get_linesize(y));
124 
125         if y == 0 {
126             y += 1;
127         }
128         if x == 0 {
129             x += 1;
130         }
131 
132         x -= 1;
133         y -= 1;
134 
135         ui.cursor.set_prefix_mode(true);
136 
137         ui.cursor.restore_pos().unwrap();
138 
139         // if y < ui.buffer.offset() as u16 + content_line_max {
140         //     ui.buffer.set_offset(0);
141         // } else {
142         //     ui.buffer.set_offset((y - content_line_max) as usize);
143         // }
144 
145         let lasty = ui.cursor.y();
146         let y = ui.buffer.goto_line(y as usize);
147         ui.cursor.move_to(x, y).unwrap();
148 
149         let pos = ui.cursor.store_tmp_pos();
150         ui.render_content(0, content_line_max as usize).unwrap();
151         ui.cursor.restore_tmp_pos(pos).unwrap();
152 
153         ui.cursor.highlight(Some(lasty)).unwrap();
154 
155         ui.cursor.store_pos();
156 
157         return WarpUiCallBackType::None;
158     }
159 
160     // 标记行
161     pub fn flag(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
162         let args = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
163 
164         if args.len() == 0 {
165             ui.buffer
166                 .add_line_flags(ui.cursor.cmd_y() as usize - 1, LineState::FLAGED);
167         }
168 
169         for s in args {
170             let line = usize::from_str_radix(s, 10);
171             if line.is_err() {
172                 APP_INFO.lock().unwrap().info = format!("\"{s}\" is not a number");
173                 return WarpUiCallBackType::None;
174             }
175 
176             let line = line.unwrap();
177             ui.buffer.add_line_flags(line - 1, LineState::FLAGED);
178         }
179 
180         WarpUiCallBackType::None
181     }
182 
183     // 锁定行
184     pub fn lock(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
185         let args = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
186 
187         if args.len() == 0 {
188             ui.buffer
189                 .add_line_flags(ui.cursor.cmd_y() as usize - 1, LineState::LOCKED);
190         }
191 
192         for s in args {
193             let line = usize::from_str_radix(s, 10);
194             if line.is_err() {
195                 APP_INFO.lock().unwrap().info = format!("\"{s}\" is not a number");
196                 return WarpUiCallBackType::None;
197             }
198 
199             let line = line.unwrap();
200             ui.buffer.add_line_flags(line - 1, LineState::LOCKED);
201         }
202 
203         WarpUiCallBackType::None
204     }
205 
206     // 标记行
207     pub fn unflag(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
208         let args = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
209 
210         if args.len() == 0 {
211             ui.buffer
212                 .remove_line_flags(ui.cursor.cmd_y() as usize - 1, LineState::FLAGED);
213         }
214 
215         for s in args {
216             let line = usize::from_str_radix(s, 10);
217             if line.is_err() {
218                 APP_INFO.lock().unwrap().info = format!("\"{s}\" is not a number");
219                 return WarpUiCallBackType::None;
220             }
221 
222             let line = line.unwrap();
223             ui.buffer.remove_line_flags(line - 1, LineState::FLAGED);
224         }
225 
226         WarpUiCallBackType::None
227     }
228 
229     // 锁定行
230     pub fn unlock(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
231         let args = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
232 
233         if args.len() == 0 {
234             ui.buffer
235                 .remove_line_flags(ui.cursor.cmd_y() as usize - 1, LineState::LOCKED);
236         }
237 
238         for s in args {
239             let line = usize::from_str_radix(s, 10);
240             if line.is_err() {
241                 APP_INFO.lock().unwrap().info = format!("\"{s}\" is not a number");
242                 return WarpUiCallBackType::None;
243             }
244 
245             let line = line.unwrap();
246             ui.buffer.remove_line_flags(line - 1, LineState::LOCKED);
247         }
248 
249         WarpUiCallBackType::None
250     }
251 
252     pub fn delete_lines(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
253         let args = args.split(|x| x == '-').collect::<Vec<_>>();
254 
255         if args.len() == 0 {
256             // 删除当前行
257             let offset = ui.buffer.offset() + ui.cursor.y() as usize;
258             let count = ui.buffer.delete_lines(offset, offset + 1);
259             if count != 0 {
260                 APP_INFO.lock().unwrap().info = format!("Successfully deleted {count} row");
261             }
262             ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)
263                 .unwrap();
264             return WarpUiCallBackType::None;
265         }
266 
267         if args.len() == 1 {
268             let line = usize::from_str_radix(args[0], 10);
269             if line.is_err() {
270                 APP_INFO.lock().unwrap().info = format!("\"{}\" is not a number", args[0]);
271                 return WarpUiCallBackType::None;
272             }
273 
274             let line = line.unwrap();
275 
276             let offset = ui.buffer.offset() + line;
277             let count = ui.buffer.delete_lines(offset, offset + 1);
278             if count != 0 {
279                 APP_INFO.lock().unwrap().info = format!("Successfully deleted {count} row");
280             }
281             ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)
282                 .unwrap();
283             return WarpUiCallBackType::None;
284         }
285 
286         let start = usize::from_str_radix(args[0], 10);
287         let end = usize::from_str_radix(args[1], 10);
288 
289         if start.is_err() || end.is_err() {
290             APP_INFO.lock().unwrap().info = "Useage: (dl)|(delete) {start}({'-'}{end})".to_string();
291             return WarpUiCallBackType::None;
292         }
293 
294         let count = ui.buffer.delete_lines(start.unwrap() - 1, end.unwrap() - 1);
295         if count != 0 {
296             APP_INFO.lock().unwrap().info = format!("Successfully deleted {count} row");
297         }
298 
299         ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)
300             .unwrap();
301 
302         return WarpUiCallBackType::None;
303     }
304 }
305