xref: /NovaShell/src/shell/mod.rs (revision 730e0306a1346ea7a86d0237bb024f4dab17e758)
1 use std::{
2     cell::RefCell,
3     collections::HashMap,
4     fs::{self, File, OpenOptions},
5     io::{BufRead, BufReader, Read, Write},
6     ops::Deref,
7     path::Path,
8     print,
9     process::Child,
10     rc::Rc,
11 };
12 
13 use crate::keycode::{FunctionKeySuffix, SpecialKeycode};
14 
15 use colored::Colorize;
16 use command::{BuildInCmd, Command, CommandError};
17 use printer::Printer;
18 
19 mod printer;
20 
21 pub mod command;
22 
23 const DEFAULT_HISTORY_COMMANDS_PATH: &str = "/history_commands.txt";
24 
25 #[allow(dead_code)]
26 pub struct Shell {
27     history_commands: Vec<Rc<RefCell<Vec<u8>>>>,
28     history_path: String,
29     printer: Printer,
30     backend_task: HashMap<usize, Child>,
31     window_size: Option<WindowSize>,
32 }
33 
34 impl Shell {
35     pub fn new() -> Shell {
36         let mut shell = Shell {
37             history_commands: Vec::new(),
38             history_path: DEFAULT_HISTORY_COMMANDS_PATH.to_string(),
39             printer: Printer::new(&Rc::new(RefCell::new(Vec::new()))),
40             backend_task: HashMap::new(),
41             window_size: WindowSize::new(),
42         };
43         shell.read_commands();
44         shell
45     }
46 
47     pub fn chdir(&mut self, new_dir: &String) {
48         let path = Path::new(&new_dir);
49         if let Err(e) = std::env::set_current_dir(&path) {
50             eprintln!("Error changing directory: {}", e);
51         }
52     }
53 
54     pub fn exec(&mut self) {
55         // 开启终端raw模式
56         // 开启终端raw模式
57         crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode");
58 
59         // 循环读取一行
60 
61         // 循环读取一行
62         loop {
63             self.printer.init_before_readline();
64             // 读取一行
65             // 读取一行
66             if self.readline() == 0 {
67                 println!();
68                 break;
69             }
70 
71             let command_bytes = self.printer.buf.borrow().clone();
72 
73             // 如果命令不以空格开头且不跟上一条命令相同,这条命令会被记录
74 
75             // 如果命令不以空格开头且不跟上一条命令相同,这条命令会被记录
76             if !command_bytes.is_empty()
77                 && !command_bytes.starts_with(&[b' '])
78                 && command_bytes
79                     != self
80                         .history_commands
81                         .last()
82                         .unwrap_or(&Rc::new(RefCell::new(Vec::new())))
83                         .borrow()
84                         .clone()
85             {
86                 self.history_commands
87                     .push(Rc::new(RefCell::new(command_bytes.clone())));
88                 self.write_commands(&command_bytes);
89             };
90 
91             // 命令不为空,执行命令
92 
93             // 命令不为空,执行命令
94             if !command_bytes.iter().all(|&byte| byte == b' ') {
95                 self.exec_commands_in_line(&command_bytes);
96             }
97             self.detect_task_done();
98         }
99     }
100 
101     fn exec_commands_in_line(&mut self, command_bytes: &Vec<u8>) {
102         match Command::parse(String::from_utf8(command_bytes.clone()).unwrap()) {
103             Ok(commands) => commands
104                 .into_iter()
105                 .for_each(|command| self.exec_command(command)),
106             Err(e) => CommandError::handle(e),
107         }
108     }
109 
110     pub fn read_commands(&mut self) {
111         let mut history = Vec::new();
112         for line in BufReader::new(match File::open(&self.history_path) {
113             Ok(file) => file,
114             Err(_) => File::create(&self.history_path).unwrap(),
115         })
116         .lines()
117         {
118             match line {
119                 Ok(s) => history.push(Rc::new(RefCell::new(s.into_bytes()))),
120                 Err(_) => {
121                     break;
122                 }
123             }
124         }
125         self.history_commands = history;
126     }
127 
128     fn write_commands(&self, command_bytes: &Vec<u8>) {
129         let mut file = OpenOptions::new()
130             .append(true)
131             .open(self.history_path.as_str())
132             .unwrap();
133         file.write_all(&command_bytes)
134             .expect("failed to write history command");
135         file.write_all(&[SpecialKeycode::LF.into()]).unwrap();
136     }
137 
138     fn read_char() -> u8 {
139         let mut buf: [u8; 1] = [0];
140         loop {
141             if std::io::stdin().read(&mut buf).is_ok() {
142                 return buf[0];
143             }
144         }
145     }
146 
147     fn handle_funckey(&mut self, command_index: &mut usize) {
148         let key = Self::read_char();
149         if key != FunctionKeySuffix::SUFFIX_0 {
150             return;
151         }
152         let key1 = Self::read_char();
153         let suffix = &[key, key1];
154         // println!("suffix: {:?}", suffix);
155         let function_key = FunctionKeySuffix::try_from(suffix);
156         if function_key.is_none() {
157             return;
158         }
159         let function_key = function_key.unwrap();
160 
161         match function_key {
162             FunctionKeySuffix::Up => {
163                 if *command_index > 0 {
164                     *command_index -= 1;
165                     self.printer
166                         .change_line(self.history_commands.get(*command_index).unwrap());
167                 }
168             }
169 
170             FunctionKeySuffix::Down => {
171                 if *command_index < self.history_commands.len() - 1 {
172                     *command_index += 1;
173                     self.printer
174                         .change_line(self.history_commands.get(*command_index).unwrap());
175                 }
176             }
177 
178             FunctionKeySuffix::Left => {
179                 if self.printer.cursor > 0 {
180                     self.printer.cursor_left(1);
181                 }
182             }
183 
184             FunctionKeySuffix::Right => {
185                 if self.printer.cursor < self.printer.buf.borrow().len() {
186                     self.printer.cursor_right(1);
187                 }
188             }
189 
190             FunctionKeySuffix::Home => {
191                 self.printer.home();
192             }
193 
194             FunctionKeySuffix::End => {
195                 self.printer.end();
196             }
197         }
198     }
199 
200     fn readline(&mut self) -> usize {
201         let mut stdout = std::io::stdout();
202         self.history_commands.push(Rc::clone(&self.printer.buf));
203         let mut command_index = self.history_commands.len() - 1;
204         loop {
205             let key = Self::read_char();
206             if let Ok(special_key) = SpecialKeycode::try_from(key) {
207                 match special_key {
208                     SpecialKeycode::ESC => {
209                         self.handle_funckey(&mut command_index);
210                     }
211 
212                     SpecialKeycode::LF | SpecialKeycode::CR => {
213                         println!();
214                         self.history_commands.pop();
215                         return 1;
216                     }
217 
218                     SpecialKeycode::BackSpace | SpecialKeycode::Delete => {
219                         self.printer.backspace();
220                     }
221 
222                     SpecialKeycode::Tab => {
223                         let mut buf = self.printer.buf.deref().borrow().clone();
224                         buf.truncate(self.printer.cursor);
225                         let str = String::from_utf8(buf.clone()).unwrap();
226                         if buf.len() == 0 || buf.iter().all(|byte| *byte == b' ') {
227                             continue;
228                         }
229 
230                         let iter = str.chars();
231                         let mut fragments: Vec<String> = Vec::new();
232                         let mut stack: String = String::with_capacity(str.len());
233                         let mut left_quote: char = ' ';
234                         for ch in iter {
235                             //存在未闭合的左引号,此时包括空格的任何字符都加入栈中,直到匹配到右引号
236                             if left_quote != ' ' {
237                                 if ch == left_quote {
238                                     left_quote = ' ';
239                                 }
240                                 stack.push(ch);
241                             } else {
242                                 //不存在未闭合的左引号
243                                 if ch == '\'' || ch == '\"' {
244                                     //字符为引号,记录下来
245                                     left_quote = ch;
246                                     stack.push(ch);
247                                 } else if ch == ' ' {
248                                     if !stack.is_empty() {
249                                         //字符为空格且栈中不为空,该空格视作命令段之间的分割线
250                                         //将栈中字符作为一个命令段加入集合,之后重置栈
251                                         fragments.push(stack.to_string());
252                                         stack.clear();
253                                     }
254                                 } else {
255                                     //其他字符都作为普通字符加入栈中
256                                     stack.push(ch);
257                                 }
258                             }
259                         }
260                         //结束时如果栈不为空
261                         if !stack.is_empty() {
262                             fragments.push(stack.to_string());
263                         } else {
264                             //结束时如果栈为空,说明光标左边的字符不属于任何命令片段,无法进行补全
265                             return 1;
266                         }
267 
268                         let mut target_fragment = fragments.last().unwrap().clone();
269                         target_fragment = target_fragment.replace("\'", "").replace("\"", "");
270 
271                         let (prefix, candidates) = if fragments.len() < 2 {
272                             //补全命令
273                             complete_command(&target_fragment)
274                         } else {
275                             //补全参数
276                             complete_path(&target_fragment)
277                         };
278 
279                         match candidates.len() {
280                             1 => {
281                                 let old_fragment = fragments.last().unwrap();
282                                 let candidate = candidates.last().unwrap();
283                                 self.printer.cursor_left(old_fragment.len());
284                                 self.printer.delete(old_fragment.len());
285                                 self.printer
286                                     .insert(format!("{}{}", prefix, candidate).as_bytes());
287                             }
288                             2.. => {
289                                 self.printer.end();
290                                 println!();
291                                 for candidate in candidates {
292                                     print!(
293                                         "{}\t",
294                                         if candidate.ends_with('/') {
295                                             candidate.truecolor(0x00, 0x88, 0xff)
296                                         } else {
297                                             candidate.white()
298                                         }
299                                     );
300                                 }
301                                 println!();
302                                 self.printer.print_prompt();
303                                 print!(
304                                     "{}",
305                                     String::from_utf8(self.printer.buf.borrow().to_vec()).unwrap()
306                                 );
307                             }
308                             _ => {}
309                         }
310                     }
311 
312                     _ => {}
313                 }
314             } else {
315                 match key {
316                     1..=31 => {}
317                     c => {
318                         self.printer.insert(&[c]);
319                         // String::from_utf8("abdsdf".as_bytes().to_vec()).unwrap();
320                     }
321                 }
322             }
323             stdout.flush().unwrap();
324         }
325     }
326 
327     fn add_backend_task(&mut self, child: Child) {
328         let mut job_id = 1;
329         while self.backend_task.contains_key(&job_id) {
330             job_id += 1;
331         }
332 
333         println!("[{}] {}", job_id, child.id());
334         self.backend_task.insert(job_id, child);
335     }
336 
337     fn detect_task_done(&mut self) {
338         self.backend_task.retain(|job_id, task| {
339             if let Ok(Some(status)) = task.try_wait() {
340                 println!("[{}] done with status: {}", job_id, status);
341                 false
342             } else {
343                 true
344             }
345         })
346     }
347 }
348 
349 #[allow(dead_code)]
350 struct WindowSize {
351     row: usize,
352     col: usize,
353 }
354 
355 impl WindowSize {
356     pub fn new() -> Option<Self> {
357         let mut ws: libc::winsize = unsafe { std::mem::zeroed() };
358         if unsafe {
359             libc::ioctl(
360                 libc::STDOUT_FILENO,
361                 libc::TIOCGWINSZ,
362                 &mut ws as *mut libc::winsize,
363             )
364         } == -1
365         {
366             None
367         } else {
368             Some(Self {
369                 row: ws.ws_row.into(),
370                 col: ws.ws_col.into(),
371             })
372         }
373     }
374 }
375 
376 pub fn complete_command(command: &str) -> (&str, Vec<String>) {
377     let mut candidates: Vec<String> = Vec::new();
378     for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
379         if cmd.starts_with(command) {
380             candidates.push(String::from(*cmd));
381         }
382     }
383     ("", candidates)
384 }
385 
386 pub fn complete_path(incomplete_path: &str) -> (&str, Vec<String>) {
387     let mut candidates: Vec<String> = Vec::new();
388     let mut dir = "";
389     let incomplete_name: &str;
390     if let Some(index) = incomplete_path.rfind('/') {
391         dir = &incomplete_path[..=index];
392         incomplete_name = &incomplete_path[index + 1..];
393     } else {
394         incomplete_name = incomplete_path;
395     }
396     if let Ok(read_dir) = fs::read_dir(if dir.is_empty() { "." } else { dir }) {
397         for entry in read_dir {
398             let entry = entry.unwrap();
399             let mut file_name = entry.file_name().into_string().unwrap();
400             if file_name.starts_with(incomplete_name) {
401                 if file_name.contains(' ') {
402                     file_name = format!("\'{}\'", file_name);
403                 }
404                 if entry.file_type().unwrap().is_dir() {
405                     file_name.push('/');
406                 }
407                 candidates.push(file_name);
408             }
409         }
410     }
411 
412     return (dir, candidates);
413 }
414