|
@@ -1,28 +1,66 @@
|
|
|
+use core::fmt;
|
|
|
use std::{
|
|
|
+ cell::RefCell,
|
|
|
fs::{self, File, OpenOptions},
|
|
|
- io::{self, BufRead, BufReader, Write},
|
|
|
+ io::{self, stdout, BufRead, BufReader, Read, Write},
|
|
|
+ ops::Deref,
|
|
|
path::Path,
|
|
|
print,
|
|
|
+ rc::Rc,
|
|
|
string::String,
|
|
|
vec::Vec,
|
|
|
};
|
|
|
|
|
|
use crate::keycode::{FunctionKeySuffix, SpecialKeycode};
|
|
|
|
|
|
+use colored::Colorize;
|
|
|
use command::{BuildInCmd, Command};
|
|
|
|
|
|
pub mod command;
|
|
|
|
|
|
+pub struct Prompt {
|
|
|
+ user_name: String,
|
|
|
+ computer_name: String,
|
|
|
+ path: String,
|
|
|
+}
|
|
|
+
|
|
|
+impl Prompt {
|
|
|
+ pub fn len(&self) -> usize {
|
|
|
+ format!("{}@{}:{}$ ", self.user_name, self.computer_name, self.path).len()
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn update_path(&mut self) {
|
|
|
+ self.path = std::env::current_dir()
|
|
|
+ .unwrap()
|
|
|
+ .to_str()
|
|
|
+ .unwrap()
|
|
|
+ .to_string();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl fmt::Display for Prompt {
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
+ write!(
|
|
|
+ f,
|
|
|
+ "{}:{}$ ",
|
|
|
+ format!("{}@{}", self.user_name, self.computer_name).bright_green(),
|
|
|
+ self.path.bright_cyan()
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
pub struct Shell {
|
|
|
- history_commands: Vec<Vec<u8>>,
|
|
|
- executed_commands: Vec<Vec<u8>>,
|
|
|
+ history_commands: Vec<Rc<RefCell<Vec<u8>>>>,
|
|
|
+ history_path: String,
|
|
|
+ printer: Printer,
|
|
|
}
|
|
|
|
|
|
impl Shell {
|
|
|
pub fn new() -> Shell {
|
|
|
let mut shell = Shell {
|
|
|
history_commands: Vec::new(),
|
|
|
- executed_commands: Vec::new(),
|
|
|
+ history_path: "history_commands.txt".to_string(),
|
|
|
+ printer: Printer::new(&Rc::new(RefCell::new(Vec::new()))),
|
|
|
};
|
|
|
shell.read_commands();
|
|
|
shell
|
|
@@ -44,21 +82,26 @@ impl Shell {
|
|
|
}
|
|
|
|
|
|
pub fn exec(&mut self) {
|
|
|
- let mut buf: Vec<u8>;
|
|
|
+ crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode");
|
|
|
loop {
|
|
|
- buf = Vec::new();
|
|
|
- self.history_commands.push(buf);
|
|
|
- Printer::print_prompt(&Self::current_dir());
|
|
|
+ self.printer.init_before_readline();
|
|
|
if self.readline(0) == 0 {
|
|
|
println!();
|
|
|
break;
|
|
|
}
|
|
|
- let command_bytes = self.history_commands.last().unwrap().clone();
|
|
|
- if command_bytes.starts_with(&[b' ']) {
|
|
|
- self.history_commands.pop().unwrap();
|
|
|
- } else {
|
|
|
- self.executed_commands.push(command_bytes.clone());
|
|
|
- }
|
|
|
+ let command_bytes = self.printer.buf.borrow().clone();
|
|
|
+ if !command_bytes.starts_with(&[b' '])
|
|
|
+ && command_bytes
|
|
|
+ != self
|
|
|
+ .history_commands
|
|
|
+ .last()
|
|
|
+ .unwrap_or(&Rc::new(RefCell::new(Vec::new())))
|
|
|
+ .borrow()
|
|
|
+ .clone()
|
|
|
+ {
|
|
|
+ self.history_commands
|
|
|
+ .push(Rc::new(RefCell::new(command_bytes.clone())));
|
|
|
+ };
|
|
|
if !command_bytes.iter().all(|&byte| byte == b' ') {
|
|
|
self.exec_commands_in_line(&command_bytes);
|
|
|
}
|
|
@@ -73,206 +116,200 @@ impl Shell {
|
|
|
.for_each(|command| self.exec_command(command));
|
|
|
}
|
|
|
|
|
|
- fn read_commands(&mut self) {
|
|
|
- for line in BufReader::new(match File::open("history_commands.txt") {
|
|
|
+ pub fn read_commands(&mut self) {
|
|
|
+ let mut history = Vec::new();
|
|
|
+ for line in BufReader::new(match File::open(&self.history_path) {
|
|
|
Ok(file) => file,
|
|
|
- Err(_) => File::create("history_commands.txt").unwrap(),
|
|
|
+ Err(_) => File::create(&self.history_path).unwrap(),
|
|
|
})
|
|
|
.lines()
|
|
|
{
|
|
|
match line {
|
|
|
- Ok(s) => self.history_commands.push(s.into_bytes()),
|
|
|
+ Ok(s) => history.push(Rc::new(RefCell::new(s.into_bytes()))),
|
|
|
Err(_) => {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ self.history_commands = history;
|
|
|
}
|
|
|
|
|
|
fn write_commands(&self) {
|
|
|
let mut file = OpenOptions::new()
|
|
|
- .append(true)
|
|
|
+ .append(false)
|
|
|
.open("history_commands.txt")
|
|
|
.unwrap();
|
|
|
- for command_line in &self.executed_commands {
|
|
|
- file.write_all(&command_line[..]).unwrap();
|
|
|
+ for command_line in &self.history_commands {
|
|
|
+ file.write_all(&command_line.borrow()[..]).unwrap();
|
|
|
file.write_all(&[SpecialKeycode::LF.into()]).unwrap();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn read_char(byte: &mut u8) {
|
|
|
- let mut c: libc::c_uchar = 0;
|
|
|
- unsafe {
|
|
|
- let p = &mut c as *mut libc::c_uchar as *mut libc::c_void;
|
|
|
- libc::read(0, p, 1);
|
|
|
- }
|
|
|
- *byte = c;
|
|
|
+ fn read_char() -> u8 {
|
|
|
+ let mut buf: [u8; 1] = [0];
|
|
|
+ std::io::stdin().read(&mut buf).expect("read char error");
|
|
|
+ buf[0]
|
|
|
}
|
|
|
|
|
|
fn readline(&mut self, _fd: usize) -> usize {
|
|
|
let mut stdout = std::io::stdout();
|
|
|
- let prompt: String = Self::current_dir().clone();
|
|
|
- let len = self.history_commands.len() - 1;
|
|
|
- let mut key: [u8; 1] = [0];
|
|
|
- let mut command_index = len;
|
|
|
- let mut buf = self.history_commands.get_mut(command_index).unwrap();
|
|
|
- let mut cursor = 0;
|
|
|
-
|
|
|
- stdout.flush().unwrap();
|
|
|
+ self.history_commands.push(Rc::clone(&self.printer.buf));
|
|
|
+ let mut command_index = self.history_commands.len() - 1;
|
|
|
loop {
|
|
|
- Self::read_char(&mut key[0]);
|
|
|
- // if stdin.read(&mut key).ok() != Some(1) {
|
|
|
- // continue;
|
|
|
- // }
|
|
|
- if let Ok(special_key) = SpecialKeycode::try_from(key[0]) {
|
|
|
+ let key = Self::read_char();
|
|
|
+ if let Ok(special_key) = SpecialKeycode::try_from(key) {
|
|
|
match special_key {
|
|
|
SpecialKeycode::FunctionKeyPrefix => {
|
|
|
- Self::read_char(&mut key[0]);
|
|
|
- let function_key = FunctionKeySuffix::try_from(key[0]).unwrap();
|
|
|
+ let key = Self::read_char();
|
|
|
+ let function_key = FunctionKeySuffix::try_from(key).unwrap();
|
|
|
match function_key {
|
|
|
FunctionKeySuffix::Up => {
|
|
|
if command_index > 0 {
|
|
|
command_index -= 1;
|
|
|
+ self.printer.change_line(
|
|
|
+ self.history_commands.get(command_index).unwrap(),
|
|
|
+ );
|
|
|
}
|
|
|
- let old_length = buf.len();
|
|
|
- buf = self.history_commands.get_mut(command_index).unwrap();
|
|
|
- Printer::replace(&buf, old_length);
|
|
|
- cursor = buf.len() - 1;
|
|
|
}
|
|
|
|
|
|
FunctionKeySuffix::Down => {
|
|
|
- if command_index < len {
|
|
|
+ if command_index < self.history_commands.len() - 1 {
|
|
|
command_index += 1;
|
|
|
+ self.printer.change_line(
|
|
|
+ self.history_commands.get(command_index).unwrap(),
|
|
|
+ );
|
|
|
}
|
|
|
- let old_length = buf.len();
|
|
|
- buf = self.history_commands.get_mut(command_index).unwrap();
|
|
|
- Printer::replace(&buf, old_length);
|
|
|
- cursor = buf.len() - 1;
|
|
|
}
|
|
|
|
|
|
FunctionKeySuffix::Left => {
|
|
|
- if cursor > 0 {
|
|
|
- Printer::set_cursor(buf, cursor, cursor - 1);
|
|
|
- cursor -= 1;
|
|
|
- }
|
|
|
+ self.printer.cursor_left();
|
|
|
}
|
|
|
|
|
|
FunctionKeySuffix::Right => {
|
|
|
- if cursor < buf.len() - 1 {
|
|
|
- Printer::set_cursor(buf, cursor, cursor + 1);
|
|
|
- cursor += 1;
|
|
|
- }
|
|
|
+ self.printer.cursor_right();
|
|
|
}
|
|
|
|
|
|
FunctionKeySuffix::Home => {
|
|
|
- Printer::set_cursor(buf, cursor, 0);
|
|
|
+ self.printer.home();
|
|
|
}
|
|
|
|
|
|
FunctionKeySuffix::End => {
|
|
|
- Printer::set_cursor(buf, cursor, buf.len());
|
|
|
+ self.printer.end();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
SpecialKeycode::LF | SpecialKeycode::CR => {
|
|
|
- // println!("buf:{:?}\tcursor:{}\tbuf.len():{}", buf, cursor, buf.len());
|
|
|
- Printer::set_cursor(buf, cursor, buf.len());
|
|
|
- let mut command = buf.clone();
|
|
|
- buf = self.history_commands.get_mut(len).unwrap();
|
|
|
- buf.clear();
|
|
|
- buf.append(&mut command);
|
|
|
-
|
|
|
+ println!();
|
|
|
+ self.history_commands.pop();
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
SpecialKeycode::BackSpace => {
|
|
|
- if cursor > 0 {
|
|
|
- Printer::delete_to_cursor(cursor, 1, buf);
|
|
|
- buf.remove(cursor - 1);
|
|
|
- cursor -= 1;
|
|
|
- }
|
|
|
+ self.printer.backspace();
|
|
|
}
|
|
|
|
|
|
SpecialKeycode::Delete => {
|
|
|
- if cursor < buf.len() - 1 {
|
|
|
- Printer::delete(cursor, buf);
|
|
|
- buf.remove(cursor);
|
|
|
- }
|
|
|
+ self.printer.delete(1);
|
|
|
}
|
|
|
|
|
|
SpecialKeycode::Tab => {
|
|
|
- if buf.len() > 1 && buf[cursor - 1] != b' ' {
|
|
|
- let command: String =
|
|
|
- String::from_utf8(buf[..cursor].to_vec()).unwrap();
|
|
|
- let mut command_frag =
|
|
|
- command.split_ascii_whitespace().collect::<Vec<_>>();
|
|
|
- let incomplete_frag = command_frag.pop().unwrap();
|
|
|
- let mut incomplete_len: usize = incomplete_frag.len();
|
|
|
- let candidates = match command_frag.len() {
|
|
|
- 0 => Printer::complete_command(incomplete_frag),
|
|
|
- 1.. => {
|
|
|
- if let Some(index) = incomplete_frag.rfind('/') {
|
|
|
- incomplete_len = incomplete_frag.len() - index - 1;
|
|
|
- } else {
|
|
|
- incomplete_len = incomplete_frag.len();
|
|
|
+ let mut buf = self.printer.buf.deref().borrow().clone();
|
|
|
+ buf.truncate(self.printer.cursor);
|
|
|
+ let str = String::from_utf8(buf.clone()).unwrap();
|
|
|
+ if buf.len() == 0 || buf.iter().all(|byte| *byte == b' ') {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ let iter = str.chars();
|
|
|
+ let mut fragments: Vec<String> = Vec::new();
|
|
|
+ let mut stack: String = String::with_capacity(str.len());
|
|
|
+ let mut left_quote: char = ' ';
|
|
|
+ for ch in iter {
|
|
|
+ //存在未闭合的左引号,此时除能够配对的引号外,任何字符都加入栈中
|
|
|
+ if left_quote != ' ' {
|
|
|
+ if ch == left_quote {
|
|
|
+ left_quote = ' ';
|
|
|
+ }
|
|
|
+ stack.push(ch);
|
|
|
+ } else {
|
|
|
+ //不存在未闭合的左引号
|
|
|
+ if ch == '\'' || ch == '\"' {
|
|
|
+ //字符为引号,记录下来
|
|
|
+ left_quote = ch;
|
|
|
+ stack.push(ch);
|
|
|
+ } else if ch == ' ' {
|
|
|
+ if !stack.is_empty() {
|
|
|
+ //字符为空格且栈中不为空,该空格视作命令段之间的分割线
|
|
|
+ //将栈中字符作为一个命令段加入集合,之后重置栈
|
|
|
+ fragments.push(stack.to_string());
|
|
|
+ stack.clear();
|
|
|
}
|
|
|
- Printer::complete_path(
|
|
|
- Self::current_dir().as_str(),
|
|
|
- incomplete_frag,
|
|
|
- )
|
|
|
+ } else {
|
|
|
+ //其他字符都作为普通字符加入栈中
|
|
|
+ stack.push(ch);
|
|
|
}
|
|
|
- _ => Vec::new(),
|
|
|
- };
|
|
|
- match candidates.len() {
|
|
|
- 1 => {
|
|
|
- let complete_part = candidates[0][incomplete_len..].as_bytes();
|
|
|
-
|
|
|
- Printer::delete_from_index(cursor, buf.len());
|
|
|
-
|
|
|
- // stdout.write_all(complete_part).unwrap();
|
|
|
- Printer::print(complete_part);
|
|
|
-
|
|
|
- Printer::print_cursor(buf[cursor]);
|
|
|
- Printer::print(&buf[cursor + 1..]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //结束时如果栈不为空
|
|
|
+ if !stack.is_empty() {
|
|
|
+ fragments.push(stack.to_string());
|
|
|
+ } else {
|
|
|
+ //结束时如果栈为空,说明光标左边的字符不属于任何命令片段,无法进行补全
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
|
|
|
- buf.splice(cursor..cursor, complete_part.iter().cloned());
|
|
|
- cursor += candidates[0].len() - incomplete_len;
|
|
|
- }
|
|
|
- 2.. => {
|
|
|
- Printer::delete_from_index(cursor, buf.len());
|
|
|
- Printer::print(&buf[cursor..buf.len()]);
|
|
|
- println!();
|
|
|
- for candidate in candidates {
|
|
|
+ let mut target_fragment = fragments.last().unwrap().clone();
|
|
|
+ target_fragment = target_fragment.replace("\'", "").replace("\"", "");
|
|
|
+
|
|
|
+ let candidates = if fragments.len() < 2 {
|
|
|
+ //补全命令
|
|
|
+ complete_command(&target_fragment)
|
|
|
+ } else {
|
|
|
+ //补全参数
|
|
|
+ complete_path(&target_fragment)
|
|
|
+ };
|
|
|
+
|
|
|
+ match candidates.len() {
|
|
|
+ 1 => {
|
|
|
+ let old_fragment = fragments.last().unwrap();
|
|
|
+ let candidate = candidates.last().unwrap();
|
|
|
+ self.printer.cursor -= old_fragment.len();
|
|
|
+ self.printer.flush_cursor();
|
|
|
+ self.printer.delete(old_fragment.len());
|
|
|
+ self.printer.insert(candidate.as_bytes());
|
|
|
+ }
|
|
|
+ 2.. => {
|
|
|
+ let old_cursor = self.printer.cursor;
|
|
|
+ self.printer.end();
|
|
|
+ println!();
|
|
|
+ for candidate in candidates {
|
|
|
+ print!(
|
|
|
+ "{} ",
|
|
|
if candidate.ends_with('/') {
|
|
|
- crate::shell::Printer::print_color(
|
|
|
- candidate.as_bytes(),
|
|
|
- 0x000088ff,
|
|
|
- 0x00000000,
|
|
|
- );
|
|
|
- print!(" ");
|
|
|
+ candidate.cyan()
|
|
|
} else {
|
|
|
- print!("{candidate} ");
|
|
|
+ candidate.white()
|
|
|
}
|
|
|
- }
|
|
|
- println!();
|
|
|
- Printer::print_prompt(&prompt);
|
|
|
- Printer::print(&buf[..buf.len() - 1]);
|
|
|
- Printer::print_cursor(b' ');
|
|
|
- Printer::set_cursor(buf, buf.len(), cursor);
|
|
|
+ );
|
|
|
}
|
|
|
- _ => {}
|
|
|
+ println!();
|
|
|
+ self.printer.print_prompt();
|
|
|
+ Printer::print(&self.printer.buf.deref().borrow());
|
|
|
+ self.printer.cursor = old_cursor;
|
|
|
+ self.printer.flush_cursor();
|
|
|
}
|
|
|
+ _ => {}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
}
|
|
|
} else {
|
|
|
- match key[0] {
|
|
|
+ match key {
|
|
|
1..=31 => {}
|
|
|
c => {
|
|
|
- buf.insert(cursor, c);
|
|
|
- cursor += 1;
|
|
|
+ self.printer.insert(&[c]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -281,50 +318,129 @@ impl Shell {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-struct Printer;
|
|
|
+struct Printer {
|
|
|
+ prompt: Prompt,
|
|
|
+ buf: Rc<RefCell<Vec<u8>>>,
|
|
|
+ cursor: usize,
|
|
|
+}
|
|
|
|
|
|
impl Printer {
|
|
|
- fn print_prompt(current_dir: &String) {
|
|
|
- io::stdout().flush().unwrap();
|
|
|
- Self::print_color("[DragonOS]:".as_bytes(), 0x0000ff90, 0x00000000);
|
|
|
- Self::print_color(current_dir.as_bytes(), 0x000088ff, 0x00000000);
|
|
|
- print!("$ ");
|
|
|
+ fn new(bytes: &Rc<RefCell<Vec<u8>>>) -> Self {
|
|
|
+ let len = bytes.deref().borrow().len();
|
|
|
+ Printer {
|
|
|
+ prompt: Prompt {
|
|
|
+ computer_name: "DragonOS".to_string(),
|
|
|
+ user_name: "root".to_string(),
|
|
|
+ path: std::env::current_dir()
|
|
|
+ .expect("Error getting current directory")
|
|
|
+ .to_str()
|
|
|
+ .unwrap()
|
|
|
+ .to_string(),
|
|
|
+ },
|
|
|
+ buf: Rc::clone(bytes),
|
|
|
+ cursor: len,
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- fn print_cursor(c: u8) {
|
|
|
- Self::print_color(&[c], 0x00000000, 0x00ffffff);
|
|
|
+ fn init_before_readline(&mut self) {
|
|
|
+ self.buf = Rc::new(RefCell::new(Vec::new()));
|
|
|
+ self.prompt.update_path();
|
|
|
+ self.print_prompt();
|
|
|
+ self.cursor = 0;
|
|
|
+ self.flush_cursor();
|
|
|
}
|
|
|
|
|
|
- fn delete_from_index(index: usize, length: usize) {
|
|
|
- for _i in 0..length - index {
|
|
|
- Printer::print(&[
|
|
|
- SpecialKeycode::BackSpace.into(),
|
|
|
- b' ',
|
|
|
- SpecialKeycode::BackSpace.into(),
|
|
|
- ]);
|
|
|
+ fn print_prompt(&self) {
|
|
|
+ print!("{}", self.prompt);
|
|
|
+ stdout().flush().unwrap();
|
|
|
+ }
|
|
|
+
|
|
|
+ //在光标处插入字符串
|
|
|
+ fn insert(&mut self, bytes: &[u8]) {
|
|
|
+ let mut buf = self.buf.deref().borrow_mut();
|
|
|
+ // self.delete_to_cursor(buf.len() - cursor);
|
|
|
+ // print!("{}"," ".repeat(buf.len() - cursor));
|
|
|
+ Printer::print(bytes);
|
|
|
+ Printer::print(&buf[self.cursor..]);
|
|
|
+ buf.splice(self.cursor..self.cursor, bytes.iter().cloned());
|
|
|
+ self.cursor += bytes.len();
|
|
|
+ self.flush_cursor();
|
|
|
+ stdout().flush().unwrap();
|
|
|
+ }
|
|
|
+
|
|
|
+ //删除下标为[cursor,cursor + len)的字符,光标位置不变
|
|
|
+ fn delete(&self, len: usize) {
|
|
|
+ let cursor = self.cursor;
|
|
|
+ let mut buf = self.buf.deref().borrow_mut();
|
|
|
+ if cursor + len - 1 < buf.len() {
|
|
|
+ Printer::print(&buf[cursor + len..]);
|
|
|
+ print!("{}", " ".repeat(len));
|
|
|
+ self.flush_cursor();
|
|
|
+ buf.drain(cursor..cursor + len);
|
|
|
+ stdout().flush().unwrap();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn delete(cursor: usize, buf: &Vec<u8>) {
|
|
|
- if cursor < buf.len() - 1 {
|
|
|
- Printer::delete_from_index(cursor, buf.len());
|
|
|
- Printer::print_cursor(buf[cursor + 1]);
|
|
|
- Printer::print(&buf[cursor + 2..]);
|
|
|
+ fn backspace(&mut self) {
|
|
|
+ if self.cursor > 0 {
|
|
|
+ crossterm::execute!(io::stdout(), crossterm::cursor::MoveLeft(1)).unwrap();
|
|
|
+ self.cursor -= 1;
|
|
|
+ self.flush_cursor();
|
|
|
+ self.delete(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn flush_cursor(&self) {
|
|
|
+ crossterm::execute!(
|
|
|
+ io::stdout(),
|
|
|
+ crossterm::cursor::MoveToColumn((self.cursor + self.prompt.len()) as u16)
|
|
|
+ )
|
|
|
+ .unwrap();
|
|
|
+ }
|
|
|
+
|
|
|
+ fn cursor_left(&mut self) {
|
|
|
+ if self.cursor > 0 {
|
|
|
+ crossterm::execute!(io::stdout(), crossterm::cursor::MoveLeft(1)).unwrap();
|
|
|
+ self.cursor -= 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn delete_to_cursor(cursor: usize, length: usize, buf: &Vec<u8>) {
|
|
|
- if cursor > 0 {
|
|
|
- Printer::delete_from_index(cursor - length, buf.len());
|
|
|
- Printer::print_cursor(buf[cursor]);
|
|
|
- Printer::print(&buf[cursor + 1..]);
|
|
|
+ fn cursor_right(&mut self) {
|
|
|
+ let buf = self.buf.deref().borrow();
|
|
|
+ if self.cursor < buf.len() {
|
|
|
+ crossterm::execute!(io::stdout(), crossterm::cursor::MoveRight(1)).unwrap();
|
|
|
+ self.cursor += 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn replace(bytes: &[u8], old_length: usize) {
|
|
|
- Printer::delete_from_index(0, old_length);
|
|
|
- Printer::print(&bytes[0..bytes.len() - 1]);
|
|
|
- Printer::print_cursor(b' ');
|
|
|
+ fn home(&mut self) {
|
|
|
+ self.cursor = 0;
|
|
|
+ self.flush_cursor();
|
|
|
+ }
|
|
|
+
|
|
|
+ fn end(&mut self) {
|
|
|
+ self.cursor = self.buf.deref().borrow().len();
|
|
|
+ self.flush_cursor();
|
|
|
+ }
|
|
|
+
|
|
|
+ fn change_line(&mut self, new_buf: &Rc<RefCell<Vec<u8>>>) {
|
|
|
+ let old_buf_borrow = self.buf.deref().borrow();
|
|
|
+ let new_buf_borrow = new_buf.deref().borrow();
|
|
|
+ self.cursor = 0;
|
|
|
+ self.flush_cursor();
|
|
|
+ Printer::print(&new_buf_borrow[..]);
|
|
|
+ self.cursor = new_buf_borrow.len();
|
|
|
+ if new_buf_borrow.len() < old_buf_borrow.len() {
|
|
|
+ print!(
|
|
|
+ "{}",
|
|
|
+ " ".repeat(old_buf_borrow.len() - new_buf_borrow.len())
|
|
|
+ );
|
|
|
+ self.flush_cursor();
|
|
|
+ }
|
|
|
+ drop(old_buf_borrow);
|
|
|
+ drop(new_buf_borrow);
|
|
|
+ self.buf = Rc::clone(new_buf);
|
|
|
+ stdout().flush().unwrap();
|
|
|
}
|
|
|
|
|
|
fn print(bytes: &[u8]) {
|
|
@@ -338,78 +454,89 @@ impl Printer {
|
|
|
dsc::syscall!(SYS_PUT_STRING, cstr.as_ptr(), front_color, background_color);
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- fn set_cursor(buf: &mut Vec<u8>, old_index: usize, new_index: usize) {
|
|
|
- if new_index < buf.len() {
|
|
|
- let index = std::cmp::min(old_index, new_index);
|
|
|
- Printer::delete_from_index(index, buf.len());
|
|
|
- Printer::print(&buf[index..new_index]);
|
|
|
- Printer::print_cursor(buf[new_index]);
|
|
|
- Printer::print(&buf[new_index + 1..]);
|
|
|
- } else {
|
|
|
- Printer::delete_from_index(old_index, buf.len());
|
|
|
- Printer::print(&buf[old_index..]);
|
|
|
- }
|
|
|
- }
|
|
|
+pub fn _print_color_example() {
|
|
|
+ let example = "abcdefghijklmnopqrstuvwxyz";
|
|
|
+ println!("{}", example.bright_black());
|
|
|
+ println!("{}", example.bright_blue());
|
|
|
+ println!("{}", example.bright_cyan());
|
|
|
+ println!("{}", example.bright_green());
|
|
|
+ println!("{}", example.bright_magenta());
|
|
|
+ println!("{}", example.bright_purple());
|
|
|
+ println!("{}", example.bright_red());
|
|
|
+ println!("{}", example.bright_white());
|
|
|
+ println!("{}", example.bright_yellow());
|
|
|
+ println!("{}", example.black());
|
|
|
+ println!("{}", example.blue());
|
|
|
+ println!("{}", example.cyan());
|
|
|
+ println!("{}", example.green());
|
|
|
+ println!("{}", example.magenta());
|
|
|
+ println!("{}", example.purple());
|
|
|
+ println!("{}", example.red());
|
|
|
+ println!("{}", example.white());
|
|
|
+ println!("{}", example.yellow());
|
|
|
+}
|
|
|
|
|
|
- fn complete_command(command: &str) -> Vec<String> {
|
|
|
- let mut candidates: Vec<String> = Vec::new();
|
|
|
- for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
|
|
|
- if cmd.starts_with(command) {
|
|
|
- candidates.push(String::from(*cmd));
|
|
|
- }
|
|
|
+pub fn complete_command(command: &str) -> Vec<String> {
|
|
|
+ let mut candidates: Vec<String> = Vec::new();
|
|
|
+ for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
|
|
|
+ if cmd.starts_with(command) {
|
|
|
+ candidates.push(String::from(*cmd));
|
|
|
}
|
|
|
- candidates
|
|
|
}
|
|
|
+ candidates
|
|
|
+}
|
|
|
|
|
|
- fn complete_path(current_dir: &str, incomplete_path: &str) -> Vec<String> {
|
|
|
- let path = if incomplete_path.starts_with('/') {
|
|
|
- String::from(incomplete_path)
|
|
|
- } else {
|
|
|
- format!("{}/{}", current_dir, incomplete_path)
|
|
|
- };
|
|
|
-
|
|
|
- let mut candidates: Vec<String> = Vec::new();
|
|
|
- let dir: &str;
|
|
|
- let incomplete_name: &str;
|
|
|
- if let Some(index) = path.rfind('/') {
|
|
|
- dir = &path[..=index];
|
|
|
- if index < path.len() {
|
|
|
- incomplete_name = &path[index + 1..];
|
|
|
- } else {
|
|
|
- incomplete_name = "";
|
|
|
- }
|
|
|
+pub fn complete_path(incomplete_path: &str) -> Vec<String> {
|
|
|
+ let current_dir = std::env::current_dir().unwrap();
|
|
|
+ let current_dir = current_dir.to_str().unwrap();
|
|
|
+ let path = if incomplete_path.starts_with('/') {
|
|
|
+ String::from(incomplete_path)
|
|
|
+ } else {
|
|
|
+ format!("{}/{}", current_dir, incomplete_path)
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut candidates: Vec<String> = Vec::new();
|
|
|
+ let dir: &str;
|
|
|
+ let incomplete_name: &str;
|
|
|
+ if let Some(index) = path.rfind('/') {
|
|
|
+ dir = &path[..=index];
|
|
|
+ if index < path.len() {
|
|
|
+ incomplete_name = &path[index + 1..];
|
|
|
} else {
|
|
|
- dir = ".";
|
|
|
- incomplete_name = &path[..];
|
|
|
+ incomplete_name = "";
|
|
|
}
|
|
|
- match fs::read_dir(dir) {
|
|
|
- Ok(read_dir) => {
|
|
|
- if incomplete_name == "" {
|
|
|
- for entry in read_dir {
|
|
|
- let entry = entry.unwrap();
|
|
|
- let mut file_name = entry.file_name().into_string().unwrap();
|
|
|
+ } else {
|
|
|
+ dir = ".";
|
|
|
+ incomplete_name = &path[..];
|
|
|
+ }
|
|
|
+ match fs::read_dir(dir) {
|
|
|
+ Ok(read_dir) => {
|
|
|
+ if incomplete_name == "" {
|
|
|
+ for entry in read_dir {
|
|
|
+ let entry = entry.unwrap();
|
|
|
+ let mut file_name = entry.file_name().into_string().unwrap();
|
|
|
+ if entry.file_type().unwrap().is_dir() {
|
|
|
+ file_name.push('/');
|
|
|
+ }
|
|
|
+ candidates.push(file_name);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for entry in read_dir {
|
|
|
+ let entry = entry.unwrap();
|
|
|
+ let mut file_name = entry.file_name().into_string().unwrap();
|
|
|
+ if file_name.starts_with(incomplete_name) {
|
|
|
if entry.file_type().unwrap().is_dir() {
|
|
|
file_name.push('/');
|
|
|
}
|
|
|
candidates.push(file_name);
|
|
|
}
|
|
|
- } else {
|
|
|
- for entry in read_dir {
|
|
|
- let entry = entry.unwrap();
|
|
|
- let mut file_name = entry.file_name().into_string().unwrap();
|
|
|
- if file_name.starts_with(incomplete_name) {
|
|
|
- if entry.file_type().unwrap().is_dir() {
|
|
|
- file_name.push('/');
|
|
|
- }
|
|
|
- candidates.push(file_name);
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- Err(_) => {}
|
|
|
}
|
|
|
- return candidates;
|
|
|
+
|
|
|
+ Err(_) => {}
|
|
|
}
|
|
|
+ return candidates;
|
|
|
}
|