xref: /Held/src/utils/ui/mode/mode.rs (revision 984a200e159c143eb730a2df362731fe1a62fe01)
1 use std::sync::atomic::Ordering;
2 use std::sync::{Mutex, MutexGuard};
3 use std::{fmt::Debug, io};
4 
5 use crate::config::lastline_cmd::LastLineCommand;
6 use crate::utils::buffer::LineState;
7 #[cfg(feature = "dragonos")]
8 use crate::utils::input::KeyEventType;
9 
10 use crate::utils::terminal::TermManager;
11 
12 use crate::utils::ui::uicore::{UiCore, APP_INFO, TAB_SIZE};
13 use crate::utils::ui::{
14     event::KeyEventCallback,
15     uicore::{CONTENT_WINSIZE, DEF_STYLE},
16 };
17 
18 use crate::utils::ui::event::WarpUiCallBackType;
19 
20 pub trait InputMode: KeyEventCallback + Debug {
21     fn mode_type(&self) -> ModeType;
22 
23     #[cfg(not(feature = "dragonos"))]
24     fn event_route(
25         &self,
26         ui: &mut MutexGuard<UiCore>,
27         event: crossterm::event::Event,
28     ) -> io::Result<WarpUiCallBackType> {
29         match event {
30             crossterm::event::Event::FocusGained => todo!(),
31             crossterm::event::Event::FocusLost => todo!(),
32             crossterm::event::Event::Key(key) => self.key_event_route(ui, key),
33             crossterm::event::Event::Mouse(_) => todo!(),
34             crossterm::event::Event::Paste(_) => todo!(),
35             crossterm::event::Event::Resize(_, _) => todo!(),
36         }
37     }
38 
39     #[cfg(not(feature = "dragonos"))]
40     fn key_event_route(
41         &self,
42         ui: &mut MutexGuard<UiCore>,
43         keyev: crossterm::event::KeyEvent,
44     ) -> io::Result<WarpUiCallBackType> {
45         let callback = match keyev.code {
46             crossterm::event::KeyCode::Backspace => self.backspace(ui)?,
47             crossterm::event::KeyCode::Enter => self.enter(ui)?,
48             crossterm::event::KeyCode::Left => self.left(ui)?,
49             crossterm::event::KeyCode::Right => self.right(ui)?,
50             crossterm::event::KeyCode::Up => self.up(ui)?,
51             crossterm::event::KeyCode::Down => self.down(ui)?,
52             crossterm::event::KeyCode::Home => todo!(),
53             crossterm::event::KeyCode::End => todo!(),
54             crossterm::event::KeyCode::PageUp => todo!(),
55             crossterm::event::KeyCode::PageDown => todo!(),
56             crossterm::event::KeyCode::Tab => self.tab(ui)?,
57             crossterm::event::KeyCode::BackTab => todo!(),
58             crossterm::event::KeyCode::Delete => todo!(),
59             crossterm::event::KeyCode::Insert => todo!(),
60             crossterm::event::KeyCode::F(_) => todo!(),
61             crossterm::event::KeyCode::Char(c) => self.input_data(ui, &[c as u8])?,
62             crossterm::event::KeyCode::Null => todo!(),
63             crossterm::event::KeyCode::Esc => self.esc(ui)?,
64             crossterm::event::KeyCode::CapsLock => todo!(),
65             crossterm::event::KeyCode::ScrollLock => todo!(),
66             crossterm::event::KeyCode::NumLock => todo!(),
67             crossterm::event::KeyCode::PrintScreen => todo!(),
68             crossterm::event::KeyCode::Pause => todo!(),
69             crossterm::event::KeyCode::Menu => todo!(),
70             crossterm::event::KeyCode::KeypadBegin => todo!(),
71             crossterm::event::KeyCode::Media(_) => todo!(),
72             crossterm::event::KeyCode::Modifier(_) => todo!(),
73         };
74 
75         Ok(callback)
76     }
77 
78     #[cfg(feature = "dragonos")]
79     fn key_event_route(
80         &self,
81         ui: &mut MutexGuard<UiCore>,
82         key: KeyEventType,
83     ) -> io::Result<WarpUiCallBackType> {
84         match key {
85             KeyEventType::Common(c) => self.input_data(ui, &[c]),
86             KeyEventType::Up => self.up(ui),
87             KeyEventType::Down => self.down(ui),
88             KeyEventType::Right => self.right(ui),
89             KeyEventType::Left => self.left(ui),
90             KeyEventType::Enter => self.enter(ui),
91             KeyEventType::Tab => self.tab(ui),
92             KeyEventType::Backspace => self.backspace(ui),
93             KeyEventType::Esc => self.esc(ui),
94             KeyEventType::Unknown(_) => {
95                 ui.update_bottom_state_bar()?;
96                 Ok(WarpUiCallBackType::None)
97             }
98         }
99     }
100 }
101 
102 #[derive(Debug, PartialEq, Clone, Copy)]
103 pub enum ModeType {
104     Command,
105     LastLine,
106     Insert,
107 }
108 
109 impl InputMode for Command {
110     fn mode_type(&self) -> ModeType {
111         ModeType::Command
112     }
113 }
114 impl InputMode for LastLine {
115     fn mode_type(&self) -> ModeType {
116         ModeType::LastLine
117     }
118 }
119 impl InputMode for Insert {
120     fn mode_type(&self) -> ModeType {
121         ModeType::Insert
122     }
123 }
124 
125 #[derive(Debug)]
126 pub struct Command;
127 
128 impl Command {
129     pub fn jump_to_next_flag(
130         &self,
131         ui: &mut MutexGuard<UiCore>,
132         flags: LineState,
133     ) -> io::Result<()> {
134         let offset = ui.buffer.offset();
135         let y = ui.cursor.y() as usize;
136 
137         let start_line_number = offset + y + 1;
138         if start_line_number >= ui.buffer.line_count() {
139             return Ok(());
140         }
141 
142         let content = &ui.buffer.all_buffer()[start_line_number..];
143 
144         // 下一个flaged位置
145         let idx = content.iter().position(|x| x.flags.contains(flags));
146 
147         if idx.is_some() {
148             // y + idx
149             let line_number = start_line_number + idx.unwrap();
150             let new_y = ui.buffer.goto_line(line_number);
151             ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
152             ui.cursor.move_to_row(new_y)?;
153             ui.cursor.highlight(Some(y as u16))?;
154         }
155 
156         Ok(())
157     }
158 
159     pub fn jump_to_previous_flag(
160         &self,
161         ui: &mut MutexGuard<UiCore>,
162         flags: LineState,
163     ) -> io::Result<()> {
164         let offset = ui.buffer.offset();
165         let y = ui.cursor.y() as usize;
166         if offset == 0 && y == 0 {
167             return Ok(());
168         }
169         let end_linenumber = offset + y - 1;
170 
171         let content = &ui.buffer.all_buffer()[0..end_linenumber];
172 
173         // 下一个flaged位置
174         let idx = content.iter().rposition(|x| x.flags.contains(flags));
175 
176         if idx.is_some() {
177             // y + idx
178             let new_y = ui.buffer.goto_line(idx.unwrap());
179             ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
180             ui.cursor.move_to_row(new_y)?;
181             ui.cursor.highlight(Some(y as u16))?;
182         }
183 
184         Ok(())
185     }
186 }
187 
188 impl KeyEventCallback for Command {
189     fn backspace(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
190         Ok(WarpUiCallBackType::None)
191     }
192     fn enter(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
193         Ok(WarpUiCallBackType::None)
194     }
195 
196     fn tab(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
197         Ok(WarpUiCallBackType::None)
198     }
199 
200     fn esc(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
201         Ok(WarpUiCallBackType::None)
202     }
203 
204     fn input_data(
205         &self,
206         ui: &mut MutexGuard<UiCore>,
207         data: &[u8],
208     ) -> io::Result<WarpUiCallBackType> {
209         match data {
210             b":" => {
211                 // 保存位置
212                 ui.cursor.store_pos();
213                 return Ok(WarpUiCallBackType::ChangMode(ModeType::LastLine));
214             }
215 
216             b"i" | b"I" => {
217                 // 切换Insert模式
218                 return Ok(WarpUiCallBackType::ChangMode(ModeType::Insert));
219             }
220 
221             b"l" | b"L" => {
222                 // 设置当前行lock
223                 let flag = ui.buffer.line_flags(ui.cursor.y());
224                 let offset = ui.buffer.offset();
225                 if flag.contains(LineState::LOCKED) {
226                     ui.buffer
227                         .remove_line_flags(offset + ui.cursor.y() as usize, LineState::LOCKED);
228                 } else {
229                     ui.buffer
230                         .add_line_flags(offset + ui.cursor.y() as usize, LineState::LOCKED);
231                 }
232                 let y = ui.cursor.y();
233                 ui.render_content(y, 1)?;
234                 return Ok(WarpUiCallBackType::None);
235             }
236 
237             b"f" | b"F" => {
238                 // 设置当前行flag
239                 let flag = ui.buffer.line_flags(ui.cursor.y());
240                 let offset = ui.buffer.offset();
241                 if flag.contains(LineState::FLAGED) {
242                     ui.buffer
243                         .remove_line_flags(offset + ui.cursor.y() as usize, LineState::FLAGED);
244                 } else {
245                     ui.buffer
246                         .add_line_flags(offset + ui.cursor.y() as usize, LineState::FLAGED);
247                 }
248 
249                 let y = ui.cursor.y();
250                 ui.render_content(y, 1)?;
251                 return Ok(WarpUiCallBackType::None);
252             }
253 
254             b"q" | b"Q" => {
255                 // 跳转到上一个flag行
256                 self.jump_to_previous_flag(ui, LineState::FLAGED)?;
257                 return Ok(WarpUiCallBackType::None);
258             }
259 
260             b"w" | b"W" => {
261                 // 跳转到下一个flag行
262                 self.jump_to_next_flag(ui, LineState::FLAGED)?;
263                 return Ok(WarpUiCallBackType::None);
264             }
265 
266             b"a" | b"A" => {
267                 self.jump_to_previous_flag(ui, LineState::LOCKED)?;
268                 return Ok(WarpUiCallBackType::None);
269             }
270 
271             b"s" | b"S" => {
272                 self.jump_to_next_flag(ui, LineState::LOCKED)?;
273                 return Ok(WarpUiCallBackType::None);
274             }
275 
276             _ => {
277                 return Ok(WarpUiCallBackType::None);
278             }
279         }
280     }
281 }
282 
283 #[derive(Debug)]
284 pub struct Insert;
285 impl KeyEventCallback for Insert {
286     fn enter(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
287         let line_idx = ui.cursor.y();
288         let col = ui.cursor.x();
289 
290         let line = ui.buffer.get_line(line_idx);
291         if line.flags.contains(LineState::LOCKED) {
292             APP_INFO.lock().unwrap().info = "Row is locked".to_string();
293             return Ok(WarpUiCallBackType::None);
294         }
295         ui.buffer.input_enter(col, line_idx);
296 
297         DEF_STYLE.read().unwrap().set_content_style()?;
298         // 清空改行光标后的内容
299         TermManager::clear_until_new_line()?;
300 
301         // 执行渲染后续文本
302         ui.cursor.move_to_nextline(1)?;
303         ui.cursor.clear_current_line()?;
304 
305         let ret = ui.render_content(
306             line_idx + 1,
307             (CONTENT_WINSIZE.read().unwrap().rows - line_idx) as usize,
308         )?;
309 
310         if ret == 0 {
311             ui.scroll_up(1)?;
312             ui.render_content(
313                 line_idx + 1,
314                 (CONTENT_WINSIZE.read().unwrap().rows - line_idx) as usize,
315             )?;
316 
317             ui.cursor.move_up(1)?;
318         }
319 
320         let last = ui.cursor.y() - 1;
321         ui.cursor.highlight(Some(last))?;
322         ui.set_edited();
323         Ok(WarpUiCallBackType::None)
324     }
325 
326     fn tab(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
327         ui.set_edited();
328         let x = ui.cursor.x();
329 
330         let tab_size = TAB_SIZE.load(Ordering::SeqCst);
331         let space_size = tab_size - (x % tab_size);
332 
333         for _ in 0..space_size {
334             ui.buffer
335                 .insert_char(' ' as u8, ui.cursor.x(), ui.cursor.y());
336         }
337 
338         let y = ui.cursor.y();
339         ui.render_content(y, 1)?;
340 
341         ui.cursor.move_right(space_size)?;
342 
343         Ok(WarpUiCallBackType::None)
344     }
345 
346     fn esc(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
347         Ok(WarpUiCallBackType::ChangMode(ModeType::Command))
348     }
349 
350     fn input_data(
351         &self,
352         ui: &mut MutexGuard<UiCore>,
353         data: &[u8],
354     ) -> io::Result<WarpUiCallBackType> {
355         let x = ui.cursor.x();
356         let y = ui.cursor.y();
357 
358         let line = ui.buffer.get_line(y);
359         if line.flags.contains(LineState::LOCKED) {
360             APP_INFO.lock().unwrap().info = "Row is locked".to_string();
361             return Ok(WarpUiCallBackType::None);
362         }
363 
364         for (idx, ch) in data.iter().enumerate() {
365             ui.buffer.insert_char(*ch, x + idx as u16, y);
366         }
367 
368         let line_data = ui.buffer.get_line(y);
369 
370         // 考虑长度包含\n,所以要减1
371         ui.cursor.write(String::from_utf8_lossy(
372             &line_data.data[x as usize..(line_data.size() - 1)],
373         ))?;
374 
375         ui.cursor.move_to_columu(x + data.len() as u16)?;
376         ui.set_edited();
377         ui.cursor.highlight(None)?;
378         Ok(WarpUiCallBackType::None)
379     }
380 }
381 
382 #[derive(Debug)]
383 pub struct LastLine {
384     buf: Mutex<Vec<u8>>,
385 }
386 
387 impl LastLine {
388     pub fn new() -> Self {
389         Self {
390             buf: Mutex::new(vec![':' as u8]),
391         }
392     }
393 
394     pub fn reset(&self) {
395         self.buf.lock().unwrap().resize(1, ':' as u8);
396     }
397 }
398 
399 impl KeyEventCallback for LastLine {
400     fn enter(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
401         let mut buf = self.buf.lock().unwrap();
402         let cmd = String::from_utf8_lossy(&buf).to_string();
403 
404         let ret = LastLineCommand::process(ui, cmd);
405 
406         ui.cursor.move_to(1, u16::MAX - 1)?;
407         // ui.cursor.move_to_columu(1)?;
408         TermManager::clear_until_new_line()?;
409         ui.cursor.move_to(1, u16::MAX - 1)?;
410 
411         buf.resize(1, 0);
412         if ret == WarpUiCallBackType::None {
413             ui.cursor.restore_pos()?;
414             return Ok(WarpUiCallBackType::ChangMode(ModeType::Command));
415         }
416 
417         Ok(ret)
418     }
419 
420     fn tab(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
421         Ok(WarpUiCallBackType::None)
422     }
423 
424     fn backspace(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
425         if ui.cursor.x() == 1 {
426             return Ok(WarpUiCallBackType::None);
427         }
428 
429         self.left(ui)?;
430         self.buf.lock().unwrap().remove(ui.cursor.x() as usize);
431 
432         ui.cursor.write(' ')?;
433         self.left(ui)?;
434 
435         Ok(WarpUiCallBackType::None)
436     }
437 
438     fn up(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
439         Ok(WarpUiCallBackType::None)
440     }
441 
442     fn down(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
443         Ok(WarpUiCallBackType::None)
444     }
445 
446     fn esc(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
447         ui.cursor.restore_pos()?;
448         Ok(WarpUiCallBackType::ChangMode(ModeType::Command))
449     }
450 
451     fn input_data(
452         &self,
453         ui: &mut MutexGuard<UiCore>,
454         data: &[u8],
455     ) -> io::Result<WarpUiCallBackType> {
456         let mut buf = self.buf.lock().unwrap();
457 
458         if ui.cursor.x() == buf.len() as u16 {
459             buf.extend(data);
460         } else {
461             let index = ui.cursor.x() as usize;
462             for (i, &item) in data.iter().enumerate() {
463                 buf.insert(index + i, item);
464             }
465         }
466 
467         ui.cursor.write(String::from_utf8_lossy(&data))?;
468 
469         Ok(WarpUiCallBackType::None)
470     }
471 }
472