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