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