Skip to content

Commit

Permalink
修复打印提示符时,有字符存在会导致光标错位的问题 (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
MemoryShore authored Aug 28, 2024
1 parent 5b85994 commit cac674d
Show file tree
Hide file tree
Showing 2 changed files with 278 additions and 204 deletions.
252 changes: 48 additions & 204 deletions src/shell/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::{
cell::RefCell,
collections::HashMap,
fmt,
fs::{self, File, OpenOptions},
io::{self, stdout, BufRead, BufReader, Read, Write},
io::{BufRead, BufReader, Read, Write},
ops::Deref,
path::Path,
print,
Expand All @@ -15,47 +14,21 @@ use crate::keycode::{FunctionKeySuffix, SpecialKeycode};

use colored::Colorize;
use command::{BuildInCmd, Command, CommandError};
use printer::Printer;

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();
}
}
mod printer;

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 mod command;

const DEFAULT_HISTORY_COMMANDS_PATH: &str = "/history_commands.txt";

#[allow(dead_code)]
pub struct Shell {
history_commands: Vec<Rc<RefCell<Vec<u8>>>>,
history_path: String,
printer: Printer,
backend_task: HashMap<usize, Child>,
window_size: Option<WindowSize>,
}

impl Shell {
Expand All @@ -65,6 +38,7 @@ impl Shell {
history_path: DEFAULT_HISTORY_COMMANDS_PATH.to_string(),
printer: Printer::new(&Rc::new(RefCell::new(Vec::new()))),
backend_task: HashMap::new(),
window_size: WindowSize::new(),
};
shell.read_commands();
shell
Expand All @@ -78,20 +52,26 @@ impl Shell {
}

pub fn exec(&mut self) {
// 开启终端raw模式
// 开启终端raw模式
crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode");

// 循环读取一行

// 循环读取一行
loop {
self.printer.init_before_readline();
// 读取一行
// 读取一行
if self.readline() == 0 {
println!();
break;
}

let command_bytes = self.printer.buf.borrow().clone();

// 如果命令不以空格开头且不跟上一条命令相同,这条命令会被记录

// 如果命令不以空格开头且不跟上一条命令相同,这条命令会被记录
if !command_bytes.is_empty()
&& !command_bytes.starts_with(&[b' '])
Expand All @@ -108,6 +88,8 @@ impl Shell {
self.write_commands(&command_bytes);
};

// 命令不为空,执行命令

// 命令不为空,执行命令
if !command_bytes.iter().all(|&byte| byte == b' ') {
self.exec_commands_in_line(&command_bytes);
Expand Down Expand Up @@ -193,11 +175,15 @@ impl Shell {
}

FunctionKeySuffix::Left => {
self.printer.cursor_left();
if self.printer.cursor > 0 {
self.printer.cursor_left(1);
}
}

FunctionKeySuffix::Right => {
self.printer.cursor_right();
if self.printer.cursor < self.printer.buf.borrow().len() {
self.printer.cursor_right(1);
}
}

FunctionKeySuffix::Home => {
Expand Down Expand Up @@ -285,14 +271,12 @@ impl Shell {
1 => {
let old_fragment = fragments.last().unwrap();
let candidate = candidates.last().unwrap();
self.printer.cursor -= old_fragment.len();
self.printer.flush_cursor();
self.printer.cursor_left(old_fragment.len());
self.printer.delete(old_fragment.len());
self.printer
.insert(format!("{}{}", prefix, candidate).as_bytes());
}
2.. => {
let old_cursor = self.printer.cursor;
self.printer.end();
println!();
for candidate in candidates {
Expand All @@ -307,9 +291,10 @@ impl Shell {
}
println!();
self.printer.print_prompt();
Printer::print(&self.printer.buf.deref().borrow());
self.printer.cursor = old_cursor;
self.printer.flush_cursor();
print!(
"{}",
String::from_utf8(self.printer.buf.borrow().to_vec()).unwrap()
);
}
_ => {}
}
Expand All @@ -322,6 +307,7 @@ impl Shell {
1..=31 => {}
c => {
self.printer.insert(&[c]);
// String::from_utf8("abdsdf".as_bytes().to_vec()).unwrap();
}
}
}
Expand Down Expand Up @@ -351,164 +337,33 @@ impl Shell {
}
}

struct Printer {
/// 提示语
prompt: Prompt,
/// 缓存区,记录当前显示的内容
buf: Rc<RefCell<Vec<u8>>>,
/// 光标位置(不包括提示语)
cursor: usize,
#[allow(dead_code)]
struct WindowSize {
row: usize,
col: usize,
}

impl Printer {
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 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 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 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 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 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();
impl WindowSize {
pub fn new() -> Option<Self> {
let mut ws: libc::winsize = unsafe { std::mem::zeroed() };
if unsafe {
libc::ioctl(
libc::STDOUT_FILENO,
libc::TIOCGWINSZ,
&mut ws as *mut libc::winsize,
)
} == -1
{
None
} else {
Some(Self {
row: ws.ws_row.into(),
col: ws.ws_col.into(),
})
}
drop(old_buf_borrow);
drop(new_buf_borrow);
self.buf = Rc::clone(new_buf);
stdout().flush().unwrap();
}

fn print(bytes: &[u8]) {
print!("{}", String::from_utf8(bytes.to_vec()).unwrap());
}
}

// 测试终端颜色显示效果
#[allow(dead_code)]
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());
}

pub fn complete_command(command: &str) -> (&str, Vec<String>) {
let mut candidates: Vec<String> = Vec::new();
for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
Expand All @@ -530,16 +385,6 @@ pub fn complete_path(incomplete_path: &str) -> (&str, Vec<String>) {
incomplete_name = incomplete_path;
}
if let Ok(read_dir) = fs::read_dir(if dir.is_empty() { "." } else { 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();
Expand All @@ -553,7 +398,6 @@ pub fn complete_path(incomplete_path: &str) -> (&str, Vec<String>) {
candidates.push(file_name);
}
}
// }
}

return (dir, candidates);
Expand Down
Loading

0 comments on commit cac674d

Please sign in to comment.