xref: /NovaShell/src/shell/mod.rs (revision cac674d3ed07e8f107c48c43f0688ec39fcc2635)
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 readline(&mut self) -> usize {
148         let mut stdout = std::io::stdout();
149         self.history_commands.push(Rc::clone(&self.printer.buf));
150         let mut command_index = self.history_commands.len() - 1;
151         loop {
152             let key = Self::read_char();
153             if let Ok(special_key) = SpecialKeycode::try_from(key) {
154                 match special_key {
155                     SpecialKeycode::FunctionKeyPrefix => {
156                         let key = Self::read_char();
157                         let function_key = FunctionKeySuffix::try_from(key).unwrap();
158                         match function_key {
159                             FunctionKeySuffix::Up => {
160                                 if command_index > 0 {
161                                     command_index -= 1;
162                                     self.printer.change_line(
163                                         self.history_commands.get(command_index).unwrap(),
164                                     );
165                                 }
166                             }
167 
168                             FunctionKeySuffix::Down => {
169                                 if command_index < self.history_commands.len() - 1 {
170                                     command_index += 1;
171                                     self.printer.change_line(
172                                         self.history_commands.get(command_index).unwrap(),
173                                     );
174                                 }
175                             }
176 
177                             FunctionKeySuffix::Left => {
178                                 if self.printer.cursor > 0 {
179                                     self.printer.cursor_left(1);
180                                 }
181                             }
182 
183                             FunctionKeySuffix::Right => {
184                                 if self.printer.cursor < self.printer.buf.borrow().len() {
185                                     self.printer.cursor_right(1);
186                                 }
187                             }
188 
189                             FunctionKeySuffix::Home => {
190                                 self.printer.home();
191                             }
192 
193                             FunctionKeySuffix::End => {
194                                 self.printer.end();
195                             }
196                         }
197                     }
198 
199                     SpecialKeycode::LF | SpecialKeycode::CR => {
200                         println!();
201                         self.history_commands.pop();
202                         return 1;
203                     }
204 
205                     SpecialKeycode::BackSpace => {
206                         self.printer.backspace();
207                     }
208 
209                     SpecialKeycode::Delete => {
210                         self.printer.delete(1);
211                     }
212 
213                     SpecialKeycode::Tab => {
214                         let mut buf = self.printer.buf.deref().borrow().clone();
215                         buf.truncate(self.printer.cursor);
216                         let str = String::from_utf8(buf.clone()).unwrap();
217                         if buf.len() == 0 || buf.iter().all(|byte| *byte == b' ') {
218                             continue;
219                         }
220 
221                         let iter = str.chars();
222                         let mut fragments: Vec<String> = Vec::new();
223                         let mut stack: String = String::with_capacity(str.len());
224                         let mut left_quote: char = ' ';
225                         for ch in iter {
226                             //存在未闭合的左引号,此时包括空格的任何字符都加入栈中,直到匹配到右引号
227                             if left_quote != ' ' {
228                                 if ch == left_quote {
229                                     left_quote = ' ';
230                                 }
231                                 stack.push(ch);
232                             } else {
233                                 //不存在未闭合的左引号
234                                 if ch == '\'' || ch == '\"' {
235                                     //字符为引号,记录下来
236                                     left_quote = ch;
237                                     stack.push(ch);
238                                 } else if ch == ' ' {
239                                     if !stack.is_empty() {
240                                         //字符为空格且栈中不为空,该空格视作命令段之间的分割线
241                                         //将栈中字符作为一个命令段加入集合,之后重置栈
242                                         fragments.push(stack.to_string());
243                                         stack.clear();
244                                     }
245                                 } else {
246                                     //其他字符都作为普通字符加入栈中
247                                     stack.push(ch);
248                                 }
249                             }
250                         }
251                         //结束时如果栈不为空
252                         if !stack.is_empty() {
253                             fragments.push(stack.to_string());
254                         } else {
255                             //结束时如果栈为空,说明光标左边的字符不属于任何命令片段,无法进行补全
256                             return 1;
257                         }
258 
259                         let mut target_fragment = fragments.last().unwrap().clone();
260                         target_fragment = target_fragment.replace("\'", "").replace("\"", "");
261 
262                         let (prefix, candidates) = if fragments.len() < 2 {
263                             //补全命令
264                             complete_command(&target_fragment)
265                         } else {
266                             //补全参数
267                             complete_path(&target_fragment)
268                         };
269 
270                         match candidates.len() {
271                             1 => {
272                                 let old_fragment = fragments.last().unwrap();
273                                 let candidate = candidates.last().unwrap();
274                                 self.printer.cursor_left(old_fragment.len());
275                                 self.printer.delete(old_fragment.len());
276                                 self.printer
277                                     .insert(format!("{}{}", prefix, candidate).as_bytes());
278                             }
279                             2.. => {
280                                 self.printer.end();
281                                 println!();
282                                 for candidate in candidates {
283                                     print!(
284                                         "{}\t",
285                                         if candidate.ends_with('/') {
286                                             candidate.truecolor(0x00, 0x88, 0xff)
287                                         } else {
288                                             candidate.white()
289                                         }
290                                     );
291                                 }
292                                 println!();
293                                 self.printer.print_prompt();
294                                 print!(
295                                     "{}",
296                                     String::from_utf8(self.printer.buf.borrow().to_vec()).unwrap()
297                                 );
298                             }
299                             _ => {}
300                         }
301                     }
302 
303                     _ => {}
304                 }
305             } else {
306                 match key {
307                     1..=31 => {}
308                     c => {
309                         self.printer.insert(&[c]);
310                         // String::from_utf8("abdsdf".as_bytes().to_vec()).unwrap();
311                     }
312                 }
313             }
314             stdout.flush().unwrap();
315         }
316     }
317 
318     fn add_backend_task(&mut self, child: Child) {
319         let mut job_id = 1;
320         while self.backend_task.contains_key(&job_id) {
321             job_id += 1;
322         }
323 
324         println!("[{}] {}", job_id, child.id());
325         self.backend_task.insert(job_id, child);
326     }
327 
328     fn detect_task_done(&mut self) {
329         self.backend_task.retain(|job_id, task| {
330             if let Ok(Some(status)) = task.try_wait() {
331                 println!("[{}] done with status: {}", job_id, status);
332                 false
333             } else {
334                 true
335             }
336         })
337     }
338 }
339 
340 #[allow(dead_code)]
341 struct WindowSize {
342     row: usize,
343     col: usize,
344 }
345 
346 impl WindowSize {
347     pub fn new() -> Option<Self> {
348         let mut ws: libc::winsize = unsafe { std::mem::zeroed() };
349         if unsafe {
350             libc::ioctl(
351                 libc::STDOUT_FILENO,
352                 libc::TIOCGWINSZ,
353                 &mut ws as *mut libc::winsize,
354             )
355         } == -1
356         {
357             None
358         } else {
359             Some(Self {
360                 row: ws.ws_row.into(),
361                 col: ws.ws_col.into(),
362             })
363         }
364     }
365 }
366 
367 pub fn complete_command(command: &str) -> (&str, Vec<String>) {
368     let mut candidates: Vec<String> = Vec::new();
369     for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
370         if cmd.starts_with(command) {
371             candidates.push(String::from(*cmd));
372         }
373     }
374     ("", candidates)
375 }
376 
377 pub fn complete_path(incomplete_path: &str) -> (&str, Vec<String>) {
378     let mut candidates: Vec<String> = Vec::new();
379     let mut dir = "";
380     let incomplete_name: &str;
381     if let Some(index) = incomplete_path.rfind('/') {
382         dir = &incomplete_path[..=index];
383         incomplete_name = &incomplete_path[index + 1..];
384     } else {
385         incomplete_name = incomplete_path;
386     }
387     if let Ok(read_dir) = fs::read_dir(if dir.is_empty() { "." } else { dir }) {
388         for entry in read_dir {
389             let entry = entry.unwrap();
390             let mut file_name = entry.file_name().into_string().unwrap();
391             if file_name.starts_with(incomplete_name) {
392                 if file_name.contains(' ') {
393                     file_name = format!("\'{}\'", file_name);
394                 }
395                 if entry.file_type().unwrap().is_dir() {
396                     file_name.push('/');
397                 }
398                 candidates.push(file_name);
399             }
400         }
401     }
402 
403     return (dir, candidates);
404 }
405