Better shell history (#18)

* Save and load shell history from file

* Update shell history on key press

* Add comments
This commit is contained in:
Vincent Ollivier 2020-02-07 18:36:27 +01:00 committed by GitHub
parent 8ee2ca489f
commit c736091480
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 77 additions and 17 deletions

View File

@ -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);