xref: /NovaShell/src/shell/mod.rs (revision 4eae9fc5e259896a1dbea68618bc343d9d1d10aa)
1 use core::fmt;
2 use std::{
3     cell::RefCell,
4     fs::{self, File, OpenOptions},
5     io::{self, stdout, BufRead, BufReader, Read, Write},
6     ops::Deref,
7     path::Path,
8     print,
9     rc::Rc,
10     string::String,
11     vec::Vec,
12 };
13 
14 use crate::keycode::{FunctionKeySuffix, SpecialKeycode};
15 
16 use colored::Colorize;
17 use command::{BuildInCmd, Command};
18 
19 pub mod command;
20 
21 pub struct Prompt {
22     user_name: String,
23     computer_name: String,
24     path: String,
25 }
26 
27 impl Prompt {
28     pub fn len(&self) -> usize {
29         format!("{}@{}:{}$ ", self.user_name, self.computer_name, self.path).len()
30     }
31 
32     pub fn update_path(&mut self) {
33         self.path = std::env::current_dir()
34             .unwrap()
35             .to_str()
36             .unwrap()
37             .to_string();
38     }
39 }
40 
41 impl fmt::Display for Prompt {
42     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43         write!(
44             f,
45             "{}:{}$ ",
46             format!("{}@{}", self.user_name, self.computer_name).bright_green(),
47             self.path.bright_cyan()
48         )
49     }
50 }
51 
52 pub struct Shell {
53     history_commands: Vec<Rc<RefCell<Vec<u8>>>>,
54     history_path: String,
55     printer: Printer,
56 }
57 
58 impl Shell {
59     pub fn new() -> Shell {
60         let mut shell = Shell {
61             history_commands: Vec::new(),
62             history_path: "history_commands.txt".to_string(),
63             printer: Printer::new(&Rc::new(RefCell::new(Vec::new()))),
64         };
65         shell.read_commands();
66         shell
67     }
68 
69     pub fn current_dir() -> String {
70         std::env::current_dir()
71             .expect("Error getting current directory")
72             .to_str()
73             .unwrap()
74             .to_string()
75     }
76 
77     pub fn chdir(&mut self, new_dir: &String) {
78         let path = Path::new(&new_dir);
79         if let Err(e) = std::env::set_current_dir(&path) {
80             eprintln!("Error changing directory: {}", e);
81         }
82     }
83 
84     pub fn exec(&mut self) {
85         crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode");
86         loop {
87             self.printer.init_before_readline();
88             if self.readline(0) == 0 {
89                 println!();
90                 break;
91             }
92             let command_bytes = self.printer.buf.borrow().clone();
93             if !command_bytes.starts_with(&[b' '])
94                 && command_bytes
95                     != self
96                         .history_commands
97                         .last()
98                         .unwrap_or(&Rc::new(RefCell::new(Vec::new())))
99                         .borrow()
100                         .clone()
101             {
102                 self.history_commands
103                     .push(Rc::new(RefCell::new(command_bytes.clone())));
104             };
105             if !command_bytes.iter().all(|&byte| byte == b' ') {
106                 self.exec_commands_in_line(&command_bytes);
107             }
108         }
109         self.write_commands();
110     }
111 
112     fn exec_commands_in_line(&mut self, command_bytes: &Vec<u8>) {
113         let commands = Command::from_strings(String::from_utf8(command_bytes.clone()).unwrap());
114         commands
115             .iter()
116             .for_each(|command| self.exec_command(command));
117     }
118 
119     pub fn read_commands(&mut self) {
120         let mut history = Vec::new();
121         for line in BufReader::new(match File::open(&self.history_path) {
122             Ok(file) => file,
123             Err(_) => File::create(&self.history_path).unwrap(),
124         })
125         .lines()
126         {
127             match line {
128                 Ok(s) => history.push(Rc::new(RefCell::new(s.into_bytes()))),
129                 Err(_) => {
130                     break;
131                 }
132             }
133         }
134         self.history_commands = history;
135     }
136 
137     fn write_commands(&self) {
138         let mut file = OpenOptions::new()
139             .append(false)
140             .open("history_commands.txt")
141             .unwrap();
142         for command_line in &self.history_commands {
143             file.write_all(&command_line.borrow()[..]).unwrap();
144             file.write_all(&[SpecialKeycode::LF.into()]).unwrap();
145         }
146     }
147 
148     fn read_char() -> u8 {
149         let mut buf: [u8; 1] = [0];
150         std::io::stdin().read(&mut buf).expect("read char error");
151         buf[0]
152     }
153 
154     fn readline(&mut self, _fd: usize) -> usize {
155         let mut stdout = std::io::stdout();
156         self.history_commands.push(Rc::clone(&self.printer.buf));
157         let mut command_index = self.history_commands.len() - 1;
158         loop {
159             let key = Self::read_char();
160             if let Ok(special_key) = SpecialKeycode::try_from(key) {
161                 match special_key {
162                     SpecialKeycode::FunctionKeyPrefix => {
163                         let key = Self::read_char();
164                         let function_key = FunctionKeySuffix::try_from(key).unwrap();
165                         match function_key {
166                             FunctionKeySuffix::Up => {
167                                 if command_index > 0 {
168                                     command_index -= 1;
169                                     self.printer.change_line(
170                                         self.history_commands.get(command_index).unwrap(),
171                                     );
172                                 }
173                             }
174 
175                             FunctionKeySuffix::Down => {
176                                 if command_index < self.history_commands.len() - 1 {
177                                     command_index += 1;
178                                     self.printer.change_line(
179                                         self.history_commands.get(command_index).unwrap(),
180                                     );
181                                 }
182                             }
183 
184                             FunctionKeySuffix::Left => {
185                                 self.printer.cursor_left();
186                             }
187 
188                             FunctionKeySuffix::Right => {
189                                 self.printer.cursor_right();
190                             }
191 
192                             FunctionKeySuffix::Home => {
193                                 self.printer.home();
194                             }
195 
196                             FunctionKeySuffix::End => {
197                                 self.printer.end();
198                             }
199                         }
200                     }
201 
202                     SpecialKeycode::LF | SpecialKeycode::CR => {
203                         println!();
204                         self.history_commands.pop();
205                         return 1;
206                     }
207 
208                     SpecialKeycode::BackSpace => {
209                         self.printer.backspace();
210                     }
211 
212                     SpecialKeycode::Delete => {
213                         self.printer.delete(1);
214                     }
215 
216                     SpecialKeycode::Tab => {
217                         let mut buf = self.printer.buf.deref().borrow().clone();
218                         buf.truncate(self.printer.cursor);
219                         let str = String::from_utf8(buf.clone()).unwrap();
220                         if buf.len() == 0 || buf.iter().all(|byte| *byte == b' ') {
221                             return 1;
222                         }
223 
224                         let iter = str.chars();
225                         let mut fragments: Vec<String> = Vec::new();
226                         let mut stack: String = String::with_capacity(str.len());
227                         let mut left_quote: char = ' ';
228                         for ch in iter {
229                             //存在未闭合的左引号,此时除能够配对的引号外,任何字符都加入栈中
230                             if left_quote != ' ' {
231                                 if ch == left_quote {
232                                     left_quote = ' ';
233                                 }
234                                 stack.push(ch);
235                             } else {
236                                 //不存在未闭合的左引号
237                                 if ch == '\'' || ch == '\"' {
238                                     //字符为引号,记录下来
239                                     left_quote = ch;
240                                     stack.push(ch);
241                                 } else if ch == ' ' {
242                                     if !stack.is_empty() {
243                                         //字符为空格且栈中不为空,该空格视作命令段之间的分割线
244                                         //将栈中字符作为一个命令段加入集合,之后重置栈
245                                         fragments.push(stack.to_string());
246                                         stack.clear();
247                                     }
248                                 } else {
249                                     //其他字符都作为普通字符加入栈中
250                                     stack.push(ch);
251                                 }
252                             }
253                         }
254                         //结束时如果栈不为空
255                         if !stack.is_empty() {
256                             fragments.push(stack.to_string());
257                         } else {
258                             //结束时如果栈为空,说明光标左边的字符不属于任何命令片段,无法进行补全
259                             return 1;
260                         }
261 
262                         let mut target_fragment = fragments.last().unwrap().clone();
263                         target_fragment = target_fragment.replace("\'", "").replace("\"", "");
264 
265                         let candidates = if fragments.len() < 2 {
266                             //补全命令
267                             complete_command(&target_fragment)
268                         } else {
269                             //补全参数
270                             complete_path(&target_fragment)
271                         };
272 
273                         match candidates.len() {
274                             1 => {
275                                 let old_fragment = fragments.last().unwrap();
276                                 let candidate = candidates.last().unwrap();
277                                 self.printer.cursor -= old_fragment.len();
278                                 self.printer.flush_cursor();
279                                 self.printer.delete(old_fragment.len());
280                                 self.printer.insert(candidate.as_bytes());
281                             }
282                             2.. => {
283                                 let old_cursor = self.printer.cursor;
284                                 self.printer.end();
285                                 println!();
286                                 for candidate in candidates {
287                                     print!(
288                                         "{}    ",
289                                         if candidate.ends_with('/') {
290                                             candidate.cyan()
291                                         } else {
292                                             candidate.white()
293                                         }
294                                     );
295                                 }
296                                 println!();
297                                 self.printer.print_prompt();
298                                 Printer::print(&self.printer.buf.deref().borrow());
299                                 self.printer.cursor = old_cursor;
300                                 self.printer.flush_cursor();
301                             }
302                             _ => {}
303                         }
304                     }
305 
306                     _ => {}
307                 }
308             } else {
309                 match key {
310                     1..=31 => {}
311                     c => {
312                         self.printer.insert(&[c]);
313                     }
314                 }
315             }
316             stdout.flush().unwrap();
317         }
318     }
319 }
320 
321 struct Printer {
322     prompt: Prompt,
323     buf: Rc<RefCell<Vec<u8>>>,
324     cursor: usize,
325 }
326 
327 impl Printer {
328     fn new(bytes: &Rc<RefCell<Vec<u8>>>) -> Self {
329         let len = bytes.deref().borrow().len();
330         Printer {
331             prompt: Prompt {
332                 computer_name: "DragonOS".to_string(),
333                 user_name: "root".to_string(),
334                 path: std::env::current_dir()
335                     .expect("Error getting current directory")
336                     .to_str()
337                     .unwrap()
338                     .to_string(),
339             },
340             buf: Rc::clone(bytes),
341             cursor: len,
342         }
343     }
344 
345     fn init_before_readline(&mut self) {
346         self.buf = Rc::new(RefCell::new(Vec::new()));
347         self.prompt.update_path();
348         self.print_prompt();
349         self.cursor = 0;
350         self.flush_cursor();
351     }
352 
353     fn print_prompt(&self) {
354         print!("{}", self.prompt);
355         stdout().flush().unwrap();
356     }
357 
358     //在光标处插入字符串
359     fn insert(&mut self, bytes: &[u8]) {
360         let mut buf = self.buf.deref().borrow_mut();
361         // self.delete_to_cursor(buf.len() - cursor);
362         // print!("{}"," ".repeat(buf.len() - cursor));
363         Printer::print(bytes);
364         Printer::print(&buf[self.cursor..]);
365         buf.splice(self.cursor..self.cursor, bytes.iter().cloned());
366         self.cursor += bytes.len();
367         self.flush_cursor();
368         stdout().flush().unwrap();
369     }
370 
371     //删除下标为[cursor,cursor + len)的字符,光标位置不变
372     fn delete(&self, len: usize) {
373         let cursor = self.cursor;
374         let mut buf = self.buf.deref().borrow_mut();
375         if cursor + len - 1 < buf.len() {
376             Printer::print(&buf[cursor + len..]);
377             print!("{}", " ".repeat(len));
378             self.flush_cursor();
379             buf.drain(cursor..cursor + len);
380             stdout().flush().unwrap();
381         }
382     }
383 
384     fn backspace(&mut self) {
385         if self.cursor > 0 {
386             crossterm::execute!(io::stdout(), crossterm::cursor::MoveLeft(1)).unwrap();
387             self.cursor -= 1;
388             self.flush_cursor();
389             self.delete(1);
390         }
391     }
392 
393     fn flush_cursor(&self) {
394         crossterm::execute!(
395             io::stdout(),
396             crossterm::cursor::MoveToColumn((self.cursor + self.prompt.len()) as u16)
397         )
398         .unwrap();
399     }
400 
401     fn cursor_left(&mut self) {
402         if self.cursor > 0 {
403             crossterm::execute!(io::stdout(), crossterm::cursor::MoveLeft(1)).unwrap();
404             self.cursor -= 1;
405         }
406     }
407 
408     fn cursor_right(&mut self) {
409         let buf = self.buf.deref().borrow();
410         if self.cursor < buf.len() {
411             crossterm::execute!(io::stdout(), crossterm::cursor::MoveRight(1)).unwrap();
412             self.cursor += 1;
413         }
414     }
415 
416     fn home(&mut self) {
417         self.cursor = 0;
418         self.flush_cursor();
419     }
420 
421     fn end(&mut self) {
422         self.cursor = self.buf.deref().borrow().len();
423         self.flush_cursor();
424     }
425 
426     fn change_line(&mut self, new_buf: &Rc<RefCell<Vec<u8>>>) {
427         let old_buf_borrow = self.buf.deref().borrow();
428         let new_buf_borrow = new_buf.deref().borrow();
429         self.cursor = 0;
430         self.flush_cursor();
431         Printer::print(&new_buf_borrow[..]);
432         self.cursor = new_buf_borrow.len();
433         if new_buf_borrow.len() < old_buf_borrow.len() {
434             print!(
435                 "{}",
436                 " ".repeat(old_buf_borrow.len() - new_buf_borrow.len())
437             );
438             self.flush_cursor();
439         }
440         drop(old_buf_borrow);
441         drop(new_buf_borrow);
442         self.buf = Rc::clone(new_buf);
443         stdout().flush().unwrap();
444     }
445 
446     fn print(bytes: &[u8]) {
447         print!("{}", String::from_utf8(bytes.to_vec()).unwrap());
448     }
449 
450     fn print_color(bytes: &[u8], front_color: usize, background_color: usize) {
451         std::io::stdout().flush().unwrap();
452         let cstr = std::ffi::CString::new(bytes).unwrap();
453         unsafe {
454             dsc::syscall!(SYS_PUT_STRING, cstr.as_ptr(), front_color, background_color);
455         }
456     }
457 }
458 
459 pub fn _print_color_example() {
460     let example = "abcdefghijklmnopqrstuvwxyz";
461     println!("{}", example.bright_black());
462     println!("{}", example.bright_blue());
463     println!("{}", example.bright_cyan());
464     println!("{}", example.bright_green());
465     println!("{}", example.bright_magenta());
466     println!("{}", example.bright_purple());
467     println!("{}", example.bright_red());
468     println!("{}", example.bright_white());
469     println!("{}", example.bright_yellow());
470     println!("{}", example.black());
471     println!("{}", example.blue());
472     println!("{}", example.cyan());
473     println!("{}", example.green());
474     println!("{}", example.magenta());
475     println!("{}", example.purple());
476     println!("{}", example.red());
477     println!("{}", example.white());
478     println!("{}", example.yellow());
479 }
480 
481 pub fn complete_command(command: &str) -> Vec<String> {
482     let mut candidates: Vec<String> = Vec::new();
483     for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
484         if cmd.starts_with(command) {
485             candidates.push(String::from(*cmd));
486         }
487     }
488     candidates
489 }
490 
491 pub fn complete_path(incomplete_path: &str) -> Vec<String> {
492     let current_dir = std::env::current_dir().unwrap();
493     let current_dir = current_dir.to_str().unwrap();
494     let path = if incomplete_path.starts_with('/') {
495         String::from(incomplete_path)
496     } else {
497         format!("{}/{}", current_dir, incomplete_path)
498     };
499 
500     let mut candidates: Vec<String> = Vec::new();
501     let dir: &str;
502     let incomplete_name: &str;
503     if let Some(index) = path.rfind('/') {
504         dir = &path[..=index];
505         if index < path.len() {
506             incomplete_name = &path[index + 1..];
507         } else {
508             incomplete_name = "";
509         }
510     } else {
511         dir = ".";
512         incomplete_name = &path[..];
513     }
514     match fs::read_dir(dir) {
515         Ok(read_dir) => {
516             if incomplete_name == "" {
517                 for entry in read_dir {
518                     let entry = entry.unwrap();
519                     let mut file_name = entry.file_name().into_string().unwrap();
520                     if entry.file_type().unwrap().is_dir() {
521                         file_name.push('/');
522                     }
523                     candidates.push(file_name);
524                 }
525             } else {
526                 for entry in read_dir {
527                     let entry = entry.unwrap();
528                     let mut file_name = entry.file_name().into_string().unwrap();
529                     if file_name.starts_with(incomplete_name) {
530                         if entry.file_type().unwrap().is_dir() {
531                             file_name.push('/');
532                         }
533                         candidates.push(file_name);
534                     }
535                 }
536             }
537         }
538 
539         Err(_) => {}
540     }
541     return candidates;
542 }
543