xref: /Held/src/config/lastline_cmd.rs (revision 7d5806d5b291a1c82166840048ed0c0986d637ec)
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 #[allow(unused)]
44 pub struct LastLineCommand {
45     /// Command
46     pub command: String,
47     pub args: Vec<String>,
48 }
49 
50 /// 提供给用户的命令行功能
51 impl LastLineCommand {
process(ui: &mut MutexGuard<UiCore>, cmd: String) -> WarpUiCallBackType52     pub fn process(ui: &mut MutexGuard<UiCore>, cmd: String) -> WarpUiCallBackType {
53         let args = cmd
54             .splitn(2, |x: char| x.is_ascii_whitespace())
55             .collect::<Vec<_>>();
56 
57         if let Some(func) = COMMAND.get(args[0]) {
58             let ret = if args.len() == 1 {
59                 func(ui, "")
60             } else {
61                 func(ui, &args[1])
62             };
63 
64             ret
65         } else {
66             let mut info = APP_INFO.lock().unwrap();
67             info.level = InfoLevel::Info;
68             info.info = NOT_FOUNT_CMD.to_string();
69             return WarpUiCallBackType::None;
70         }
71     }
72 
is_split_char(x: char) -> bool73     const fn is_split_char(x: char) -> bool {
74         x == ',' || x == ';' || x == ':' || x == '/' || x.is_ascii_whitespace()
75     }
76 
force_exit(_ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType77     fn force_exit(_ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType {
78         WarpUiCallBackType::Exit(false)
79     }
80 
exit_without_store(ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType81     fn exit_without_store(ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType {
82         if ui.edited() {
83             // 编辑过但不保存?
84             // 更新警示信息
85             let mut info = APP_INFO.lock().unwrap();
86             info.level = InfoLevel::Warn;
87             info.info = EDITED_NO_STORE.to_string();
88             return WarpUiCallBackType::None;
89         }
90         WarpUiCallBackType::Exit(false)
91     }
92 
exit(_ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType93     fn exit(_ui: &mut MutexGuard<UiCore>, _args: &str) -> WarpUiCallBackType {
94         WarpUiCallBackType::Exit(true)
95     }
96 
goto(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType97     fn goto(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
98         if args.is_empty() {
99             let mut info = APP_INFO.lock().unwrap();
100             info.level = InfoLevel::Info;
101             info.info = "Useage: {goto}|{gt} {row}{' '|','|';'|':'|'/'}{col}".to_string();
102             return WarpUiCallBackType::None;
103         }
104         let (y, x) = {
105             let a = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
106 
107             if a.len() == 1 {
108                 (u16::from_str_radix(a[0], 10), Ok(1))
109             } else {
110                 (u16::from_str_radix(a[0], 10), u16::from_str_radix(a[1], 10))
111             }
112         };
113 
114         if y.is_err() {
115             let mut info = APP_INFO.lock().unwrap();
116             info.level = InfoLevel::Info;
117             info.info = "Useage: goto {row}({' '|','|';'|':'|'/'}{col})".to_string();
118             return WarpUiCallBackType::None;
119         }
120 
121         let buf_line_max = ui.buffer.line_count() as u16;
122         let content_line_max = CONTENT_WINSIZE.read().unwrap().rows;
123         let mut y = y.unwrap().min(buf_line_max);
124         let mut x = x.unwrap_or(1).min(ui.buffer.get_linesize(y));
125 
126         if y == 0 {
127             y += 1;
128         }
129         if x == 0 {
130             x += 1;
131         }
132 
133         x -= 1;
134         y -= 1;
135 
136         ui.cursor.set_prefix_mode(true);
137 
138         ui.cursor.restore_pos().unwrap();
139 
140         // if y < ui.buffer.offset() as u16 + content_line_max {
141         //     ui.buffer.set_offset(0);
142         // } else {
143         //     ui.buffer.set_offset((y - content_line_max) as usize);
144         // }
145 
146         let lasty = ui.cursor.y();
147         let y = ui.buffer.goto_line(y as usize);
148         ui.cursor.move_to(x, y).unwrap();
149 
150         let pos = ui.cursor.store_tmp_pos();
151         ui.render_content(0, content_line_max as usize).unwrap();
152         ui.cursor.restore_tmp_pos(pos).unwrap();
153 
154         ui.cursor.highlight(Some(lasty)).unwrap();
155 
156         ui.cursor.store_pos();
157 
158         return WarpUiCallBackType::None;
159     }
160 
161     // 标记行
flag(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType162     pub fn flag(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
163         let args = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
164 
165         if args.len() == 0 {
166             ui.buffer
167                 .add_line_flags(ui.cursor.cmd_y() as usize - 1, LineState::FLAGED);
168         }
169 
170         for s in args {
171             let line = usize::from_str_radix(s, 10);
172             if line.is_err() {
173                 APP_INFO.lock().unwrap().info = format!("\"{s}\" is not a number");
174                 return WarpUiCallBackType::None;
175             }
176 
177             let line = line.unwrap();
178             ui.buffer.add_line_flags(line - 1, LineState::FLAGED);
179         }
180 
181         WarpUiCallBackType::None
182     }
183 
184     // 锁定行
lock(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType185     pub fn lock(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
186         let args = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
187 
188         match args.len() {
189             0 => {
190                 //没有参数,锁定当前行
191                 ui.buffer
192                     .add_line_flags(ui.cursor.cmd_y() as usize - 1, LineState::LOCKED)
193             }
194             _ => {
195                 //有参数,锁定指定行
196                 for arg in args {
197                     let line = usize::from_str_radix(arg, 10);
198                     if line.is_err() {
199                         APP_INFO.lock().unwrap().info = format!("\"{arg}\" is not a number");
200                         return WarpUiCallBackType::None;
201                     }
202 
203                     let line = line.unwrap();
204                     ui.buffer.add_line_flags(line - 1, LineState::LOCKED);
205                 }
206             }
207         }
208 
209         WarpUiCallBackType::None
210     }
211 
212     // 标记行
unflag(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType213     pub fn unflag(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
214         let args = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
215 
216         match args.len() {
217             0 => {
218                 //没有参数,解除标记当前行
219                 ui.buffer
220                     .remove_line_flags(ui.cursor.cmd_y() as usize - 1, LineState::FLAGED)
221             }
222             _ => {
223                 //有参数,解除标记指定行
224                 for arg in args {
225                     let line = usize::from_str_radix(arg, 10);
226                     if line.is_err() {
227                         APP_INFO.lock().unwrap().info = format!("\"{arg}\" is not a number");
228                         return WarpUiCallBackType::None;
229                     }
230 
231                     let line = line.unwrap();
232                     ui.buffer.remove_line_flags(line - 1, LineState::FLAGED);
233                 }
234             }
235         }
236 
237         WarpUiCallBackType::None
238     }
239 
240     // 解除锁定行
unlock(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType241     pub fn unlock(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
242         let args = args.split(|x| Self::is_split_char(x)).collect::<Vec<_>>();
243 
244         match args.len() {
245             0 => {
246                 //没有参数,解除锁定当前行
247                 ui.buffer
248                     .remove_line_flags(ui.cursor.cmd_y() as usize - 1, LineState::LOCKED)
249             }
250             _ => {
251                 //有参数,解除锁定指定行
252                 for arg in args {
253                     let line = usize::from_str_radix(arg, 10);
254                     if line.is_err() {
255                         APP_INFO.lock().unwrap().info = format!("\"{arg}\" is not a number");
256                         return WarpUiCallBackType::None;
257                     }
258 
259                     let line = line.unwrap();
260                     ui.buffer.remove_line_flags(line - 1, LineState::LOCKED);
261                 }
262             }
263         }
264 
265         WarpUiCallBackType::None
266     }
267 
delete_lines(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType268     pub fn delete_lines(ui: &mut MutexGuard<UiCore>, args: &str) -> WarpUiCallBackType {
269         let args = args.split(|x| x == '-').collect::<Vec<_>>();
270 
271         match args.len() {
272             0 => {
273                 let offset = ui.buffer.offset() + ui.cursor.y() as usize;
274                 let count = ui.buffer.delete_lines(offset, offset + 1);
275                 if count != 0 {
276                     APP_INFO.lock().unwrap().info = format!("Successfully deleted {count} row");
277                 }
278                 ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)
279                     .unwrap();
280                 return WarpUiCallBackType::None;
281             }
282             1 => {
283                 let line = usize::from_str_radix(args[0], 10);
284                 if line.is_err() {
285                     APP_INFO.lock().unwrap().info = format!("\"{}\" is not a number", args[0]);
286                     return WarpUiCallBackType::None;
287                 }
288 
289                 let line = line.unwrap();
290 
291                 let offset = ui.buffer.offset() + line;
292                 let count = ui.buffer.delete_lines(offset, offset + 1);
293                 if count != 0 {
294                     APP_INFO.lock().unwrap().info = format!("Successfully deleted {count} row");
295                 }
296                 ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)
297                     .unwrap();
298                 return WarpUiCallBackType::None;
299             }
300             _ => {
301                 let start = usize::from_str_radix(args[0], 10);
302                 let end = usize::from_str_radix(args[1], 10);
303 
304                 if start.is_err() || end.is_err() {
305                     APP_INFO.lock().unwrap().info =
306                         "Useage: (dl)|(delete) {start}({'-'}{end})".to_string();
307                     return WarpUiCallBackType::None;
308                 }
309 
310                 let count = ui.buffer.delete_lines(start.unwrap() - 1, end.unwrap() - 1);
311                 if count != 0 {
312                     APP_INFO.lock().unwrap().info = format!("Successfully deleted {count} row");
313                 }
314 
315                 ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)
316                     .unwrap();
317             }
318         }
319         return WarpUiCallBackType::None;
320     }
321 }
322