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