mirror of https://github.com/vinc/moros.git
Better shell history (#18)
* Save and load shell history from file * Update shell history on key press * Add comments
This commit is contained in:
parent
8ee2ca489f
commit
c736091480
|
@ -1,4 +1,5 @@
|
|||
use crate::{print, user, kernel};
|
||||
use alloc::format;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::string::String;
|
||||
|
@ -33,6 +34,7 @@ impl Shell {
|
|||
}
|
||||
|
||||
pub fn run(&mut self) -> user::shell::ExitCode {
|
||||
self.load_history();
|
||||
self.print_prompt();
|
||||
loop {
|
||||
let (x, y) = kernel::vga::cursor_position();
|
||||
|
@ -51,6 +53,7 @@ impl Shell {
|
|||
}
|
||||
},
|
||||
'\n' => { // Newline
|
||||
self.update_history();
|
||||
self.update_autocomplete();
|
||||
print!("\n");
|
||||
if self.cmd.len() > 0 {
|
||||
|
@ -64,15 +67,22 @@ impl Shell {
|
|||
|
||||
let line = self.cmd.clone();
|
||||
match self.exec(&line) {
|
||||
ExitCode::CommandSuccessful => {},
|
||||
ExitCode::ShellExit => { return ExitCode::CommandSuccessful },
|
||||
_ => { print!("?\n") },
|
||||
ExitCode::CommandSuccessful => {
|
||||
self.save_history();
|
||||
},
|
||||
ExitCode::ShellExit => {
|
||||
return ExitCode::CommandSuccessful
|
||||
},
|
||||
_ => {
|
||||
print!("?\n")
|
||||
},
|
||||
}
|
||||
self.cmd.clear();
|
||||
}
|
||||
self.print_prompt();
|
||||
},
|
||||
'\t' => { // Tab
|
||||
self.update_history();
|
||||
self.print_autocomplete();
|
||||
},
|
||||
'↑' => { // Arrow up
|
||||
|
@ -81,27 +91,26 @@ impl Shell {
|
|||
if self.history_index > 0 {
|
||||
self.history_index -= 1;
|
||||
}
|
||||
if let Some(cmd) = self.history.iter().nth(self.history_index) {
|
||||
self.cmd = cmd.clone();
|
||||
kernel::vga::clear_row();
|
||||
print!("{}{}", self.prompt, self.cmd);
|
||||
}
|
||||
let cmd = &self.history[self.history_index];
|
||||
kernel::vga::clear_row();
|
||||
print!("{}{}", self.prompt, cmd);
|
||||
}
|
||||
},
|
||||
'↓' => { // Arrow down
|
||||
self.update_autocomplete();
|
||||
if self.history.len() > 0 {
|
||||
if self.history_index < self.history.len() - 1 {
|
||||
self.history_index += 1;
|
||||
}
|
||||
if let Some(cmd) = self.history.iter().nth(self.history_index) {
|
||||
self.cmd = cmd.clone();
|
||||
kernel::vga::clear_row();
|
||||
print!("{}{}", self.prompt, self.cmd);
|
||||
}
|
||||
if self.history_index < self.history.len() {
|
||||
self.history_index += 1;
|
||||
let cmd = if self.history_index < self.history.len() {
|
||||
&self.history[self.history_index]
|
||||
} else {
|
||||
&self.cmd
|
||||
};
|
||||
kernel::vga::clear_row();
|
||||
print!("{}{}", self.prompt, cmd);
|
||||
}
|
||||
},
|
||||
'←' => { // Arrow left
|
||||
self.update_history();
|
||||
self.update_autocomplete();
|
||||
if x > self.prompt.len() {
|
||||
kernel::vga::set_cursor_position(x - 1, y);
|
||||
|
@ -109,6 +118,7 @@ impl Shell {
|
|||
}
|
||||
},
|
||||
'→' => { // Arrow right
|
||||
self.update_history();
|
||||
self.update_autocomplete();
|
||||
if x < self.prompt.len() + self.cmd.len() {
|
||||
kernel::vga::set_cursor_position(x + 1, y);
|
||||
|
@ -116,6 +126,7 @@ impl Shell {
|
|||
}
|
||||
},
|
||||
'\x08' => { // Backspace
|
||||
self.update_history();
|
||||
self.update_autocomplete();
|
||||
let cmd = self.cmd.clone();
|
||||
if cmd.len() > 0 && x > self.prompt.len() {
|
||||
|
@ -133,6 +144,7 @@ impl Shell {
|
|||
}
|
||||
},
|
||||
c => {
|
||||
self.update_history();
|
||||
self.update_autocomplete();
|
||||
if c.is_ascii() && kernel::vga::is_printable(c as u8) {
|
||||
let cmd = self.cmd.clone();
|
||||
|
@ -151,6 +163,50 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
// Called when a key other than up or down is pressed while in history
|
||||
// mode. The history index point to a command that will be selected and
|
||||
// the index will be reset to the length of the history vector to signify
|
||||
// that the editor is no longer in history mode.
|
||||
pub fn update_history(&mut self) {
|
||||
if self.history_index != self.history.len() {
|
||||
self.cmd = self.history[self.history_index].clone();
|
||||
self.history_index = self.history.len();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_history(&mut self) {
|
||||
let username = "admin"; // TODO: The login command should write the username somewhere
|
||||
let pathname = format!("/usr/{}/.shell_history", username);
|
||||
|
||||
if let Some(file) = kernel::fs::File::open(&pathname) {
|
||||
let contents = file.read_to_string();
|
||||
for line in contents.split('\n') {
|
||||
let cmd = line.trim();
|
||||
if cmd.len() > 0 {
|
||||
self.history.push(cmd.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.history_index = self.history.len();
|
||||
}
|
||||
|
||||
pub fn save_history(&mut self) {
|
||||
let username = "admin"; // TODO: The login command should write the username somewhere
|
||||
let pathname = format!("/usr/{}/.shell_history", username);
|
||||
|
||||
let mut contents = String::new();
|
||||
for cmd in &self.history {
|
||||
contents.push_str(&format!("{}\n", cmd));
|
||||
}
|
||||
|
||||
let mut file = match kernel::fs::File::open(&pathname) {
|
||||
Some(file) => file,
|
||||
None => kernel::fs::File::create(&pathname).unwrap(),
|
||||
};
|
||||
|
||||
file.write(&contents.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
pub fn print_autocomplete(&mut self) {
|
||||
let mut args = self.parse(&self.cmd);
|
||||
let i = args.len() - 1;
|
||||
|
@ -195,6 +251,10 @@ impl Shell {
|
|||
print!("{}{}", self.prompt, cmd);
|
||||
}
|
||||
|
||||
// Called when a key other than tab is pressed while in autocomplete mode.
|
||||
// The autocomplete index point to an argument that will be added to the
|
||||
// command and the index will be reset to signify that the editor is no
|
||||
// longer in autocomplete mode.
|
||||
pub fn update_autocomplete(&mut self) {
|
||||
if self.autocomplete_index != 0 {
|
||||
let mut args = self.parse(&self.cmd);
|
||||
|
|
Loading…
Reference in New Issue