From aef3885b85e910df32ad0df3c0d3ec01131c96d5 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Thu, 19 Sep 2024 23:31:48 -0400 Subject: [PATCH 01/56] Initial attempt to read file into string --- applications/less/src/lib.rs | 118 ++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index aea7f9cc65..4e7cf7e69a 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -1,27 +1,32 @@ //! A text file reader which allows the user using `Up` and `Down` to scroll the screen. #![no_std] -// FIXME - extern crate alloc; -// extern crate task; -// extern crate getopts; -// extern crate path; -// extern crate fs_node; -// extern crate keycodes_ascii; -// extern crate libterm; -// extern crate spin; -// extern crate app_io; -// extern crate stdio; -// extern crate core2; -// #[macro_use] extern crate log; +extern crate task; +extern crate getopts; +extern crate path; +extern crate fs_node; +extern crate keycodes_ascii; +extern crate libterm; +extern crate spin; +extern crate app_io; +extern crate stdio; +extern crate core2; +#[macro_use] extern crate log; + +use fs_node::{FileOrDir}; +use path::Path; +use app_io::println; // use keycodes_ascii::{Keycode, KeyAction}; // use core::str; -use alloc::{ - vec::Vec, - string::String, -}; +use alloc::vec; +use vec::Vec; +use alloc::string::String; +use alloc::string::ToString; +// vec::Vec, +// string::String, +//}; // use getopts::Options; // use path::Path; // use fs_node::FileOrDir; @@ -39,47 +44,48 @@ use alloc::{ // end: usize // } + // /// Read the whole file to a String. -// fn get_content_string(file_path: String) -> Result { -// let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { -// return Err("failed to get current task".to_string()); -// }; -// let path = Path::new(file_path); +fn get_content_string(file_path: String) -> Result { + let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { + return Err("failed to get current task".to_string()); + }; + let path = Path::new(file_path); -// // navigate to the filepath specified by first argument -// match path.get(&curr_wd) { -// Some(file_dir_enum) => { -// match file_dir_enum { -// FileOrDir::Dir(directory) => { -// Err(format!("{:?} is a directory, cannot 'less' non-files.", directory.lock().get_name())) -// } -// FileOrDir::File(file) => { -// let mut file_locked = file.lock(); -// let file_size = file_locked.len(); -// let mut string_slice_as_bytes = vec![0; file_size]; -// let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { -// Ok(num) => num, -// Err(e) => { -// return Err(format!("Failed to read {:?}, error {:?}", -// file_locked.get_name(), e)) -// } -// }; -// let read_string = match str::from_utf8(&string_slice_as_bytes) { -// Ok(string_slice) => string_slice, -// Err(utf8_err) => { -// return Err(format!("File {:?} was not a printable UTF-8 text file: {}", -// file_locked.get_name(), utf8_err)) -// } -// }; -// Ok(read_string.to_string()) -// } -// } -// }, -// _ => { -// Err(format!("Couldn't find file at path {}", path)) -// } -// } -// } + // navigate to the filepath specified by first argument + match path.get(&curr_wd) { + Some(file_dir_enum) => { + match file_dir_enum { + FileOrDir::Dir(directory) => { + println!("{:?} is a directory, cannot 'less' non-files.", directory.lock().get_name()) + } + FileOrDir::File(file) => { + let mut file_locked = file.lock(); + let file_size = file_locked.len(); + let mut string_slice_as_bytes = vec![0; file_size]; + let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { + Ok(num) => num, + //Err(e) => { + // println!("Failed to read {:?}, error {:?}", + // file_locked.get_name(), e) + //} + }; + let read_string = match str::from_utf8(&string_slice_as_bytes) { + Ok(string_slice) => string_slice, + //Err(utf8_err) => { + // println!("File {:?} was not a printable UTF-8 text file: {}", + // file_locked.get_name(), utf8_err) + //} + }; + Ok(read_string.to_string()) + } + } + }, + _ => { + println!("Couldn't find file at path {}", path) + } + } +} // /// This function parses the text file. It scans through the whole file and records the string slice // /// for each line. This function has full UTF-8 support, which means that the case where a single character From 9f597e262a2b5ec846d9dbbf786f5cb32a851efe Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Tue, 5 Nov 2024 00:35:57 -0500 Subject: [PATCH 02/56] remove errors to read file into initial string --- applications/less/src/lib.rs | 46 ++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 4e7cf7e69a..d0266c98da 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -12,18 +12,22 @@ extern crate spin; extern crate app_io; extern crate stdio; extern crate core2; -#[macro_use] extern crate log; +//#[macro_use] extern crate log; use fs_node::{FileOrDir}; use path::Path; -use app_io::println; +//use app_io::println; // use keycodes_ascii::{Keycode, KeyAction}; -// use core::str; +use core::str; +use alloc::{ + string::String, + vec::Vec, +}; use alloc::vec; -use vec::Vec; -use alloc::string::String; +use alloc::format; use alloc::string::ToString; +use app_io::println; // vec::Vec, // string::String, //}; @@ -50,14 +54,15 @@ fn get_content_string(file_path: String) -> Result { let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task".to_string()); }; - let path = Path::new(file_path); + let path = Path::new(file_path.as_str()); // navigate to the filepath specified by first argument match path.get(&curr_wd) { - Some(file_dir_enum) => { + + Some(file_dir_enum) => { match file_dir_enum { FileOrDir::Dir(directory) => { - println!("{:?} is a directory, cannot 'less' non-files.", directory.lock().get_name()) + Err(format!("Is a directory, cannot 'less' non-files.")) } FileOrDir::File(file) => { let mut file_locked = file.lock(); @@ -65,25 +70,30 @@ fn get_content_string(file_path: String) -> Result { let mut string_slice_as_bytes = vec![0; file_size]; let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { Ok(num) => num, - //Err(e) => { - // println!("Failed to read {:?}, error {:?}", - // file_locked.get_name(), e) - //} + Err(e) => { + println!("Failed to read error "); + return Err(format!("Failed to file size: {:?}", e)); + } }; let read_string = match str::from_utf8(&string_slice_as_bytes) { Ok(string_slice) => string_slice, - //Err(utf8_err) => { - // println!("File {:?} was not a printable UTF-8 text file: {}", - // file_locked.get_name(), utf8_err) - //} + Err(utf8_err) => { + println!("File was not a printable UTF-8 text file"); + return Err(format!("Failed to read file: {:?}", utf8_err)); + } }; Ok(read_string.to_string()) } } }, - _ => { - println!("Couldn't find file at path {}", path) + None => { + // Handle the case where the path wasn't found + // // For example, you could return an error or print a message: + Err(format!("Couldn't find file at path")) } + //_ => { + //println!("Couldn't find file at path {}", path) + //} } } From b1bcaa4b33e4cefd97ec6743418b4fde7a848032 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 6 Nov 2024 00:01:56 -0500 Subject: [PATCH 03/56] write content file when 'less' is called --- applications/less/src/lib.rs | 75 +++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index d0266c98da..80fbb267c1 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -28,6 +28,7 @@ use alloc::vec; use alloc::format; use alloc::string::ToString; use app_io::println; +use getopts::Options; // vec::Vec, // string::String, //}; @@ -62,7 +63,7 @@ fn get_content_string(file_path: String) -> Result { Some(file_dir_enum) => { match file_dir_enum { FileOrDir::Dir(directory) => { - Err(format!("Is a directory, cannot 'less' non-files.")) + Err(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())) } FileOrDir::File(file) => { let mut file_locked = file.lock(); @@ -216,7 +217,7 @@ fn get_content_string(file_path: String) -> Result { // } -pub fn main(_args: Vec) -> isize { +pub fn main(args: Vec) -> isize { // // Get stdout. // let stdout = match app_io::stdout() { @@ -228,52 +229,54 @@ pub fn main(_args: Vec) -> isize { // }; // // Set and parse options. - // let mut opts = Options::new(); - // opts.optflag("h", "help", "print this help menu"); - // let matches = match opts.parse(args) { - // Ok(m) => m, - // Err(e) => { - // error!("{}", e); - // print_usage(opts, stdout); - // return -1; - // } - // }; - // if matches.opt_present("h") { - // print_usage(opts, stdout); - // return 0; - // } - // if matches.free.is_empty() { - // print_usage(opts, stdout); - // return 0; - // } - // let filename = matches.free[0].clone(); - - // if let Err(e) = run(filename) { - // error!("{}", e); - // return 1; - // } + let mut opts = Options::new(); + opts.optflag("h", "help", "print this help menu"); + let matches = match opts.parse(args) { + Ok(m) => m, + Err(e) => { + //Err(format!("Is a directory, cannot 'less' non-files.")) + format!("{}", e); + //print_usage(opts, stdout); + return -1; + } + }; + if matches.opt_present("h") { + //print_usage(opts, stdout); + return 0; + } + if matches.free.is_empty() { + //print_usage(opts, stdout); + return 0; + } + let filename = matches.free[0].clone(); + + let _content = get_content_string(filename); + + //if let Err(e) = run(filename) { + // error!("{}", e); + // return 1; + //} 0 } -// fn run(filename: String) -> Result<(), String> { +//fn run(filename: String) -> Result<(), String> { -// // Acquire key event queue. + // Acquire key event queue. // let key_event_queue = app_io::take_key_event_queue()?; // let key_event_queue = (*key_event_queue).as_ref() // .ok_or("failed to take key event reader")?; -// // Read the whole file to a String. + // Read the whole file to a String. // let content = get_content_string(filename)?; // // Get it run. // let map = parse_content(&content)?; - // Ok(event_handler_loop(&content, &map, key_event_queue)?) -// } +//} -// fn print_usage(opts: Options, stdout: StdioWriter) { -// let _ = stdout.lock().write_all(format!("{}\n", opts.usage(USAGE)).as_bytes()); -// } +//fn print_usage(opts: Options, stdout: StdioWriter) { +// let _ = stdout.lock().write_all(format!("{}\n", opts.usage(USAGE)).as_bytes()); +//} -// const USAGE: &'static str = "Usage: less file -// read files"; +//const USAGE: &'static str = "Usage: less file +//read files"; From c22a389ec9ba998694215d36115beac7e9b74036 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Fri, 8 Nov 2024 16:47:24 -0500 Subject: [PATCH 04/56] add print string contents to verify existing functionality --- applications/less/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 80fbb267c1..d32fa62231 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -29,9 +29,6 @@ use alloc::format; use alloc::string::ToString; use app_io::println; use getopts::Options; -// vec::Vec, -// string::String, -//}; // use getopts::Options; // use path::Path; // use fs_node::FileOrDir; @@ -83,6 +80,7 @@ fn get_content_string(file_path: String) -> Result { return Err(format!("Failed to read file: {:?}", utf8_err)); } }; + println!("{}", read_string); Ok(read_string.to_string()) } } From abe76e2413c2e24bf51225b4c67b4a2e5a980b25 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Fri, 8 Nov 2024 18:30:27 -0500 Subject: [PATCH 05/56] Add terminal size to determine line splice --- applications/less/Cargo.toml | 1 + applications/less/src/lib.rs | 56 ++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/applications/less/Cargo.toml b/applications/less/Cargo.toml index 77806ecf34..05ab00039a 100644 --- a/applications/less/Cargo.toml +++ b/applications/less/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Zhiyao Ma "] getopts = "0.2.21" spin = "0.9.4" core2 = { version = "0.4.0", default-features = false, features = ["alloc", "nightly"] } +terminal_size = "0.1" [dependencies.task] path = "../../kernel/task" diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index d32fa62231..f44bc649b3 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -12,6 +12,7 @@ extern crate spin; extern crate app_io; extern crate stdio; extern crate core2; +extern crate terminal_size; //#[macro_use] extern crate log; use fs_node::{FileOrDir}; @@ -29,23 +30,30 @@ use alloc::format; use alloc::string::ToString; use app_io::println; use getopts::Options; +use terminal_size::{terminal_size, Width, Height}; // use getopts::Options; // use path::Path; // use fs_node::FileOrDir; -// use alloc::collections::BTreeMap; +use alloc::collections::BTreeMap; // use libterm::Terminal; // use spin::Mutex; // use stdio::{StdioWriter, KeyEventQueueReader}; // use core2::io::Write; -// /// The metadata for each line in the file. -// struct LineSlice { -// /// The starting index in the String for a line. (inclusive) -// start: usize, -// /// The ending index in the String for a line. (exclusive) -// end: usize -// } +// The metadata for each line in the file. +struct LineSlice { + // The starting index in the String for a line. (inclusive) + start: usize, + // The ending index in the String for a line. (exclusive) + end: usize +} +fn get_terminal_dimensions() -> Option<(usize, usize)> { + match terminal_size() { + Some((Width(width), Height(height))) => Some((width as usize, height as usize)), + None => None, // If terminal size could not be determined + } +} // /// Read the whole file to a String. fn get_content_string(file_path: String) -> Result { @@ -100,15 +108,15 @@ fn get_content_string(file_path: String) -> Result { // /// for each line. This function has full UTF-8 support, which means that the case where a single character // /// occupies multiple bytes are well considered. The slice index returned by this function is guaranteed // /// not to cause panic. -// fn parse_content(content: &String) -> Result, &'static str> { -// // Get the width and height of the terminal screen. -// let (width, _height) = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")? -// .lock() -// .get_text_dimensions(); +fn parse_content(content: &String) -> Result, &'static str> { + // Get the width and height of the terminal screen. + let (width, _height) = get_terminal_dimensions() + .ok_or("couldn't get terminal dimensions")?; -// // Record the slice index of each line. -// let mut map: BTreeMap = BTreeMap::new(); -// // Number of the current line. + println!("{} {}", width, _height); + // Record the slice index of each line. + let mut map: BTreeMap = BTreeMap::new(); + // Number of the current line. // let mut cur_line_num: usize = 0; // // Number of characters in the current line. // let mut char_num_in_line: usize = 0; @@ -133,8 +141,8 @@ fn get_content_string(file_path: String) -> Result { // } // map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); -// Ok(map) -// } + Ok(map) +} // /// Display part of the file (may be whole file if the file is short) to the terminal, starting // /// at line number `line_start`. @@ -248,8 +256,18 @@ pub fn main(args: Vec) -> isize { } let filename = matches.free[0].clone(); - let _content = get_content_string(filename); + let content = get_content_string(filename); + match content { + Ok(content) => { + parse_content(&content); // Now `content` is a `String`, and `&content` is a `&String` + }, + Err(e) => { + // Handle the error (e.g.,) + println!("Error: {}", e); + } + } + //if let Err(e) = run(filename) { // error!("{}", e); // return 1; From 98a43a356564316fccd032df7fc61c8e55d7cbe2 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Sat, 9 Nov 2024 18:24:14 -0500 Subject: [PATCH 06/56] Save recent progress to use correct terminal size --- Cargo.lock | 2 ++ applications/less/Cargo.toml | 7 +++++- applications/less/src/lib.rs | 43 +++++++++++++++++++++++++----------- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bc9205de2..9b2899d43a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1779,6 +1779,8 @@ dependencies = [ "spin 0.9.4", "stdio", "task", + "text_display", + "window", ] [[package]] diff --git a/applications/less/Cargo.toml b/applications/less/Cargo.toml index 05ab00039a..2023dc4b2a 100644 --- a/applications/less/Cargo.toml +++ b/applications/less/Cargo.toml @@ -7,7 +7,12 @@ authors = ["Zhiyao Ma "] getopts = "0.2.21" spin = "0.9.4" core2 = { version = "0.4.0", default-features = false, features = ["alloc", "nightly"] } -terminal_size = "0.1" + +[dependencies.text_display] +path = "../../kernel/displayable/text_display" + +[dependencies.window] +path = "../../kernel/window" [dependencies.task] path = "../../kernel/task" diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index f44bc649b3..5679151bd7 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -12,7 +12,9 @@ extern crate spin; extern crate app_io; extern crate stdio; extern crate core2; -extern crate terminal_size; +//extern crate terminal_size; +//extern crate text_display; + //#[macro_use] extern crate log; use fs_node::{FileOrDir}; @@ -30,12 +32,17 @@ use alloc::format; use alloc::string::ToString; use app_io::println; use getopts::Options; -use terminal_size::{terminal_size, Width, Height}; +//use terminal_size::{terminal_size, Width, Height}; // use getopts::Options; // use path::Path; // use fs_node::FileOrDir; use alloc::collections::BTreeMap; -// use libterm::Terminal; +use libterm::Terminal; +//use window::Window; +//use text_display::TextDisplay; +//use libterm::cursor::Cursor; +use alloc::sync::Arc; +use spin::Mutex; // use spin::Mutex; // use stdio::{StdioWriter, KeyEventQueueReader}; // use core2::io::Write; @@ -48,12 +55,12 @@ struct LineSlice { end: usize } -fn get_terminal_dimensions() -> Option<(usize, usize)> { - match terminal_size() { - Some((Width(width), Height(height))) => Some((width as usize, height as usize)), - None => None, // If terminal size could not be determined - } -} +//fn get_terminal_dimensions() -> Option<(usize, usize)> { +// match terminal_size() { +// Some((Width(width), Height(height))) => Some((width as usize, height as usize)), +// None => None, // If terminal size could not be determined +// } +//} // /// Read the whole file to a String. fn get_content_string(file_path: String) -> Result { @@ -88,7 +95,7 @@ fn get_content_string(file_path: String) -> Result { return Err(format!("Failed to read file: {:?}", utf8_err)); } }; - println!("{}", read_string); + //println!("{}", read_string); Ok(read_string.to_string()) } } @@ -110,8 +117,18 @@ fn get_content_string(file_path: String) -> Result { // /// not to cause panic. fn parse_content(content: &String) -> Result, &'static str> { // Get the width and height of the terminal screen. - let (width, _height) = get_terminal_dimensions() - .ok_or("couldn't get terminal dimensions")?; + //let (width, _height) = get_terminal_dimensions() + // .ok_or("couldn't get terminal dimensions")?; + let terminal = match Terminal::new() { + Ok(term) => Arc::new(Mutex::new(term)), + Err(e) => { + return Err(e); + //println!("Failed to initialize terminal: {}", e); + } + }; + + let (width, _height) = terminal.lock().get_text_dimensions(); + println!("{} {}", width, _height); // Record the slice index of each line. @@ -260,7 +277,7 @@ pub fn main(args: Vec) -> isize { match content { Ok(content) => { - parse_content(&content); // Now `content` is a `String`, and `&content` is a `&String` + let map = parse_content(&content); // Now `content` is a `String`, and `&content` is a `&String` }, Err(e) => { // Handle the error (e.g.,) From 727c1faa23c152deb34d560ae78d4b2329d7d330 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Sun, 10 Nov 2024 00:21:34 -0500 Subject: [PATCH 07/56] Add correct instance of Terminal for dimension --- applications/less/src/lib.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 5679151bd7..9fc692508f 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -118,19 +118,13 @@ fn get_content_string(file_path: String) -> Result { fn parse_content(content: &String) -> Result, &'static str> { // Get the width and height of the terminal screen. //let (width, _height) = get_terminal_dimensions() - // .ok_or("couldn't get terminal dimensions")?; - let terminal = match Terminal::new() { - Ok(term) => Arc::new(Mutex::new(term)), - Err(e) => { - return Err(e); - //println!("Failed to initialize terminal: {}", e); - } - }; - - let (width, _height) = terminal.lock().get_text_dimensions(); - - - println!("{} {}", width, _height); + // .ok_or("couldn't get terminal dimensions")?;e + + // let (width, _height) = terminal.lock().get_text_dimensions(); + let mut terminal = Terminal::new().expect("Failed to create terminal"); + let (width, height) = terminal.get_text_dimensions(); + + // println!("{} {}", width, _height); // Record the slice index of each line. let mut map: BTreeMap = BTreeMap::new(); // Number of the current line. @@ -285,6 +279,8 @@ pub fn main(args: Vec) -> isize { } } + let mut terminal = Terminal::new().expect("Failed to create terminal"); + //if let Err(e) = run(filename) { // error!("{}", e); // return 1; From 583d574626a1a0ad4048d0e4452725eb89b53ff4 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Sun, 10 Nov 2024 00:22:35 -0500 Subject: [PATCH 08/56] Remove window and text display import --- applications/less/Cargo.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/applications/less/Cargo.toml b/applications/less/Cargo.toml index 2023dc4b2a..77806ecf34 100644 --- a/applications/less/Cargo.toml +++ b/applications/less/Cargo.toml @@ -8,12 +8,6 @@ getopts = "0.2.21" spin = "0.9.4" core2 = { version = "0.4.0", default-features = false, features = ["alloc", "nightly"] } -[dependencies.text_display] -path = "../../kernel/displayable/text_display" - -[dependencies.window] -path = "../../kernel/window" - [dependencies.task] path = "../../kernel/task" From da8fdf818ae5c9224bb2f7d4e9540c922a9c3ef6 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Sun, 10 Nov 2024 17:28:13 -0500 Subject: [PATCH 09/56] Delete extra 'Terminal' instance --- applications/less/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 9fc692508f..1d0a841564 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -123,6 +123,7 @@ fn parse_content(content: &String) -> Result, &'stati // let (width, _height) = terminal.lock().get_text_dimensions(); let mut terminal = Terminal::new().expect("Failed to create terminal"); let (width, height) = terminal.get_text_dimensions(); + println!("{} {}", width, height); // println!("{} {}", width, _height); // Record the slice index of each line. @@ -279,8 +280,6 @@ pub fn main(args: Vec) -> isize { } } - let mut terminal = Terminal::new().expect("Failed to create terminal"); - //if let Err(e) = run(filename) { // error!("{}", e); // return 1; From 6b871abf9b7447cff6172de06cce73fff4cc487b Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Sun, 10 Nov 2024 17:51:07 -0500 Subject: [PATCH 10/56] Initial implementation of display content function --- applications/less/src/lib.rs | 112 +++++++++++++++++------------------ 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 1d0a841564..6c77c4e6ee 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -129,64 +129,64 @@ fn parse_content(content: &String) -> Result, &'stati // Record the slice index of each line. let mut map: BTreeMap = BTreeMap::new(); // Number of the current line. -// let mut cur_line_num: usize = 0; -// // Number of characters in the current line. -// let mut char_num_in_line: usize = 0; -// // Starting index in the String of the current line. -// let mut line_start_idx: usize = 0; -// // The previous character during the iteration. Set '\0' as the initial value since we don't expect -// // to encounter this character in the beginning of the file. -// let mut previous_char: char = '\0'; + let mut cur_line_num: usize = 0; + // Number of characters in the current line. + let mut char_num_in_line: usize = 0; + // Starting index in the String of the current line. + let mut line_start_idx: usize = 0; + // The previous character during the iteration. Set '\0' as the initial value since we don't expect + // to encounter this character in the beginning of the file. + let mut previous_char: char = '\0'; -// // Iterate through the whole file. -// // `c` is the current character. `str_idx` is the index of the first byte of the current character. -// for (str_idx, c) in content.char_indices() { -// // When we need to begin a new line, record the previous line in the map. -// if char_num_in_line == width || previous_char == '\n' { -// map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); -// char_num_in_line = 0; -// line_start_idx = str_idx; -// cur_line_num += 1; -// } -// char_num_in_line += 1; -// previous_char = c; -// } -// map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); + // Iterate through the whole file. + // `c` is the current character. `str_idx` is the index of the first byte of the current character. + for (str_idx, c) in content.char_indices() { + // When we need to begin a new line, record the previous line in the map. + if char_num_in_line == width || previous_char == '\n' { + map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); + char_num_in_line = 0; + line_start_idx = str_idx; + cur_line_num += 1; + } + char_num_in_line += 1; + previous_char = c; + } + map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); Ok(map) } // /// Display part of the file (may be whole file if the file is short) to the terminal, starting // /// at line number `line_start`. -// fn display_content(content: &String, map: &BTreeMap, -// line_start: usize, terminal: &Arc>) -// -> Result<(), &'static str> { -// // Get exclusive control of the terminal. It is locked through the whole function to -// // avoid the overhead of locking it multiple times. -// let mut locked_terminal = terminal.lock(); +fn display_content(content: &String, map: &BTreeMap, + line_start: usize, terminal: &Arc>) + -> Result<(), &'static str> { + // Get exclusive control of the terminal. It is locked through the whole function to + // avoid the overhead of locking it multiple times. + let mut locked_terminal = terminal.lock(); -// // Calculate the last line to display. Make sure we don't extend over the end of the file. -// let (_width, height) = locked_terminal.get_text_dimensions(); -// let mut line_end: usize = line_start + height; -// if line_end > map.len() { -// line_end = map.len(); -// } + // Calculate the last line to display. Make sure we don't extend over the end of the file. + let (_width, height) = locked_terminal.get_text_dimensions(); + let mut line_end: usize = line_start + height; + if line_end > map.len() { + line_end = map.len(); + } -// // Refresh the terminal with the lines we've selected. -// let start_indices = match map.get(&line_start) { -// Some(indices) => indices, -// None => return Err("failed to get the byte indices of the first line") -// }; -// let end_indices = match map.get(&(line_end - 1)) { -// Some(indices) => indices, -// None => return Err("failed to get the byte indices of the last line") -// }; -// locked_terminal.clear(); -// locked_terminal.print_to_terminal( -// content[start_indices.start..end_indices.end].to_string() -// ); -// locked_terminal.refresh_display() -// } + // Refresh the terminal with the lines we've selected. + let start_indices = match map.get(&line_start) { + Some(indices) => indices, + None => return Err("failed to get the byte indices of the first line") + }; + let end_indices = match map.get(&(line_end - 1)) { + Some(indices) => indices, + None => return Err("failed to get the byte indices of the last line") + }; + locked_terminal.clear(); + locked_terminal.print_to_terminal( + content[start_indices.start..end_indices.end].to_string() + ); + locked_terminal.refresh_display() +} // /// Handle user keyboard strikes and perform corresponding operations. // fn event_handler_loop(content: &String, map: &BTreeMap, @@ -238,13 +238,13 @@ fn parse_content(content: &String) -> Result, &'stati pub fn main(args: Vec) -> isize { // // Get stdout. - // let stdout = match app_io::stdout() { - // Ok(stdout) => stdout, - // Err(e) => { - // error!("{}", e); - // return 1; - // } - // }; + let stdout = match app_io::stdout() { + Ok(stdout) => stdout, + Err(e) => { + error!("{}", e); + return 1; + } + }; // // Set and parse options. let mut opts = Options::new(); From c2354b2c43e12714534aeb85d78ffbad4def0257 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Sun, 10 Nov 2024 18:20:47 -0500 Subject: [PATCH 11/56] Event handler implementation --- applications/less/src/lib.rs | 93 +++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 6c77c4e6ee..4a5f187a58 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -43,7 +43,9 @@ use libterm::Terminal; //use libterm::cursor::Cursor; use alloc::sync::Arc; use spin::Mutex; -// use spin::Mutex; +use stdio::KeyEventQueueReader; +use keycodes_ascii::Keycode; +use keycodes_ascii::KeyAction; // use stdio::{StdioWriter, KeyEventQueueReader}; // use core2::io::Write; @@ -163,7 +165,8 @@ fn display_content(content: &String, map: &BTreeMap, -> Result<(), &'static str> { // Get exclusive control of the terminal. It is locked through the whole function to // avoid the overhead of locking it multiple times. - let mut locked_terminal = terminal.lock(); + let locked_terminal = Terminal::new().expect("Failed to create terminal"); + // let mut locked_terminal = terminal.lock(); // Calculate the last line to display. Make sure we don't extend over the end of the file. let (_width, height) = locked_terminal.get_text_dimensions(); @@ -189,50 +192,50 @@ fn display_content(content: &String, map: &BTreeMap, } // /// Handle user keyboard strikes and perform corresponding operations. -// fn event_handler_loop(content: &String, map: &BTreeMap, -// key_event_queue: &KeyEventQueueReader) -// -> Result<(), &'static str> { +fn event_handler_loop(content: &String, map: &BTreeMap, + key_event_queue: &KeyEventQueueReader) + -> Result<(), &'static str> { + // Get a reference to this task's terminal. The terminal is *not* locked here. + //let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; + let terminal = Terminal::new().expect("Failed to create terminal"); -// // Get a reference to this task's terminal. The terminal is *not* locked here. -// let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; + // Display the beginning of the file. + let mut line_start: usize = 0; + display_content(content, map, 0, &terminal)?; -// // Display the beginning of the file. -// let mut line_start: usize = 0; -// display_content(content, map, 0, &terminal)?; - -// // Handle user keyboard strikes. -// loop { -// match key_event_queue.read_one() { -// Some(keyevent) => { -// if keyevent.action != KeyAction::Pressed { continue; } -// match keyevent.keycode { -// // Quit the program on "Q". -// Keycode::Q => { -// let mut locked_terminal = terminal.lock(); -// locked_terminal.clear(); -// return locked_terminal.refresh_display() -// }, -// // Scroll down a line on "Down". -// Keycode::Down => { -// if line_start + 1 < map.len() { -// line_start += 1; -// } -// display_content(content, map, line_start, &terminal)?; -// }, -// // Scroll up a line on "Up". -// Keycode::Up => { -// if line_start > 0 { -// line_start -= 1; -// } -// display_content(content, map, line_start, &terminal)?; -// } -// _ => {} -// } -// }, -// _ => {} -// } -// } -// } + // Handle user keyboard strikes. + loop { + match key_event_queue.read_one() { + Some(keyevent) => { + if keyevent.action != KeyAction::Pressed { continue; } + match keyevent.keycode { + // Quit the program on "Q". + Keycode::Q => { + let mut locked_terminal = terminal.lock(); + locked_terminal.clear(); + return locked_terminal.refresh_display() + }, + // Scroll down a line on "Down". + Keycode::Down => { + if line_start + 1 < map.len() { + line_start += 1; + } + display_content(content, map, line_start, &terminal)?; + }, + // Scroll up a line on "Up". + Keycode::Up => { + if line_start > 0 { + line_start -= 1; + } + display_content(content, map, line_start, &terminal)?; + } + _ => {} + } + }, + _ => {} + } + } +} pub fn main(args: Vec) -> isize { @@ -241,7 +244,7 @@ pub fn main(args: Vec) -> isize { let stdout = match app_io::stdout() { Ok(stdout) => stdout, Err(e) => { - error!("{}", e); + println!("{}", e); return 1; } }; From 621a8577ba7309ce5120f207e04868ecd6b88f96 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Sun, 10 Nov 2024 19:06:49 -0500 Subject: [PATCH 12/56] Add 'shared_map' to grab the terminal instance --- kernel/app_io/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/app_io/src/lib.rs b/kernel/app_io/src/lib.rs index 440d3563f2..0c030ed1e2 100644 --- a/kernel/app_io/src/lib.rs +++ b/kernel/app_io/src/lib.rs @@ -18,6 +18,8 @@ use alloc::{format, sync::Arc}; use core2::io::{self, Error, ErrorKind, Read, Write}; use stdio::{StdioReader, StdioWriter}; use tty::{LineDiscipline, Slave}; +use sync_block::MutexGuard; +use hashbrown::HashMap; pub trait ImmutableRead: Send + Sync + 'static { fn read(&self, buf: &mut [u8]) -> io::Result; @@ -104,6 +106,10 @@ mod shared_maps { } } +pub fn get_my_terminal() -> MutexGuard<'static, HashMap> { + shared_maps::lock_stream_map() +} + /// Shells call this function to store queue stdio streams for applications. If /// there are any existing readers/writers for the task (which should not /// happen in normal practice), it returns the old one, otherwise returns None. From 993d6c3e2555ea8ae17ab5a76f2694af5b9a11e9 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Tue, 12 Nov 2024 00:19:29 -0500 Subject: [PATCH 13/56] use new instance of 'Terminal' to write to display --- applications/less/src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 4a5f187a58..d25bd187b8 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -123,6 +123,10 @@ fn parse_content(content: &String) -> Result, &'stati // .ok_or("couldn't get terminal dimensions")?;e // let (width, _height) = terminal.lock().get_text_dimensions(); + //let mut t = app_io::get_my_terminal(). + // get(&task::get_my_current_task_id()) + // .map(|property| property.terminal.clone()); + let mut terminal = Terminal::new().expect("Failed to create terminal"); let (width, height) = terminal.get_text_dimensions(); println!("{} {}", width, height); @@ -161,11 +165,11 @@ fn parse_content(content: &String) -> Result, &'stati // /// Display part of the file (may be whole file if the file is short) to the terminal, starting // /// at line number `line_start`. fn display_content(content: &String, map: &BTreeMap, - line_start: usize, terminal: &Arc>) + line_start: usize, terminal: &Terminal) -> Result<(), &'static str> { // Get exclusive control of the terminal. It is locked through the whole function to // avoid the overhead of locking it multiple times. - let locked_terminal = Terminal::new().expect("Failed to create terminal"); + let mut locked_terminal = Terminal::new().expect("Failed to create terminal"); // let mut locked_terminal = terminal.lock(); // Calculate the last line to display. Make sure we don't extend over the end of the file. @@ -197,7 +201,7 @@ fn event_handler_loop(content: &String, map: &BTreeMap, -> Result<(), &'static str> { // Get a reference to this task's terminal. The terminal is *not* locked here. //let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; - let terminal = Terminal::new().expect("Failed to create terminal"); + let mut terminal = Terminal::new().expect("Failed to create terminal"); // Display the beginning of the file. let mut line_start: usize = 0; @@ -211,9 +215,9 @@ fn event_handler_loop(content: &String, map: &BTreeMap, match keyevent.keycode { // Quit the program on "Q". Keycode::Q => { - let mut locked_terminal = terminal.lock(); - locked_terminal.clear(); - return locked_terminal.refresh_display() + //let mut locked_terminal = terminal.lock(); + //locked_terminal.clear(); + return terminal.refresh_display() }, // Scroll down a line on "Down". Keycode::Down => { From 0b2e906f5ffff85424bf69d4bdf0b4c41bd378a1 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Sun, 17 Nov 2024 14:29:32 -0600 Subject: [PATCH 14/56] attempt to add libterm dependency --- kernel/app_io/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/app_io/Cargo.toml b/kernel/app_io/Cargo.toml index 807702c826..1bebbcfc2b 100644 --- a/kernel/app_io/Cargo.toml +++ b/kernel/app_io/Cargo.toml @@ -17,6 +17,9 @@ path = "../../kernel/task" [dependencies.stdio] path = "../../libs/stdio" +[dependencies.libterm] +path = "../../kernel/libterm" + [dependencies.sync_block] path = "../../kernel/sync_block" From 527ca79f9209716d12fe464486a3e9dbedaa0965 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Sun, 17 Nov 2024 15:31:27 -0600 Subject: [PATCH 15/56] Attempt to display string to terminal --- Cargo.lock | 2 -- applications/less/src/lib.rs | 9 +++++++++ kernel/app_io/Cargo.toml | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b2899d43a..5bc9205de2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1779,8 +1779,6 @@ dependencies = [ "spin 0.9.4", "stdio", "task", - "text_display", - "window", ] [[package]] diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index d25bd187b8..2d58e1d536 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -49,6 +49,7 @@ use keycodes_ascii::KeyAction; // use stdio::{StdioWriter, KeyEventQueueReader}; // use core2::io::Write; + // The metadata for each line in the file. struct LineSlice { // The starting index in the String for a line. (inclusive) @@ -287,6 +288,14 @@ pub fn main(args: Vec) -> isize { } } + //let terminal = Arc::new(Mutex::new(Terminal::new().expect("Failed to create terminal"))); + //let mut locked_terminal = terminal.lock().expect("failed to lock terminal"); + + let mut locked_terminal = Terminal::new().expect("Failed to create termina;"); + let message = "Hello, Theseus!"; + locked_terminal.print_to_terminal(message.to_string()); + locked_terminal.refresh_display().unwrap(); + //if let Err(e) = run(filename) { // error!("{}", e); // return 1; diff --git a/kernel/app_io/Cargo.toml b/kernel/app_io/Cargo.toml index 1bebbcfc2b..f60af2cd53 100644 --- a/kernel/app_io/Cargo.toml +++ b/kernel/app_io/Cargo.toml @@ -17,8 +17,8 @@ path = "../../kernel/task" [dependencies.stdio] path = "../../libs/stdio" -[dependencies.libterm] -path = "../../kernel/libterm" +#[dependencies.libterm] +#path = "../../kernel/libterm" [dependencies.sync_block] path = "../../kernel/sync_block" From 2dbf85d1b075724dcdd5ee73222b6b97ce0c2cf7 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Mon, 18 Nov 2024 22:16:17 -0600 Subject: [PATCH 16/56] Get text pagination to project file onto terminal --- kernel/libterm/src/lib.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/kernel/libterm/src/lib.rs b/kernel/libterm/src/lib.rs index bea9ab297c..478bce8811 100644 --- a/kernel/libterm/src/lib.rs +++ b/kernel/libterm/src/lib.rs @@ -449,6 +449,40 @@ impl Terminal { Ok(terminal) } + + pub fn text_editor(s: String) -> Result { + let wm_ref = window_manager::WINDOW_MANAGER.get().ok_or("The window manager is not initialized")?; + let (window_width, window_height) = { + let wm = wm_ref.lock(); + wm.get_screen_size() + }; + + let window = window::Window::new( + Coord::new(0, 0), + window_width, + window_height, + FONT_BACKGROUND_COLOR, + )?; + + let area = window.area(); + let text_display = TextDisplay::new(area.width(), area.height(), FONT_FOREGROUND_COLOR, FONT_BACKGROUND_COLOR)?; + + let mut terminal = Terminal { + window, + scrollback_buffer: String::new(), + scroll_start_idx: 0, + is_scroll_end: true, + text_display, + cursor: Cursor::default(), + }; + terminal.display_text()?; + + terminal.print_to_terminal(s.clone()); + Ok(terminal) + } + + + /// Adds a string to be printed to the terminal to the terminal scrollback buffer. /// Note that one needs to call `refresh_display` to get things actually printed. pub fn print_to_terminal(&mut self, s: String) { From 443e7822e698c860e62305445da718c0117190d1 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Tue, 19 Nov 2024 23:00:36 -0600 Subject: [PATCH 17/56] Remove prompt instruction after entering 'less' editing mode --- Cargo.lock | 1 + applications/less/Cargo.toml | 4 +++ applications/shell/src/lib.rs | 62 ++++++++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bc9205de2..0b8c50b07b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1776,6 +1776,7 @@ dependencies = [ "libterm", "log", "path", + "shell", "spin 0.9.4", "stdio", "task", diff --git a/applications/less/Cargo.toml b/applications/less/Cargo.toml index 77806ecf34..11c493f060 100644 --- a/applications/less/Cargo.toml +++ b/applications/less/Cargo.toml @@ -29,6 +29,10 @@ path = "../../kernel/app_io" [dependencies.stdio] path = "../../libs/stdio" +[dependencies.shell] +path = "../shell" + + [dependencies.log] version = "0.4.8" diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index ea8197dd84..fb8f97be6b 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -135,7 +135,7 @@ enum AppErr { SpawnErr(String) } -struct Shell { +pub struct Shell { /// Variable that stores the task id of any application manually spawned from the terminal jobs: BTreeMap, /// Map task number to job number. @@ -166,13 +166,15 @@ struct Shell { /// The terminal's current environment env: Arc>, /// the terminal that is bind with the shell instance - terminal: Arc> + terminal: Arc>, + /// The indicator to show "text editing" mode + less: bool } impl Shell { /// Create a new shell. Currently the shell will bind to the default terminal instance provided /// by the `app_io` crate. - fn new() -> Result { + pub fn new() -> Result { // Initialize a dfqueue for the terminal object to handle printing from applications. // Note that this is only to support legacy output. Newly developed applications should // turn to use `stdio` provided by the `stdio` crate together with the support of `app_io`. @@ -202,10 +204,52 @@ impl Shell { print_consumer, print_producer, env: Arc::new(Mutex::new(env)), + less: false, terminal }) } + pub fn new_editor(s: String) -> Result { + // Initialize a dfqueue for the terminal object to handle printing from applications. + // Note that this is only to support legacy output. Newly developed applications should + // turn to use `stdio` provided by the `stdio` crate together with the support of `app_io`. + let terminal_print_dfq: DFQueue = DFQueue::new(); + let print_consumer = terminal_print_dfq.into_consumer(); + let print_producer = print_consumer.obtain_producer(); + + let key_event_queue: KeyEventQueue = KeyEventQueue::new(); + let key_event_producer = key_event_queue.get_writer(); + let key_event_consumer = key_event_queue.get_reader(); + + let env = Environment::default(); + + let terminal = Arc::new(Mutex::new(Terminal::text_editor(s.clone())?)); + + let mut shell = Shell { + jobs: BTreeMap::new(), + task_to_job: BTreeMap::new(), + key_event_consumer: Arc::new(Mutex::new(Some(key_event_consumer))), + key_event_producer, + fg_job_num: None, + cmdline: String::new(), + input_buffer: String::new(), + command_history: Vec::new(), + history_index: 0, + buffered_cmd_recorded: false, + print_consumer, + print_producer, + env: Arc::new(Mutex::new(env)), + less: true, + terminal + }; + + shell.input_buffer = String::new(); + shell.key_event_consumer = Arc::new(Mutex::new(None)); + Ok(shell) + } + + + /// Insert a character to the command line buffer in the shell. /// The position to insert is determined by the position of the cursor in the terminal. /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. @@ -259,13 +303,15 @@ impl Shell { /// Set the command line to be a specific string. /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. fn set_cmdline(&mut self, s: String, sync_terminal: bool) -> Result<(), &'static str> { + let mut s1 = s.clone(); + s1.push_str("hello"); if !self.cmdline.is_empty() { self.clear_cmdline(sync_terminal)?; } self.cmdline = s.clone(); self.update_cursor_pos(0)?; if sync_terminal { - self.terminal.lock().print_to_terminal(s); + self.terminal.lock().print_to_terminal(s1); } Ok(()) } @@ -1028,6 +1074,7 @@ impl Shell { // so we need to downcast it from Any to isize. let val: Option<&isize> = exit_status.downcast_ref::(); info!("terminal: task [{}] returned exit value: {:?}", exited_task_id, val); + self.terminal.lock().print_to_terminal(format!("hello")); if let Some(val) = val { self.terminal.lock().print_to_terminal( format!("task [{exited_task_id}] exited with code {val} ({val:#X})\n") @@ -1161,6 +1208,11 @@ impl Shell { /// Redisplays the terminal prompt (does not insert a newline before it) fn redisplay_prompt(&mut self) { + if self.less { + // Do not display the prompt + return; + } + let curr_env = self.env.lock(); let mut prompt = curr_env.working_dir.lock().get_absolute_path(); prompt = format!("{prompt}: "); @@ -1238,7 +1290,7 @@ impl Shell { /// The print queue is handled first inside the loop iteration, which means that all print events in the print /// queue will always be printed to the text display before input events or any other managerial functions are handled. /// This allows for clean appending to the scrollback buffer and prevents interleaving of text. - fn start(mut self) -> Result<(), &'static str> { + pub fn start(mut self) -> Result<(), &'static str> { let mut need_refresh = false; let mut need_prompt = false; self.redisplay_prompt(); From 48724c9fcc0b3de34913539f355205825cf988fa Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Fri, 22 Nov 2024 16:40:07 -0500 Subject: [PATCH 18/56] Add missing text pagination to Missing filename ("less --help" for help) --- applications/less/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 2d58e1d536..8ecdb2f9de 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -12,6 +12,7 @@ extern crate spin; extern crate app_io; extern crate stdio; extern crate core2; +extern crate shell; //extern crate terminal_size; //extern crate text_display; @@ -46,6 +47,7 @@ use spin::Mutex; use stdio::KeyEventQueueReader; use keycodes_ascii::Keycode; use keycodes_ascii::KeyAction; +use shell::Shell; // use stdio::{StdioWriter, KeyEventQueueReader}; // use core2::io::Write; @@ -281,6 +283,8 @@ pub fn main(args: Vec) -> isize { match content { Ok(content) => { let map = parse_content(&content); // Now `content` is a `String`, and `&content` is a `&String` + let shell = Shell::new_editor(content).expect("Failed to create new editor shell"); + shell.start().unwrap(); }, Err(e) => { // Handle the error (e.g.,) From f10c4d16e956d4d27d2966bfe5645cdd67d5684a Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Fri, 22 Nov 2024 18:54:34 -0500 Subject: [PATCH 19/56] Test 'Tab' command working --- applications/shell/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index fb8f97be6b..51d0464b80 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -243,8 +243,8 @@ impl Shell { terminal }; - shell.input_buffer = String::new(); - shell.key_event_consumer = Arc::new(Mutex::new(None)); + // shell.input_buffer = String::new(); + // shell.key_event_consumer = Arc::new(Mutex::new(None)); Ok(shell) } @@ -527,6 +527,9 @@ impl Shell { // Perform command line auto completion. if keyevent.keycode == Keycode::Tab { if self.fg_job_num.is_none() { + let prompt = format!("Tab"); + self.terminal.lock().print_to_terminal(prompt); + //self.terminal.print_to_terminal(prompt); self.complete_cmdline()?; } return Ok(()); @@ -620,6 +623,8 @@ impl Shell { // Cycles to the next previous command if keyevent.keycode == Keycode::Up { + //let c = "Hello"; + //self.terminal.lock().print_to_terminal("Hello"); self.goto_previous_command()?; return Ok(()); } @@ -1208,14 +1213,9 @@ impl Shell { /// Redisplays the terminal prompt (does not insert a newline before it) fn redisplay_prompt(&mut self) { - if self.less { - // Do not display the prompt - return; - } - let curr_env = self.env.lock(); let mut prompt = curr_env.working_dir.lock().get_absolute_path(); - prompt = format!("{prompt}: "); + prompt = format!("{prompt} : "); self.terminal.lock().print_to_terminal(prompt); self.terminal.lock().print_to_terminal(self.cmdline.clone()); } From 8b7049dce088a61d2246c9c20b298bfcb35471de Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Fri, 22 Nov 2024 23:26:35 -0600 Subject: [PATCH 20/56] Prelimary key word short cut to Quit --- applications/less/src/lib.rs | 8 ++++---- applications/shell/src/lib.rs | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 8ecdb2f9de..f67b022f02 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -295,10 +295,10 @@ pub fn main(args: Vec) -> isize { //let terminal = Arc::new(Mutex::new(Terminal::new().expect("Failed to create terminal"))); //let mut locked_terminal = terminal.lock().expect("failed to lock terminal"); - let mut locked_terminal = Terminal::new().expect("Failed to create termina;"); - let message = "Hello, Theseus!"; - locked_terminal.print_to_terminal(message.to_string()); - locked_terminal.refresh_display().unwrap(); + //let mut locked_terminal = Terminal::new().expect("Failed to create termina;"); + //let message = "Hello, Theseus!"; + //locked_terminal.print_to_terminal(message.to_string()); + //locked_terminal.refresh_display().unwrap(); //if let Err(e) = run(filename) { // error!("{}", e); diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 51d0464b80..7c506bfd79 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -527,14 +527,24 @@ impl Shell { // Perform command line auto completion. if keyevent.keycode == Keycode::Tab { if self.fg_job_num.is_none() { - let prompt = format!("Tab"); - self.terminal.lock().print_to_terminal(prompt); + //let prompt = format!("Tab"); + //self.terminal.lock().print_to_terminal(prompt); //self.terminal.print_to_terminal(prompt); self.complete_cmdline()?; } return Ok(()); } + + if keyevent.keycode == Keycode::Q { + if self.fg_job_num.is_none() { + let prompt = format!("Quit"); + self.terminal.lock().print_to_terminal(prompt); + } + return Ok(()); + } + + // Tracks what the user does whenever she presses the backspace button if keyevent.keycode == Keycode::Backspace { if self.fg_job_num.is_some() { From 1fd2b54a899a5cfd9cf274d78b7a3d23cb8590b5 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Sun, 24 Nov 2024 18:07:54 -0600 Subject: [PATCH 21/56] Fix clippy error messages with string format error --- applications/shell/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 7c506bfd79..f205508ec5 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -538,8 +538,7 @@ impl Shell { if keyevent.keycode == Keycode::Q { if self.fg_job_num.is_none() { - let prompt = format!("Quit"); - self.terminal.lock().print_to_terminal(prompt); + self.terminal.lock().print_to_terminal("Quit".to_string()); } return Ok(()); } @@ -1089,7 +1088,6 @@ impl Shell { // so we need to downcast it from Any to isize. let val: Option<&isize> = exit_status.downcast_ref::(); info!("terminal: task [{}] returned exit value: {:?}", exited_task_id, val); - self.terminal.lock().print_to_terminal(format!("hello")); if let Some(val) = val { self.terminal.lock().print_to_terminal( format!("task [{exited_task_id}] exited with code {val} ({val:#X})\n") From 286ace506fb2d0852b29f4e34e7b5fe6cd08b666 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Sun, 24 Nov 2024 22:38:27 -0600 Subject: [PATCH 22/56] Convert 'str' to String --- applications/less/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index f67b022f02..363a3376a6 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -108,7 +108,7 @@ fn get_content_string(file_path: String) -> Result { None => { // Handle the case where the path wasn't found // // For example, you could return an error or print a message: - Err(format!("Couldn't find file at path")) + Err(format!("Couldn't find file at path".to_string())) } //_ => { //println!("Couldn't find file at path {}", path) @@ -167,7 +167,7 @@ fn parse_content(content: &String) -> Result, &'stati // /// Display part of the file (may be whole file if the file is short) to the terminal, starting // /// at line number `line_start`. -fn display_content(content: &String, map: &BTreeMap, +fn display_content(content: &str, map: &BTreeMap, line_start: usize, terminal: &Terminal) -> Result<(), &'static str> { // Get exclusive control of the terminal. It is locked through the whole function to From 26bf332b5f72d350bac47c00f24a29589092c5f7 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Sun, 24 Nov 2024 22:52:24 -0600 Subject: [PATCH 23/56] Handle none case with warning --- applications/less/src/lib.rs | 157 +++++++++++++++++------------------ 1 file changed, 77 insertions(+), 80 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 363a3376a6..0b235138f8 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -106,13 +106,10 @@ fn get_content_string(file_path: String) -> Result { } }, None => { - // Handle the case where the path wasn't found - // // For example, you could return an error or print a message: - Err(format!("Couldn't find file at path".to_string())) + println!("Warning: path is not found"); + // Optionally, return a default or handle it another way. + Ok("".to_string()) // Example: return empty string or a default value } - //_ => { - //println!("Couldn't find file at path {}", path) - //} } } @@ -167,82 +164,82 @@ fn parse_content(content: &String) -> Result, &'stati // /// Display part of the file (may be whole file if the file is short) to the terminal, starting // /// at line number `line_start`. -fn display_content(content: &str, map: &BTreeMap, - line_start: usize, terminal: &Terminal) - -> Result<(), &'static str> { - // Get exclusive control of the terminal. It is locked through the whole function to - // avoid the overhead of locking it multiple times. - let mut locked_terminal = Terminal::new().expect("Failed to create terminal"); - // let mut locked_terminal = terminal.lock(); - - // Calculate the last line to display. Make sure we don't extend over the end of the file. - let (_width, height) = locked_terminal.get_text_dimensions(); - let mut line_end: usize = line_start + height; - if line_end > map.len() { - line_end = map.len(); - } - - // Refresh the terminal with the lines we've selected. - let start_indices = match map.get(&line_start) { - Some(indices) => indices, - None => return Err("failed to get the byte indices of the first line") - }; - let end_indices = match map.get(&(line_end - 1)) { - Some(indices) => indices, - None => return Err("failed to get the byte indices of the last line") - }; - locked_terminal.clear(); - locked_terminal.print_to_terminal( - content[start_indices.start..end_indices.end].to_string() - ); - locked_terminal.refresh_display() -} +// fn display_content(content: &str, map: &BTreeMap, +// line_start: usize, terminal: &Terminal) +// -> Result<(), &'static str> { +// // Get exclusive control of the terminal. It is locked through the whole function to +// // avoid the overhead of locking it multiple times. +// let mut locked_terminal = Terminal::new().expect("Failed to create terminal"); +// // let mut locked_terminal = terminal.lock(); + +// // Calculate the last line to display. Make sure we don't extend over the end of the file. +// let (_width, height) = locked_terminal.get_text_dimensions(); +// let mut line_end: usize = line_start + height; +// if line_end > map.len() { +// line_end = map.len(); +// } + +// // Refresh the terminal with the lines we've selected. +// let start_indices = match map.get(&line_start) { +// Some(indices) => indices, +// None => return Err("failed to get the byte indices of the first line") +// }; +// let end_indices = match map.get(&(line_end - 1)) { +// Some(indices) => indices, +// None => return Err("failed to get the byte indices of the last line") +// }; +// locked_terminal.clear(); +// locked_terminal.print_to_terminal( +// content[start_indices.start..end_indices.end].to_string() +// ); +// locked_terminal.refresh_display() +// } // /// Handle user keyboard strikes and perform corresponding operations. -fn event_handler_loop(content: &String, map: &BTreeMap, - key_event_queue: &KeyEventQueueReader) - -> Result<(), &'static str> { - // Get a reference to this task's terminal. The terminal is *not* locked here. - //let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; - let mut terminal = Terminal::new().expect("Failed to create terminal"); - - // Display the beginning of the file. - let mut line_start: usize = 0; - display_content(content, map, 0, &terminal)?; - - // Handle user keyboard strikes. - loop { - match key_event_queue.read_one() { - Some(keyevent) => { - if keyevent.action != KeyAction::Pressed { continue; } - match keyevent.keycode { - // Quit the program on "Q". - Keycode::Q => { - //let mut locked_terminal = terminal.lock(); - //locked_terminal.clear(); - return terminal.refresh_display() - }, - // Scroll down a line on "Down". - Keycode::Down => { - if line_start + 1 < map.len() { - line_start += 1; - } - display_content(content, map, line_start, &terminal)?; - }, - // Scroll up a line on "Up". - Keycode::Up => { - if line_start > 0 { - line_start -= 1; - } - display_content(content, map, line_start, &terminal)?; - } - _ => {} - } - }, - _ => {} - } - } -} +// fn event_handler_loop(content: &String, map: &BTreeMap, +// key_event_queue: &KeyEventQueueReader) +// -> Result<(), &'static str> { +// // Get a reference to this task's terminal. The terminal is *not* locked here. +// //let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; +// let mut terminal = Terminal::new().expect("Failed to create terminal"); + +// // Display the beginning of the file. +// let mut line_start: usize = 0; +// display_content(content, map, 0, &terminal)?; + +// // Handle user keyboard strikes. +// loop { +// match key_event_queue.read_one() { +// Some(keyevent) => { +// if keyevent.action != KeyAction::Pressed { continue; } +// match keyevent.keycode { +// // Quit the program on "Q". +// Keycode::Q => { +// //let mut locked_terminal = terminal.lock(); +// //locked_terminal.clear(); +// return terminal.refresh_display() +// }, +// // Scroll down a line on "Down". +// Keycode::Down => { +// if line_start + 1 < map.len() { +// line_start += 1; +// } +// display_content(content, map, line_start, &terminal)?; +// }, +// // Scroll up a line on "Up". +// Keycode::Up => { +// if line_start > 0 { +// line_start -= 1; +// } +// display_content(content, map, line_start, &terminal)?; +// } +// _ => {} +// } +// }, +// _ => {} +// } +// } +// } pub fn main(args: Vec) -> isize { From 7661174e975f0cf4db15c311843ce4db642d5a84 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Sun, 24 Nov 2024 23:07:47 -0600 Subject: [PATCH 24/56] Keycode 'Q' to leave out of text --- applications/shell/src/lib.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index f205508ec5..0029d9d0e4 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -225,7 +225,7 @@ impl Shell { let terminal = Arc::new(Mutex::new(Terminal::text_editor(s.clone())?)); - let mut shell = Shell { + let shell = Shell { jobs: BTreeMap::new(), task_to_job: BTreeMap::new(), key_event_consumer: Arc::new(Mutex::new(Some(key_event_consumer))), @@ -537,9 +537,11 @@ impl Shell { if keyevent.keycode == Keycode::Q { - if self.fg_job_num.is_none() { - self.terminal.lock().print_to_terminal("Quit".to_string()); - } + self.less = false; + self.redisplay_prompt(); + //if self.fg_job_num.is_none() { + // self.terminal.lock().print_to_terminal("Quit".to_string()); + //} return Ok(()); } @@ -1221,11 +1223,13 @@ impl Shell { /// Redisplays the terminal prompt (does not insert a newline before it) fn redisplay_prompt(&mut self) { - let curr_env = self.env.lock(); - let mut prompt = curr_env.working_dir.lock().get_absolute_path(); - prompt = format!("{prompt} : "); - self.terminal.lock().print_to_terminal(prompt); - self.terminal.lock().print_to_terminal(self.cmdline.clone()); + if !self.less { + let curr_env = self.env.lock(); + let mut prompt = curr_env.working_dir.lock().get_absolute_path(); + prompt = format!("{prompt} : "); + self.terminal.lock().print_to_terminal(prompt); + self.terminal.lock().print_to_terminal(self.cmdline.clone()); + } } /// If there is any output event from running application, print it to the screen, otherwise it does nothing. From bebf802b77c7a1051d1460b1c39617fa7c13914d Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Sun, 24 Nov 2024 23:08:52 -0600 Subject: [PATCH 25/56] Remove LineSlice struct for now --- applications/less/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 0b235138f8..039bc97037 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -53,12 +53,12 @@ use shell::Shell; // The metadata for each line in the file. -struct LineSlice { - // The starting index in the String for a line. (inclusive) - start: usize, - // The ending index in the String for a line. (exclusive) - end: usize -} +// struct LineSlice { +// // The starting index in the String for a line. (inclusive) +// start: usize, +// // The ending index in the String for a line. (exclusive) +// end: usize +// } //fn get_terminal_dimensions() -> Option<(usize, usize)> { // match terminal_size() { From 1230e77fd54cff9255c6fbef53e9de2c73fc97de Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Sun, 24 Nov 2024 23:09:38 -0600 Subject: [PATCH 26/56] Remove 'Terminal' instantiation --- applications/less/src/lib.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 039bc97037..0eee550a9e 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -289,18 +289,6 @@ pub fn main(args: Vec) -> isize { } } - //let terminal = Arc::new(Mutex::new(Terminal::new().expect("Failed to create terminal"))); - //let mut locked_terminal = terminal.lock().expect("failed to lock terminal"); - - //let mut locked_terminal = Terminal::new().expect("Failed to create termina;"); - //let message = "Hello, Theseus!"; - //locked_terminal.print_to_terminal(message.to_string()); - //locked_terminal.refresh_display().unwrap(); - - //if let Err(e) = run(filename) { - // error!("{}", e); - // return 1; - //} 0 } From b69d78eeea8ea284d388581a1bcb0a48f451dea4 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Mon, 25 Nov 2024 23:18:26 -0600 Subject: [PATCH 27/56] Restart fresh terminal instance upon quiting --- applications/less/src/lib.rs | 12 ++++++------ applications/shell/src/lib.rs | 12 +++--------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 0eee550a9e..41235813ca 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -53,12 +53,12 @@ use shell::Shell; // The metadata for each line in the file. -// struct LineSlice { -// // The starting index in the String for a line. (inclusive) -// start: usize, -// // The ending index in the String for a line. (exclusive) -// end: usize -// } +struct LineSlice { + // The starting index in the String for a line. (inclusive) + start: usize, + // The ending index in the String for a line. (exclusive) + end: usize +} //fn get_terminal_dimensions() -> Option<(usize, usize)> { // match terminal_size() { diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 0029d9d0e4..b1d839cef8 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -248,8 +248,6 @@ impl Shell { Ok(shell) } - - /// Insert a character to the command line buffer in the shell. /// The position to insert is determined by the position of the cursor in the terminal. /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. @@ -527,9 +525,6 @@ impl Shell { // Perform command line auto completion. if keyevent.keycode == Keycode::Tab { if self.fg_job_num.is_none() { - //let prompt = format!("Tab"); - //self.terminal.lock().print_to_terminal(prompt); - //self.terminal.print_to_terminal(prompt); self.complete_cmdline()?; } return Ok(()); @@ -537,11 +532,10 @@ impl Shell { if keyevent.keycode == Keycode::Q { - self.less = false; + self.less = false; + self.input_buffer = String::new(); + self.terminal.lock().clear(); self.redisplay_prompt(); - //if self.fg_job_num.is_none() { - // self.terminal.lock().print_to_terminal("Quit".to_string()); - //} return Ok(()); } From b19dc2d59ba712c388c0b9088c603482c4655814 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Thu, 28 Nov 2024 13:05:21 -0600 Subject: [PATCH 28/56] Migrate less functionality to 'shell' --- applications/shell/src/lib.rs | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index b1d839cef8..3f206d74ec 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -44,6 +44,7 @@ use core2::io::Write; use core::ops::Deref; use app_io::IoStreams; use fs_node::FileOrDir; +use core::str; /// The status of a job. #[derive(PartialEq)] @@ -209,6 +210,10 @@ impl Shell { }) } + pub fn get_terminal(&self) -> Arc> { + Arc::clone(&self.terminal) + } + pub fn new_editor(s: String) -> Result { // Initialize a dfqueue for the terminal object to handle printing from applications. // Note that this is only to support legacy output. Newly developed applications should @@ -1402,6 +1407,7 @@ impl Shell { "fg" => return true, "bg" => return true, "clear" => return true, + "less" => return true, _ => return false } } @@ -1419,6 +1425,7 @@ impl Shell { "fg" => self.execute_internal_fg(), "bg" => self.execute_internal_bg(), "clear" => self.execute_internal_clear(), + "less" => self.execute_internal_less(iter), _ => Ok(()) } } else { @@ -1426,6 +1433,60 @@ impl Shell { } } + fn get_content_string(&self, file_path: String) -> Result { + let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { + return Err("failed to get current task".to_string()); + }; + let path = Path::new(file_path.as_str()); + + // navigate to the filepath specified by first argument + match path.get(&curr_wd) { + + Some(file_dir_enum) => { + match file_dir_enum { + FileOrDir::Dir(directory) => { + Err(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())) + } + FileOrDir::File(file) => { + let mut file_locked = file.lock(); + let file_size = file_locked.len(); + let mut string_slice_as_bytes = vec![0; file_size]; + let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { + Ok(num) => num, + Err(e) => { + self.terminal.lock().print_to_terminal("Failed to read error ".to_string()); + return Err(format!("Failed to file size: {:?}", e)); + } + }; + let read_string = match str::from_utf8(&string_slice_as_bytes) { + Ok(string_slice) => string_slice, + Err(utf8_err) => { + self.terminal.lock().print_to_terminal("File was not a printable UTF-8 text file".to_string()); + return Err(format!("Failed to read file: {:?}", utf8_err)); + } + }; + self.terminal.lock().print_to_terminal(read_string.to_string()); + Ok(read_string.to_string()) + } + } + }, + None => { + self.terminal.lock().print_to_terminal("Warning: path is not found".to_string()); + // Optionally, return a default or handle it another way. + Ok("".to_string()) // Example: return empty string or a default value + } + } + } + + fn execute_internal_less<'a, I>(&mut self, iter: I)-> Result<(), &'static str> { + self.terminal.lock().clear(); + self.clear_cmdline(false)?; + self.redisplay_prompt(); + self.terminal.lock().print_to_terminal("less".to_string()); + Ok(()) + } + + fn execute_internal_clear(&mut self) -> Result<(), &'static str> { self.terminal.lock().clear(); self.clear_cmdline(false)?; From 0c00a64550df4dbc7bf6367abadea19db4bc5287 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Thu, 28 Nov 2024 14:19:43 -0600 Subject: [PATCH 29/56] Displayable text added --- applications/shell/src/lib.rs | 40 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 3f206d74ec..2474c52a4c 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1425,7 +1425,7 @@ impl Shell { "fg" => self.execute_internal_fg(), "bg" => self.execute_internal_bg(), "clear" => self.execute_internal_clear(), - "less" => self.execute_internal_less(iter), + "less" => self.execute_internal_less(iter.collect()), _ => Ok(()) } } else { @@ -1433,11 +1433,33 @@ impl Shell { } } + fn execute_internal_less(&mut self, args: Vec<&str>) -> Result<(), &'static str> { + if args.len() < 1 { + self.terminal.lock().print_to_terminal("Not enough arguments provided.\n".to_string()); + self.clear_cmdline(false)?; + self.redisplay_prompt(); + return Ok(()) + } + + let file_path = args[0]; + self.terminal.lock().print_to_terminal(format!("The second element is: {}\n", file_path).to_string()); + let content = self.get_content_string(file_path.to_string()); + + self.terminal.lock().clear(); + self.clear_cmdline(false)?; + self.terminal.lock().print_to_terminal(content.unwrap_or("".to_string())); + self.redisplay_prompt(); + Ok(()) + } + fn get_content_string(&self, file_path: String) -> Result { let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task".to_string()); }; - let path = Path::new(file_path.as_str()); + + let prompt = self.env.lock().working_dir.lock().get_absolute_path(); + let full_path = format!("{}/{}", prompt.to_string(), file_path.to_string()); + let path = Path::new(full_path.as_str()); // navigate to the filepath specified by first argument match path.get(&curr_wd) { @@ -1465,28 +1487,18 @@ impl Shell { return Err(format!("Failed to read file: {:?}", utf8_err)); } }; - self.terminal.lock().print_to_terminal(read_string.to_string()); + //self.terminal.lock().print_to_terminal(read_string.to_string()); Ok(read_string.to_string()) } } }, None => { - self.terminal.lock().print_to_terminal("Warning: path is not found".to_string()); - // Optionally, return a default or handle it another way. + self.terminal.lock().print_to_terminal(format!("Path not found: {}\n", path.to_string()).to_string()); Ok("".to_string()) // Example: return empty string or a default value } } } - fn execute_internal_less<'a, I>(&mut self, iter: I)-> Result<(), &'static str> { - self.terminal.lock().clear(); - self.clear_cmdline(false)?; - self.redisplay_prompt(); - self.terminal.lock().print_to_terminal("less".to_string()); - Ok(()) - } - - fn execute_internal_clear(&mut self) -> Result<(), &'static str> { self.terminal.lock().clear(); self.clear_cmdline(false)?; From eeae44d6bdd550313c46bfde72ad1a2d83752158 Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Thu, 28 Nov 2024 14:29:30 -0600 Subject: [PATCH 30/56] test file for large file --- .../test_files/text/opening_crawl_all.txt | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 extra_files/test_files/text/opening_crawl_all.txt diff --git a/extra_files/test_files/text/opening_crawl_all.txt b/extra_files/test_files/text/opening_crawl_all.txt new file mode 100644 index 0000000000..f8ed9ac021 --- /dev/null +++ b/extra_files/test_files/text/opening_crawl_all.txt @@ -0,0 +1,81 @@ +It is a period of civil war. +Rebel spaceships, striking +from a hidden base, have won +their first victory against +the evil Galactic Empire. + +During the battle, Rebel +spies managed to steal secret +plans to the Empire's +ultimate weapon, the DEATH +STAR, an armored space +station with enough power to +destroy an entire planet. + +Pursued by the Empire's +sinister agents, Princess +Leia races home aboard her +starship, custodian of the +stolen plans that can save +her people and restore +freedom to the galaxy.... + +There is unrest in the Galactic +Senate. Several thousand solar +systems have declared their +intentions to leave the Republic. + +This separatist movement, +under the leadership of the +mysterious Count Dooku, has +made it difficult for the limited +number of Jedi Knights to maintain +peace and order in the galaxy. + +Senator Amidala, the former +Queen of Naboo, is returning +to the Galactic Senate to vote +on the critical issue of creating +an ARMY OF THE REPUBLIC +to assist the overwhelmed +Jedi.... + +Luke Skywalker has returned to +his home planet of Tatooine in +an attempt to rescue his +friend Han Solo from the +clutches of the vile gangster +Jabba the Hutt. + +Little does Luke know that the +GALACTIC EMPIRE has secretly +begun construction on a new +armored space station even +more powerful than the first +dreaded Death Star. + +When completed, this ultimate +weapon will spell certain doom +for the small band of rebels +struggling to restore freedom +to the galaxy... + +War! The Republic is crumbling +under attacks by the ruthless +Sith Lord, Count Dooku. +There are heroes on both sides. +Evil is everywhere. + +In a stunning move, the +fiendish droid leader, General +Grievous, has swept into the +Republic capital and kidnapped +Chancellor Palpatine, leader of +the Galactic Senate. + +As the Separatist Droid Army +attempts to flee the besieged +capital with their valuable +hostage, two Jedi Knights lead a +desperate mission to rescue the +captive Chancellor.... From d3091492bd0828a8ee8f703b9f3b6fb4a07f525c Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Thu, 28 Nov 2024 14:53:10 -0600 Subject: [PATCH 31/56] Grab the line slices within a text file --- applications/shell/src/lib.rs | 49 ++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 2474c52a4c..ac2492ae11 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -56,6 +56,13 @@ enum JobStatus { Stopped } +struct LineSlice { + // The starting index in the String for a line. (inclusive) + start: usize, + // The ending index in the String for a line. (exclusive) + end: usize + } + /// This structure is used by shell to track its spawned applications. Each successfully /// evaluated command line will create a `Job`. Each job contains one or more tasks. /// Tasks are stored in `tasks` in the same sequence as in the command line. @@ -1447,11 +1454,51 @@ impl Shell { self.terminal.lock().clear(); self.clear_cmdline(false)?; - self.terminal.lock().print_to_terminal(content.unwrap_or("".to_string())); + let _ = self.parse_content(content.unwrap_or("".to_string())); self.redisplay_prompt(); Ok(()) } + fn parse_content(&mut self, content: String) -> Result, &'static str> { + // Get the width and height of the terminal screen. + let (width, height) = self.terminal.lock().get_text_dimensions(); + + self.terminal.lock().print_to_terminal(format!("{} {}\n", width, height).to_string()); + + // Record the slice index of each line. + let mut map: BTreeMap = BTreeMap::new(); + // Number of the current line. + let mut cur_line_num: usize = 0; + // Number of characters in the current line. + let mut char_num_in_line: usize = 0; + // Starting index in the String of the current line. + let mut line_start_idx: usize = 0; + // The previous character during the iteration. Set '\0' as the initial value since we don't expect + // to encounter this character in the beginning of the file. + let mut previous_char: char = '\0'; + + // Iterate through the whole file. + // `c` is the current character. `str_idx` is the index of the first byte of the current character. + for (str_idx, c) in content.char_indices() { + // When we need to begin a new line, record the previous line in the map. + if char_num_in_line == width || previous_char == '\n' { + map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); + char_num_in_line = 0; + line_start_idx = str_idx; + cur_line_num += 1; + } + char_num_in_line += 1; + previous_char = c; + } + map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); + + for (line_num, line_slice) in &map { + self.terminal.lock().print_to_terminal(format!("Line {}: start = {}, end = {}\n", line_num, line_slice.start, line_slice.end).to_string()); + } + + Ok(map) + } + fn get_content_string(&self, file_path: String) -> Result { let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task".to_string()); From e9c64beb2c165decdca559a64602e2621794d07b Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Thu, 28 Nov 2024 15:14:32 -0600 Subject: [PATCH 32/56] Grab the sliced string against dimension and display --- applications/shell/src/lib.rs | 46 ++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index ac2492ae11..3ac3cbbcdb 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -640,8 +640,6 @@ impl Shell { // Cycles to the next previous command if keyevent.keycode == Keycode::Up { - //let c = "Hello"; - //self.terminal.lock().print_to_terminal("Hello"); self.goto_previous_command()?; return Ok(()); } @@ -1449,16 +1447,56 @@ impl Shell { } let file_path = args[0]; - self.terminal.lock().print_to_terminal(format!("The second element is: {}\n", file_path).to_string()); let content = self.get_content_string(file_path.to_string()); self.terminal.lock().clear(); self.clear_cmdline(false)?; - let _ = self.parse_content(content.unwrap_or("".to_string())); + let map = self.parse_content(content.clone().unwrap_or("".to_string())); + match map { + Ok(map) => { + let _ = self.display_content_slice(content.clone().unwrap_or("".to_string()), map, 0); + } + Err(e) => { + self.terminal.lock().print_to_terminal(format!("Error parsing content: {}", e).to_string()); + } + } + self.redisplay_prompt(); Ok(()) } + fn display_content_slice(&mut self, content: String, map: BTreeMap, + line_start: usize) + -> Result<(), &'static str> { + // Get exclusive control of the terminal. It is locked through the whole function to + // avoid the overhead of locking it multiple times. + + // Calculate the last line to display. Make sure we don't extend over the end of the file. + let (_width, height) = self.terminal.lock().get_text_dimensions(); + let mut line_end: usize = line_start + (height-20); + + if line_end > map.len() { + line_end = map.len(); + } + + // Refresh the terminal with the lines we've selected. + let start_indices = match map.get(&line_start) { + Some(indices) => indices, + None => return Err("failed to get the byte indices of the first line") + }; + + let end_indices = match map.get(&(line_end - 1)) { + Some(indices) => indices, + None => return Err("failed to get the byte indices of the last line") + }; + + self.terminal.lock().clear(); + self.terminal.lock().print_to_terminal( + content[start_indices.start..end_indices.end].to_string() + ); + self.terminal.lock().refresh_display() + } + fn parse_content(&mut self, content: String) -> Result, &'static str> { // Get the width and height of the terminal screen. let (width, height) = self.terminal.lock().get_text_dimensions(); From 05fd274a506c6aa9e940b2f7f3a80a3673b48f5c Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Thu, 28 Nov 2024 21:52:15 -0600 Subject: [PATCH 33/56] Handle keyboard hits for navigation --- applications/shell/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 3ac3cbbcdb..6a4aadb11f 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1584,6 +1584,41 @@ impl Shell { } } + // /// Handle user keyboard strikes and perform corresponding operations. + fn event_handler_loop(&self, content: String, map: &BTreeMap, + key_event: KeyEvent) -> Result<(), &'static str> { + // Get a reference to this task's terminal. The terminal is *not* locked here. + + // Display the beginning of the file. + let mut line_start: usize = 0; + self.display_content_slice(content, map, 0)?; + + // Handle user keyboard strikes. + loop { + + // Quit the program on "Q". + if key_event.keycode == Keycode::Q { + self.terminal.lock().clear(); + return self.terminal.lock().refresh_display() + } + // Scroll down a line on "Down". + else if key_event.keycode == Keycode::Down { + if line_start + 1 < map.len() { + line_start += 1; + } + self.display_content_slice(content, map, line_start)?; + } + // Scroll up a line on "Up". + else if key_event.keycode == Keycode::Up { + if line_start > 0 { + line_start -= 1; + } + self.display_content_slice(content, map, line_start)?; + } + + } + } + fn execute_internal_clear(&mut self) -> Result<(), &'static str> { self.terminal.lock().clear(); self.clear_cmdline(false)?; From 6d70adf27525f744148b44027624ea6997d420cc Mon Sep 17 00:00:00 2001 From: Nathan Hsiao Date: Thu, 28 Nov 2024 22:50:37 -0600 Subject: [PATCH 34/56] Quit 'less' upon 'Q' keyboard --- applications/shell/src/lib.rs | 48 +++++------------------------------ 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 6a4aadb11f..1ca59c07e2 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -314,7 +314,6 @@ impl Shell { /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. fn set_cmdline(&mut self, s: String, sync_terminal: bool) -> Result<(), &'static str> { let mut s1 = s.clone(); - s1.push_str("hello"); if !self.cmdline.is_empty() { self.clear_cmdline(sync_terminal)?; } @@ -544,10 +543,11 @@ impl Shell { if keyevent.keycode == Keycode::Q { - self.less = false; - self.input_buffer = String::new(); - self.terminal.lock().clear(); - self.redisplay_prompt(); + if self.less == true { + self.less = false; + self.terminal.lock().clear(); + self.redisplay_prompt(); + } return Ok(()); } @@ -1448,6 +1448,7 @@ impl Shell { let file_path = args[0]; let content = self.get_content_string(file_path.to_string()); + self.less = true; self.terminal.lock().clear(); self.clear_cmdline(false)?; @@ -1464,7 +1465,7 @@ impl Shell { self.redisplay_prompt(); Ok(()) } - + fn display_content_slice(&mut self, content: String, map: BTreeMap, line_start: usize) -> Result<(), &'static str> { @@ -1584,41 +1585,6 @@ impl Shell { } } - // /// Handle user keyboard strikes and perform corresponding operations. - fn event_handler_loop(&self, content: String, map: &BTreeMap, - key_event: KeyEvent) -> Result<(), &'static str> { - // Get a reference to this task's terminal. The terminal is *not* locked here. - - // Display the beginning of the file. - let mut line_start: usize = 0; - self.display_content_slice(content, map, 0)?; - - // Handle user keyboard strikes. - loop { - - // Quit the program on "Q". - if key_event.keycode == Keycode::Q { - self.terminal.lock().clear(); - return self.terminal.lock().refresh_display() - } - // Scroll down a line on "Down". - else if key_event.keycode == Keycode::Down { - if line_start + 1 < map.len() { - line_start += 1; - } - self.display_content_slice(content, map, line_start)?; - } - // Scroll up a line on "Up". - else if key_event.keycode == Keycode::Up { - if line_start > 0 { - line_start -= 1; - } - self.display_content_slice(content, map, line_start)?; - } - - } - } - fn execute_internal_clear(&mut self) -> Result<(), &'static str> { self.terminal.lock().clear(); self.clear_cmdline(false)?; From 351cbf84d4eaa83f04f5438c63e9e809ec23fc89 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Tue, 3 Dec 2024 18:17:28 -0500 Subject: [PATCH 35/56] remove 's1' clone for testing --- applications/shell/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 1ca59c07e2..e3f71693f4 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -176,7 +176,11 @@ pub struct Shell { /// the terminal that is bind with the shell instance terminal: Arc>, /// The indicator to show "text editing" mode - less: bool + less: bool, + // String to print + content: String, + // BTree Map to keep track of file new line indices + map: BTreeMap } impl Shell { @@ -313,14 +317,13 @@ impl Shell { /// Set the command line to be a specific string. /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. fn set_cmdline(&mut self, s: String, sync_terminal: bool) -> Result<(), &'static str> { - let mut s1 = s.clone(); if !self.cmdline.is_empty() { self.clear_cmdline(sync_terminal)?; } self.cmdline = s.clone(); self.update_cursor_pos(0)?; if sync_terminal { - self.terminal.lock().print_to_terminal(s1); + self.terminal.lock().print_to_terminal(s); } Ok(()) } From 4c3b55c39a88441eb6231946d879091ee9858ead Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Tue, 3 Dec 2024 18:32:52 -0500 Subject: [PATCH 36/56] Move away from Terminal reference --- applications/shell/src/lib.rs | 45 +---------------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index e3f71693f4..2e20d9049f 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -220,50 +220,7 @@ impl Shell { terminal }) } - - pub fn get_terminal(&self) -> Arc> { - Arc::clone(&self.terminal) - } - - pub fn new_editor(s: String) -> Result { - // Initialize a dfqueue for the terminal object to handle printing from applications. - // Note that this is only to support legacy output. Newly developed applications should - // turn to use `stdio` provided by the `stdio` crate together with the support of `app_io`. - let terminal_print_dfq: DFQueue = DFQueue::new(); - let print_consumer = terminal_print_dfq.into_consumer(); - let print_producer = print_consumer.obtain_producer(); - - let key_event_queue: KeyEventQueue = KeyEventQueue::new(); - let key_event_producer = key_event_queue.get_writer(); - let key_event_consumer = key_event_queue.get_reader(); - - let env = Environment::default(); - - let terminal = Arc::new(Mutex::new(Terminal::text_editor(s.clone())?)); - - let shell = Shell { - jobs: BTreeMap::new(), - task_to_job: BTreeMap::new(), - key_event_consumer: Arc::new(Mutex::new(Some(key_event_consumer))), - key_event_producer, - fg_job_num: None, - cmdline: String::new(), - input_buffer: String::new(), - command_history: Vec::new(), - history_index: 0, - buffered_cmd_recorded: false, - print_consumer, - print_producer, - env: Arc::new(Mutex::new(env)), - less: true, - terminal - }; - - // shell.input_buffer = String::new(); - // shell.key_event_consumer = Arc::new(Mutex::new(None)); - Ok(shell) - } - + /// Insert a character to the command line buffer in the shell. /// The position to insert is determined by the position of the cursor in the terminal. /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. From b137731caeeb916359398ea475b518c4a9292b7f Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Tue, 3 Dec 2024 19:45:14 -0500 Subject: [PATCH 37/56] access map instance for line access --- applications/shell/src/lib.rs | 42 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 2e20d9049f..83fefc692d 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -186,7 +186,7 @@ pub struct Shell { impl Shell { /// Create a new shell. Currently the shell will bind to the default terminal instance provided /// by the `app_io` crate. - pub fn new() -> Result { + fn new() -> Result { // Initialize a dfqueue for the terminal object to handle printing from applications. // Note that this is only to support legacy output. Newly developed applications should // turn to use `stdio` provided by the `stdio` crate together with the support of `app_io`. @@ -216,11 +216,13 @@ impl Shell { print_consumer, print_producer, env: Arc::new(Mutex::new(env)), + terminal, less: false, - terminal + content: String::new(), + map: BTreeMap::new() }) } - + /// Insert a character to the command line buffer in the shell. /// The position to insert is determined by the position of the cursor in the terminal. /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. @@ -1412,22 +1414,14 @@ impl Shell { self.terminal.lock().clear(); self.clear_cmdline(false)?; - let map = self.parse_content(content.clone().unwrap_or("".to_string())); - match map { - Ok(map) => { - let _ = self.display_content_slice(content.clone().unwrap_or("".to_string()), map, 0); - } - Err(e) => { - self.terminal.lock().print_to_terminal(format!("Error parsing content: {}", e).to_string()); - } - } + self.parse_content(content.clone().unwrap_or("".to_string())); + let _ = self.display_content_slice(content.clone().unwrap_or("".to_string()), 0); self.redisplay_prompt(); Ok(()) } - fn display_content_slice(&mut self, content: String, map: BTreeMap, - line_start: usize) + fn display_content_slice(&mut self, content: String, line_start: usize) -> Result<(), &'static str> { // Get exclusive control of the terminal. It is locked through the whole function to // avoid the overhead of locking it multiple times. @@ -1436,17 +1430,17 @@ impl Shell { let (_width, height) = self.terminal.lock().get_text_dimensions(); let mut line_end: usize = line_start + (height-20); - if line_end > map.len() { - line_end = map.len(); + if line_end > self.map.len() { + line_end = self.map.len(); } // Refresh the terminal with the lines we've selected. - let start_indices = match map.get(&line_start) { + let start_indices = match self.map.get(&line_start) { Some(indices) => indices, None => return Err("failed to get the byte indices of the first line") }; - let end_indices = match map.get(&(line_end - 1)) { + let end_indices = match self.map.get(&(line_end - 1)) { Some(indices) => indices, None => return Err("failed to get the byte indices of the last line") }; @@ -1458,14 +1452,12 @@ impl Shell { self.terminal.lock().refresh_display() } - fn parse_content(&mut self, content: String) -> Result, &'static str> { + fn parse_content(&mut self, content: String) { // Get the width and height of the terminal screen. let (width, height) = self.terminal.lock().get_text_dimensions(); self.terminal.lock().print_to_terminal(format!("{} {}\n", width, height).to_string()); - // Record the slice index of each line. - let mut map: BTreeMap = BTreeMap::new(); // Number of the current line. let mut cur_line_num: usize = 0; // Number of characters in the current line. @@ -1481,7 +1473,7 @@ impl Shell { for (str_idx, c) in content.char_indices() { // When we need to begin a new line, record the previous line in the map. if char_num_in_line == width || previous_char == '\n' { - map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); + self.map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); char_num_in_line = 0; line_start_idx = str_idx; cur_line_num += 1; @@ -1489,13 +1481,11 @@ impl Shell { char_num_in_line += 1; previous_char = c; } - map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); + self.map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); - for (line_num, line_slice) in &map { + for (line_num, line_slice) in &self.map { self.terminal.lock().print_to_terminal(format!("Line {}: start = {}, end = {}\n", line_num, line_slice.start, line_slice.end).to_string()); } - - Ok(map) } fn get_content_string(&self, file_path: String) -> Result { From 87a2e1c9a2d2d905e690c10e92236af1560a0f6c Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 00:45:04 -0500 Subject: [PATCH 38/56] Remove previous less command and replace with shell implementation --- applications/less/src/lib.rs | 72 +++++++++++++++--------------------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 41235813ca..ca3ab00dfe 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -245,50 +245,36 @@ fn parse_content(content: &String) -> Result, &'stati pub fn main(args: Vec) -> isize { // // Get stdout. - let stdout = match app_io::stdout() { - Ok(stdout) => stdout, - Err(e) => { - println!("{}", e); - return 1; - } - }; - - // // Set and parse options. - let mut opts = Options::new(); - opts.optflag("h", "help", "print this help menu"); - let matches = match opts.parse(args) { - Ok(m) => m, - Err(e) => { - //Err(format!("Is a directory, cannot 'less' non-files.")) - format!("{}", e); - //print_usage(opts, stdout); - return -1; - } - }; - if matches.opt_present("h") { - //print_usage(opts, stdout); - return 0; - } - if matches.free.is_empty() { - //print_usage(opts, stdout); - return 0; - } - let filename = matches.free[0].clone(); - - let content = get_content_string(filename); + // let stdout = match app_io::stdout() { + // Ok(stdout) => stdout, + // Err(e) => { + // println!("{}", e); + // return 1; + // } + // }; + + // // // Set and parse options. + // let mut opts = Options::new(); + // opts.optflag("h", "help", "print this help menu"); + // let matches = match opts.parse(args) { + // Ok(m) => m, + // Err(e) => { + // //Err(format!("Is a directory, cannot 'less' non-files.")) + // format!("{}", e); + // //print_usage(opts, stdout); + // return -1; + // } + // }; + // if matches.opt_present("h") { + // //print_usage(opts, stdout); + // return 0; + // } + // if matches.free.is_empty() { + // //print_usage(opts, stdout); + // return 0; + // } + // let filename = matches.free[0].clone(); - match content { - Ok(content) => { - let map = parse_content(&content); // Now `content` is a `String`, and `&content` is a `&String` - let shell = Shell::new_editor(content).expect("Failed to create new editor shell"); - shell.start().unwrap(); - }, - Err(e) => { - // Handle the error (e.g.,) - println!("Error: {}", e); - } - } - 0 } From 31496531a5f95daf676aa98d049e2862b4e30897 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 19:18:40 -0500 Subject: [PATCH 39/56] Fix 'Q' option to allow user to enter 'q' letter --- applications/shell/src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 83fefc692d..b2ebad3713 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -504,12 +504,10 @@ impl Shell { } - if keyevent.keycode == Keycode::Q { - if self.less == true { - self.less = false; - self.terminal.lock().clear(); - self.redisplay_prompt(); - } + if keyevent.keycode == Keycode::Q && self.less{ + self.less = false; + self.terminal.lock().clear(); + self.redisplay_prompt(); return Ok(()); } From 39104699e49139b6d2d02eb40479f697900781b1 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 19:40:19 -0500 Subject: [PATCH 40/56] Use shell instance of content string to display --- applications/shell/src/lib.rs | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index b2ebad3713..a82950f6c2 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -56,13 +56,6 @@ enum JobStatus { Stopped } -struct LineSlice { - // The starting index in the String for a line. (inclusive) - start: usize, - // The ending index in the String for a line. (exclusive) - end: usize - } - /// This structure is used by shell to track its spawned applications. Each successfully /// evaluated command line will create a `Job`. Each job contains one or more tasks. /// Tasks are stored in `tasks` in the same sequence as in the command line. @@ -143,6 +136,13 @@ enum AppErr { SpawnErr(String) } +struct LineSlice { + // The starting index in the String for a line. (inclusive) + start: usize, + // The ending index in the String for a line. (exclusive) + end: usize +} + pub struct Shell { /// Variable that stores the task id of any application manually spawned from the terminal jobs: BTreeMap, @@ -1400,26 +1400,25 @@ impl Shell { fn execute_internal_less(&mut self, args: Vec<&str>) -> Result<(), &'static str> { if args.len() < 1 { - self.terminal.lock().print_to_terminal("Not enough arguments provided.\n".to_string()); + self.terminal.lock().print_to_terminal("not enough arguments provided.\n".to_string()); self.clear_cmdline(false)?; self.redisplay_prompt(); - return Ok(()) + Ok(()) } - let file_path = args[0]; - let content = self.get_content_string(file_path.to_string()); self.less = true; - + let file_path = args[0]; + let _ = self.get_content_string(file_path.to_string()); self.terminal.lock().clear(); self.clear_cmdline(false)?; - self.parse_content(content.clone().unwrap_or("".to_string())); - let _ = self.display_content_slice(content.clone().unwrap_or("".to_string()), 0); + self.parse_content(); + let _ = self.display_content_slice(0); self.redisplay_prompt(); Ok(()) } - fn display_content_slice(&mut self, content: String, line_start: usize) + fn display_content_slice(&mut self, line_start: usize) -> Result<(), &'static str> { // Get exclusive control of the terminal. It is locked through the whole function to // avoid the overhead of locking it multiple times. @@ -1445,12 +1444,12 @@ impl Shell { self.terminal.lock().clear(); self.terminal.lock().print_to_terminal( - content[start_indices.start..end_indices.end].to_string() + self.content[start_indices.start..end_indices.end].to_string() ); self.terminal.lock().refresh_display() } - fn parse_content(&mut self, content: String) { + fn parse_content(&mut self) { // Get the width and height of the terminal screen. let (width, height) = self.terminal.lock().get_text_dimensions(); @@ -1468,7 +1467,7 @@ impl Shell { // Iterate through the whole file. // `c` is the current character. `str_idx` is the index of the first byte of the current character. - for (str_idx, c) in content.char_indices() { + for (str_idx, c) in self.content.char_indices() { // When we need to begin a new line, record the previous line in the map. if char_num_in_line == width || previous_char == '\n' { self.map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); @@ -1479,14 +1478,14 @@ impl Shell { char_num_in_line += 1; previous_char = c; } - self.map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); + self.map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: self.content.len() }); for (line_num, line_slice) in &self.map { self.terminal.lock().print_to_terminal(format!("Line {}: start = {}, end = {}\n", line_num, line_slice.start, line_slice.end).to_string()); } } - fn get_content_string(&self, file_path: String) -> Result { + fn get_content_string(&mut self, file_path: String) -> Result { let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task".to_string()); }; @@ -1522,6 +1521,7 @@ impl Shell { } }; //self.terminal.lock().print_to_terminal(read_string.to_string()); + self.content = read_string.to_string(); Ok(read_string.to_string()) } } From 71bd2a777118598e63a2ec5ba487f6b689c3c389 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 23:34:00 -0500 Subject: [PATCH 41/56] Add 'Up' and 'Down' functionality --- applications/shell/src/lib.rs | 40 +++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index a82950f6c2..87c066d518 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -177,10 +177,12 @@ pub struct Shell { terminal: Arc>, /// The indicator to show "text editing" mode less: bool, - // String to print + // string to print content: String, // BTree Map to keep track of file new line indices - map: BTreeMap + map: BTreeMap, + // Line + line_start: usize } impl Shell { @@ -219,7 +221,8 @@ impl Shell { terminal, less: false, content: String::new(), - map: BTreeMap::new() + map: BTreeMap::new(), + line_start: 0 }) } @@ -511,7 +514,24 @@ impl Shell { return Ok(()); } - + // Cycles to the next previous command + if keyevent.keycode == Keycode::Up && self.less { + if self.line_start > 0 { + self.line_start -= 1; + } + let _ = self.display_content_slice(); + return Ok(()); + } + + // Cycles to the next previous command + if keyevent.keycode == Keycode::Down && self.less { + if self.line_start + 1 < self.map.len() { + self.line_start += 1; + } + let _ = self.display_content_slice(); + return Ok(()); + } + // Tracks what the user does whenever she presses the backspace button if keyevent.keycode == Keycode::Backspace { if self.fg_job_num.is_some() { @@ -1403,7 +1423,7 @@ impl Shell { self.terminal.lock().print_to_terminal("not enough arguments provided.\n".to_string()); self.clear_cmdline(false)?; self.redisplay_prompt(); - Ok(()) + return Ok(()) } self.less = true; @@ -1412,27 +1432,25 @@ impl Shell { self.terminal.lock().clear(); self.clear_cmdline(false)?; self.parse_content(); - let _ = self.display_content_slice(0); - + let _ = self.display_content_slice(); self.redisplay_prompt(); Ok(()) } - fn display_content_slice(&mut self, line_start: usize) - -> Result<(), &'static str> { + fn display_content_slice(&mut self) -> Result<(), &'static str> { // Get exclusive control of the terminal. It is locked through the whole function to // avoid the overhead of locking it multiple times. // Calculate the last line to display. Make sure we don't extend over the end of the file. let (_width, height) = self.terminal.lock().get_text_dimensions(); - let mut line_end: usize = line_start + (height-20); + let mut line_end: usize = self.line_start + (height-20); if line_end > self.map.len() { line_end = self.map.len(); } // Refresh the terminal with the lines we've selected. - let start_indices = match self.map.get(&line_start) { + let start_indices = match self.map.get(&self.line_start) { Some(indices) => indices, None => return Err("failed to get the byte indices of the first line") }; From 345b66895aeb05530984ea6d99993d7475bf47cb Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 23:40:10 -0500 Subject: [PATCH 42/56] Restore less functionality back to original --- applications/less/src/lib.rs | 414 ++++++++++++++++------------------- 1 file changed, 188 insertions(+), 226 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index ca3ab00dfe..3f8718ecaf 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -1,301 +1,263 @@ //! A text file reader which allows the user using `Up` and `Down` to scroll the screen. #![no_std] -extern crate alloc; -extern crate task; -extern crate getopts; -extern crate path; -extern crate fs_node; -extern crate keycodes_ascii; -extern crate libterm; -extern crate spin; -extern crate app_io; -extern crate stdio; -extern crate core2; -extern crate shell; -//extern crate terminal_size; -//extern crate text_display; - -//#[macro_use] extern crate log; +// FIXME -use fs_node::{FileOrDir}; -use path::Path; -//use app_io::println; +extern crate alloc; +// extern crate task; +// extern crate getopts; +// extern crate path; +// extern crate fs_node; +// extern crate keycodes_ascii; +// extern crate libterm; +// extern crate spin; +// extern crate app_io; +// extern crate stdio; +// extern crate core2; +// #[macro_use] extern crate log; // use keycodes_ascii::{Keycode, KeyAction}; -use core::str; +// use core::str; use alloc::{ - string::String, vec::Vec, + string::String, }; -use alloc::vec; -use alloc::format; -use alloc::string::ToString; -use app_io::println; -use getopts::Options; -//use terminal_size::{terminal_size, Width, Height}; // use getopts::Options; // use path::Path; // use fs_node::FileOrDir; -use alloc::collections::BTreeMap; -use libterm::Terminal; -//use window::Window; -//use text_display::TextDisplay; -//use libterm::cursor::Cursor; -use alloc::sync::Arc; -use spin::Mutex; -use stdio::KeyEventQueueReader; -use keycodes_ascii::Keycode; -use keycodes_ascii::KeyAction; -use shell::Shell; +// use alloc::collections::BTreeMap; +// use libterm::Terminal; +// use spin::Mutex; // use stdio::{StdioWriter, KeyEventQueueReader}; // use core2::io::Write; - -// The metadata for each line in the file. -struct LineSlice { - // The starting index in the String for a line. (inclusive) - start: usize, - // The ending index in the String for a line. (exclusive) - end: usize -} - -//fn get_terminal_dimensions() -> Option<(usize, usize)> { -// match terminal_size() { -// Some((Width(width), Height(height))) => Some((width as usize, height as usize)), -// None => None, // If terminal size could not be determined -// } -//} +// /// The metadata for each line in the file. +// struct LineSlice { +// /// The starting index in the String for a line. (inclusive) +// start: usize, +// /// The ending index in the String for a line. (exclusive) +// end: usize +// } // /// Read the whole file to a String. -fn get_content_string(file_path: String) -> Result { - let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { - return Err("failed to get current task".to_string()); - }; - let path = Path::new(file_path.as_str()); +// fn get_content_string(file_path: String) -> Result { +// let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { +// return Err("failed to get current task".to_string()); +// }; +// let path = Path::new(file_path); - // navigate to the filepath specified by first argument - match path.get(&curr_wd) { - - Some(file_dir_enum) => { - match file_dir_enum { - FileOrDir::Dir(directory) => { - Err(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())) - } - FileOrDir::File(file) => { - let mut file_locked = file.lock(); - let file_size = file_locked.len(); - let mut string_slice_as_bytes = vec![0; file_size]; - let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { - Ok(num) => num, - Err(e) => { - println!("Failed to read error "); - return Err(format!("Failed to file size: {:?}", e)); - } - }; - let read_string = match str::from_utf8(&string_slice_as_bytes) { - Ok(string_slice) => string_slice, - Err(utf8_err) => { - println!("File was not a printable UTF-8 text file"); - return Err(format!("Failed to read file: {:?}", utf8_err)); - } - }; - //println!("{}", read_string); - Ok(read_string.to_string()) - } - } - }, - None => { - println!("Warning: path is not found"); - // Optionally, return a default or handle it another way. - Ok("".to_string()) // Example: return empty string or a default value - } - } -} +// // navigate to the filepath specified by first argument +// match path.get(&curr_wd) { +// Some(file_dir_enum) => { +// match file_dir_enum { +// FileOrDir::Dir(directory) => { +// Err(format!("{:?} is a directory, cannot 'less' non-files.", directory.lock().get_name())) +// } +// FileOrDir::File(file) => { +// let mut file_locked = file.lock(); +// let file_size = file_locked.len(); +// let mut string_slice_as_bytes = vec![0; file_size]; +// let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { +// Ok(num) => num, +// Err(e) => { +// return Err(format!("Failed to read {:?}, error {:?}", +// file_locked.get_name(), e)) +// } +// }; +// let read_string = match str::from_utf8(&string_slice_as_bytes) { +// Ok(string_slice) => string_slice, +// Err(utf8_err) => { +// return Err(format!("File {:?} was not a printable UTF-8 text file: {}", +// file_locked.get_name(), utf8_err)) +// } +// }; +// Ok(read_string.to_string()) +// } +// } +// }, +// _ => { +// Err(format!("Couldn't find file at path {}", path)) +// } +// } +// } // /// This function parses the text file. It scans through the whole file and records the string slice // /// for each line. This function has full UTF-8 support, which means that the case where a single character // /// occupies multiple bytes are well considered. The slice index returned by this function is guaranteed // /// not to cause panic. -fn parse_content(content: &String) -> Result, &'static str> { - // Get the width and height of the terminal screen. - //let (width, _height) = get_terminal_dimensions() - // .ok_or("couldn't get terminal dimensions")?;e - - // let (width, _height) = terminal.lock().get_text_dimensions(); - //let mut t = app_io::get_my_terminal(). - // get(&task::get_my_current_task_id()) - // .map(|property| property.terminal.clone()); +// fn parse_content(content: &String) -> Result, &'static str> { +// // Get the width and height of the terminal screen. +// let (width, _height) = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")? +// .lock() +// .get_text_dimensions(); - let mut terminal = Terminal::new().expect("Failed to create terminal"); - let (width, height) = terminal.get_text_dimensions(); - println!("{} {}", width, height); +// // Record the slice index of each line. +// let mut map: BTreeMap = BTreeMap::new(); +// // Number of the current line. +// let mut cur_line_num: usize = 0; +// // Number of characters in the current line. +// let mut char_num_in_line: usize = 0; +// // Starting index in the String of the current line. +// let mut line_start_idx: usize = 0; +// // The previous character during the iteration. Set '\0' as the initial value since we don't expect +// // to encounter this character in the beginning of the file. +// let mut previous_char: char = '\0'; - // println!("{} {}", width, _height); - // Record the slice index of each line. - let mut map: BTreeMap = BTreeMap::new(); - // Number of the current line. - let mut cur_line_num: usize = 0; - // Number of characters in the current line. - let mut char_num_in_line: usize = 0; - // Starting index in the String of the current line. - let mut line_start_idx: usize = 0; - // The previous character during the iteration. Set '\0' as the initial value since we don't expect - // to encounter this character in the beginning of the file. - let mut previous_char: char = '\0'; +// // Iterate through the whole file. +// // `c` is the current character. `str_idx` is the index of the first byte of the current character. +// for (str_idx, c) in content.char_indices() { +// // When we need to begin a new line, record the previous line in the map. +// if char_num_in_line == width || previous_char == '\n' { +// map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); +// char_num_in_line = 0; +// line_start_idx = str_idx; +// cur_line_num += 1; +// } +// char_num_in_line += 1; +// previous_char = c; +// } +// map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); - // Iterate through the whole file. - // `c` is the current character. `str_idx` is the index of the first byte of the current character. - for (str_idx, c) in content.char_indices() { - // When we need to begin a new line, record the previous line in the map. - if char_num_in_line == width || previous_char == '\n' { - map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); - char_num_in_line = 0; - line_start_idx = str_idx; - cur_line_num += 1; - } - char_num_in_line += 1; - previous_char = c; - } - map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); - - Ok(map) -} +// Ok(map) +// } // /// Display part of the file (may be whole file if the file is short) to the terminal, starting // /// at line number `line_start`. -// fn display_content(content: &str, map: &BTreeMap, -// line_start: usize, terminal: &Terminal) -// -> Result<(), &'static str> { -// // Get exclusive control of the terminal. It is locked through the whole function to -// // avoid the overhead of locking it multiple times. -// let mut locked_terminal = Terminal::new().expect("Failed to create terminal"); -// // let mut locked_terminal = terminal.lock(); +// fn display_content(content: &String, map: &BTreeMap, +// line_start: usize, terminal: &Arc>) +// -> Result<(), &'static str> { +// // Get exclusive control of the terminal. It is locked through the whole function to +// // avoid the overhead of locking it multiple times. +// let mut locked_terminal = terminal.lock(); -// // Calculate the last line to display. Make sure we don't extend over the end of the file. -// let (_width, height) = locked_terminal.get_text_dimensions(); -// let mut line_end: usize = line_start + height; -// if line_end > map.len() { -// line_end = map.len(); -// } +// // Calculate the last line to display. Make sure we don't extend over the end of the file. +// let (_width, height) = locked_terminal.get_text_dimensions(); +// let mut line_end: usize = line_start + height; +// if line_end > map.len() { +// line_end = map.len(); +// } -// // Refresh the terminal with the lines we've selected. -// let start_indices = match map.get(&line_start) { -// Some(indices) => indices, -// None => return Err("failed to get the byte indices of the first line") -// }; -// let end_indices = match map.get(&(line_end - 1)) { -// Some(indices) => indices, -// None => return Err("failed to get the byte indices of the last line") -// }; -// locked_terminal.clear(); -// locked_terminal.print_to_terminal( -// content[start_indices.start..end_indices.end].to_string() -// ); -// locked_terminal.refresh_display() +// // Refresh the terminal with the lines we've selected. +// let start_indices = match map.get(&line_start) { +// Some(indices) => indices, +// None => return Err("failed to get the byte indices of the first line") +// }; +// let end_indices = match map.get(&(line_end - 1)) { +// Some(indices) => indices, +// None => return Err("failed to get the byte indices of the last line") +// }; +// locked_terminal.clear(); +// locked_terminal.print_to_terminal( +// content[start_indices.start..end_indices.end].to_string() +// ); +// locked_terminal.refresh_display() // } // /// Handle user keyboard strikes and perform corresponding operations. // fn event_handler_loop(content: &String, map: &BTreeMap, -// key_event_queue: &KeyEventQueueReader) -// -> Result<(), &'static str> { -// // Get a reference to this task's terminal. The terminal is *not* locked here. -// //let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; -// let mut terminal = Terminal::new().expect("Failed to create terminal"); +// key_event_queue: &KeyEventQueueReader) +// -> Result<(), &'static str> { -// // Display the beginning of the file. -// let mut line_start: usize = 0; -// display_content(content, map, 0, &terminal)?; +// // Get a reference to this task's terminal. The terminal is *not* locked here. +// let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; -// // Handle user keyboard strikes. -// loop { -// match key_event_queue.read_one() { -// Some(keyevent) => { -// if keyevent.action != KeyAction::Pressed { continue; } -// match keyevent.keycode { -// // Quit the program on "Q". -// Keycode::Q => { -// //let mut locked_terminal = terminal.lock(); -// //locked_terminal.clear(); -// return terminal.refresh_display() -// }, -// // Scroll down a line on "Down". -// Keycode::Down => { -// if line_start + 1 < map.len() { -// line_start += 1; -// } -// display_content(content, map, line_start, &terminal)?; -// }, -// // Scroll up a line on "Up". -// Keycode::Up => { -// if line_start > 0 { -// line_start -= 1; -// } -// display_content(content, map, line_start, &terminal)?; -// } -// _ => {} -// } -// }, -// _ => {} -// } -// } +// // Display the beginning of the file. +// let mut line_start: usize = 0; +// display_content(content, map, 0, &terminal)?; + +// // Handle user keyboard strikes. +// loop { +// match key_event_queue.read_one() { +// Some(keyevent) => { +// if keyevent.action != KeyAction::Pressed { continue; } +// match keyevent.keycode { +// // Quit the program on "Q". +// Keycode::Q => { +// let mut locked_terminal = terminal.lock(); +// locked_terminal.clear(); +// return locked_terminal.refresh_display() +// }, +// // Scroll down a line on "Down". +// Keycode::Down => { +// if line_start + 1 < map.len() { +// line_start += 1; +// } +// display_content(content, map, line_start, &terminal)?; +// }, +// // Scroll up a line on "Up". +// Keycode::Up => { +// if line_start > 0 { +// line_start -= 1; +// } +// display_content(content, map, line_start, &terminal)?; +// } +// _ => {} +// } +// }, +// _ => {} +// } +// } // } -pub fn main(args: Vec) -> isize { +pub fn main(_args: Vec) -> isize { // // Get stdout. // let stdout = match app_io::stdout() { - // Ok(stdout) => stdout, - // Err(e) => { - // println!("{}", e); - // return 1; - // } + // Ok(stdout) => stdout, + // Err(e) => { + // error!("{}", e); + // return 1; + // } // }; - // // // Set and parse options. + // // Set and parse options. // let mut opts = Options::new(); // opts.optflag("h", "help", "print this help menu"); // let matches = match opts.parse(args) { // Ok(m) => m, // Err(e) => { - // //Err(format!("Is a directory, cannot 'less' non-files.")) - // format!("{}", e); - // //print_usage(opts, stdout); + // error!("{}", e); + // print_usage(opts, stdout); // return -1; // } // }; // if matches.opt_present("h") { - // //print_usage(opts, stdout); + // print_usage(opts, stdout); // return 0; // } // if matches.free.is_empty() { - // //print_usage(opts, stdout); + // print_usage(opts, stdout); // return 0; // } // let filename = matches.free[0].clone(); - + + // if let Err(e) = run(filename) { + // error!("{}", e); + // return 1; + // } 0 } -//fn run(filename: String) -> Result<(), String> { +// fn run(filename: String) -> Result<(), String> { - // Acquire key event queue. +// // Acquire key event queue. // let key_event_queue = app_io::take_key_event_queue()?; // let key_event_queue = (*key_event_queue).as_ref() // .ok_or("failed to take key event reader")?; - // Read the whole file to a String. +// // Read the whole file to a String. // let content = get_content_string(filename)?; // // Get it run. // let map = parse_content(&content)?; + // Ok(event_handler_loop(&content, &map, key_event_queue)?) -//} +// } -//fn print_usage(opts: Options, stdout: StdioWriter) { -// let _ = stdout.lock().write_all(format!("{}\n", opts.usage(USAGE)).as_bytes()); -//} +// fn print_usage(opts: Options, stdout: StdioWriter) { +// let _ = stdout.lock().write_all(format!("{}\n", opts.usage(USAGE)).as_bytes()); +// } -//const USAGE: &'static str = "Usage: less file -//read files"; +// const USAGE: &'static str = "Usage: less file +// read files"; \ No newline at end of file From ad24748fad2f164b41476340dad01957af3a7eb2 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 23:46:25 -0500 Subject: [PATCH 43/56] Revert less application to original again --- applications/less/src/lib.rs | 262 +++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 3f8718ecaf..c993af1f09 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -200,6 +200,268 @@ use alloc::{ // } +pub fn main(_args: Vec) -> isize { + + // // Get stdout. + // let stdout = match app_io::stdout() { + // Ok(stdout) => stdout, + // Err(e) => { + // error!("{}", e); + // return 1; + // } + // }; + + // // Set and parse options. + // let mut opts = Options::new(); + // opts.optflag("h", "help", "print this help menu"); + // let matches = match opts.parse(args) { + // Ok(m) => m, + // Err(e) => { + // error!("{}", e); + // print_usage(opts, stdout); + // return -1; + // } + // }; + // if matches.opt_present("h") { + // print_usage(opts, stdout); + // return 0; + // } + // if matches.free.is_empty() { + // print_usage(opts, stdout); + // return 0; + // } + // let filename = matches.free[0].clone(); + + // if let Err(e) = run(filename) { + // error!("{}", e); + // return 1; + // } + 0 +} + +// fn run(filename: String) -> Result<(), String> { + +// // Acquire key event queue. +// let key_event_queue = app_io::take_key_event_queue()?; +// let key_event_queue = (*key_event_queue).as_ref() +// .ok_or("failed to take key event reader")?; + +// // Read the whole file to a String. +// let content = get_content_string(filename)?; + +// // Get it run. +// let map = parse_content(&content)?; + +// Ok(event_handler_loop(&content, &map, key_event_queue)?) +// } + +// fn print_usage(opts: Options, stdout: StdioWriter) { +// let _ = stdout.lock().write_all(format!("{}\n", opts.usage(USAGE)).as_bytes()); +// } + +// const USAGE: &'static str = "Usage: less file +// read files";//! A text file reader which allows the user using `Up` and `Down` to scroll the screen. +#![no_std] + +// FIXME + +extern crate alloc; +// extern crate task; +// extern crate getopts; +// extern crate path; +// extern crate fs_node; +// extern crate keycodes_ascii; +// extern crate libterm; +// extern crate spin; +// extern crate app_io; +// extern crate stdio; +// extern crate core2; +// #[macro_use] extern crate log; + +// use keycodes_ascii::{Keycode, KeyAction}; +// use core::str; +use alloc::{ + vec::Vec, + string::String, +}; +// use getopts::Options; +// use path::Path; +// use fs_node::FileOrDir; +// use alloc::collections::BTreeMap; +// use libterm::Terminal; +// use spin::Mutex; +// use stdio::{StdioWriter, KeyEventQueueReader}; +// use core2::io::Write; + +// /// The metadata for each line in the file. +// struct LineSlice { +// /// The starting index in the String for a line. (inclusive) +// start: usize, +// /// The ending index in the String for a line. (exclusive) +// end: usize +// } + +// /// Read the whole file to a String. +// fn get_content_string(file_path: String) -> Result { +// let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { +// return Err("failed to get current task".to_string()); +// }; +// let path = Path::new(file_path); + +// // navigate to the filepath specified by first argument +// match path.get(&curr_wd) { +// Some(file_dir_enum) => { +// match file_dir_enum { +// FileOrDir::Dir(directory) => { +// Err(format!("{:?} is a directory, cannot 'less' non-files.", directory.lock().get_name())) +// } +// FileOrDir::File(file) => { +// let mut file_locked = file.lock(); +// let file_size = file_locked.len(); +// let mut string_slice_as_bytes = vec![0; file_size]; +// let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { +// Ok(num) => num, +// Err(e) => { +// return Err(format!("Failed to read {:?}, error {:?}", +// file_locked.get_name(), e)) +// } +// }; +// let read_string = match str::from_utf8(&string_slice_as_bytes) { +// Ok(string_slice) => string_slice, +// Err(utf8_err) => { +// return Err(format!("File {:?} was not a printable UTF-8 text file: {}", +// file_locked.get_name(), utf8_err)) +// } +// }; +// Ok(read_string.to_string()) +// } +// } +// }, +// _ => { +// Err(format!("Couldn't find file at path {}", path)) +// } +// } +// } + +// /// This function parses the text file. It scans through the whole file and records the string slice +// /// for each line. This function has full UTF-8 support, which means that the case where a single character +// /// occupies multiple bytes are well considered. The slice index returned by this function is guaranteed +// /// not to cause panic. +// fn parse_content(content: &String) -> Result, &'static str> { +// // Get the width and height of the terminal screen. +// let (width, _height) = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")? +// .lock() +// .get_text_dimensions(); + +// // Record the slice index of each line. +// let mut map: BTreeMap = BTreeMap::new(); +// // Number of the current line. +// let mut cur_line_num: usize = 0; +// // Number of characters in the current line. +// let mut char_num_in_line: usize = 0; +// // Starting index in the String of the current line. +// let mut line_start_idx: usize = 0; +// // The previous character during the iteration. Set '\0' as the initial value since we don't expect +// // to encounter this character in the beginning of the file. +// let mut previous_char: char = '\0'; + +// // Iterate through the whole file. +// // `c` is the current character. `str_idx` is the index of the first byte of the current character. +// for (str_idx, c) in content.char_indices() { +// // When we need to begin a new line, record the previous line in the map. +// if char_num_in_line == width || previous_char == '\n' { +// map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); +// char_num_in_line = 0; +// line_start_idx = str_idx; +// cur_line_num += 1; +// } +// char_num_in_line += 1; +// previous_char = c; +// } +// map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); + +// Ok(map) +// } + +// /// Display part of the file (may be whole file if the file is short) to the terminal, starting +// /// at line number `line_start`. +// fn display_content(content: &String, map: &BTreeMap, +// line_start: usize, terminal: &Arc>) +// -> Result<(), &'static str> { +// // Get exclusive control of the terminal. It is locked through the whole function to +// // avoid the overhead of locking it multiple times. +// let mut locked_terminal = terminal.lock(); + +// // Calculate the last line to display. Make sure we don't extend over the end of the file. +// let (_width, height) = locked_terminal.get_text_dimensions(); +// let mut line_end: usize = line_start + height; +// if line_end > map.len() { +// line_end = map.len(); +// } + +// // Refresh the terminal with the lines we've selected. +// let start_indices = match map.get(&line_start) { +// Some(indices) => indices, +// None => return Err("failed to get the byte indices of the first line") +// }; +// let end_indices = match map.get(&(line_end - 1)) { +// Some(indices) => indices, +// None => return Err("failed to get the byte indices of the last line") +// }; +// locked_terminal.clear(); +// locked_terminal.print_to_terminal( +// content[start_indices.start..end_indices.end].to_string() +// ); +// locked_terminal.refresh_display() +// } + +// /// Handle user keyboard strikes and perform corresponding operations. +// fn event_handler_loop(content: &String, map: &BTreeMap, +// key_event_queue: &KeyEventQueueReader) +// -> Result<(), &'static str> { + +// // Get a reference to this task's terminal. The terminal is *not* locked here. +// let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; + +// // Display the beginning of the file. +// let mut line_start: usize = 0; +// display_content(content, map, 0, &terminal)?; + +// // Handle user keyboard strikes. +// loop { +// match key_event_queue.read_one() { +// Some(keyevent) => { +// if keyevent.action != KeyAction::Pressed { continue; } +// match keyevent.keycode { +// // Quit the program on "Q". +// Keycode::Q => { +// let mut locked_terminal = terminal.lock(); +// locked_terminal.clear(); +// return locked_terminal.refresh_display() +// }, +// // Scroll down a line on "Down". +// Keycode::Down => { +// if line_start + 1 < map.len() { +// line_start += 1; +// } +// display_content(content, map, line_start, &terminal)?; +// }, +// // Scroll up a line on "Up". +// Keycode::Up => { +// if line_start > 0 { +// line_start -= 1; +// } +// display_content(content, map, line_start, &terminal)?; +// } +// _ => {} +// } +// }, +// _ => {} +// } +// } +// } + + pub fn main(_args: Vec) -> isize { // // Get stdout. From 190c3a44f766bda3ac5649dbb665b1941ebac02e Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 23:47:58 -0500 Subject: [PATCH 44/56] again --- applications/less/src/lib.rs | 262 ----------------------------------- 1 file changed, 262 deletions(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index c993af1f09..3f8718ecaf 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -200,268 +200,6 @@ use alloc::{ // } -pub fn main(_args: Vec) -> isize { - - // // Get stdout. - // let stdout = match app_io::stdout() { - // Ok(stdout) => stdout, - // Err(e) => { - // error!("{}", e); - // return 1; - // } - // }; - - // // Set and parse options. - // let mut opts = Options::new(); - // opts.optflag("h", "help", "print this help menu"); - // let matches = match opts.parse(args) { - // Ok(m) => m, - // Err(e) => { - // error!("{}", e); - // print_usage(opts, stdout); - // return -1; - // } - // }; - // if matches.opt_present("h") { - // print_usage(opts, stdout); - // return 0; - // } - // if matches.free.is_empty() { - // print_usage(opts, stdout); - // return 0; - // } - // let filename = matches.free[0].clone(); - - // if let Err(e) = run(filename) { - // error!("{}", e); - // return 1; - // } - 0 -} - -// fn run(filename: String) -> Result<(), String> { - -// // Acquire key event queue. -// let key_event_queue = app_io::take_key_event_queue()?; -// let key_event_queue = (*key_event_queue).as_ref() -// .ok_or("failed to take key event reader")?; - -// // Read the whole file to a String. -// let content = get_content_string(filename)?; - -// // Get it run. -// let map = parse_content(&content)?; - -// Ok(event_handler_loop(&content, &map, key_event_queue)?) -// } - -// fn print_usage(opts: Options, stdout: StdioWriter) { -// let _ = stdout.lock().write_all(format!("{}\n", opts.usage(USAGE)).as_bytes()); -// } - -// const USAGE: &'static str = "Usage: less file -// read files";//! A text file reader which allows the user using `Up` and `Down` to scroll the screen. -#![no_std] - -// FIXME - -extern crate alloc; -// extern crate task; -// extern crate getopts; -// extern crate path; -// extern crate fs_node; -// extern crate keycodes_ascii; -// extern crate libterm; -// extern crate spin; -// extern crate app_io; -// extern crate stdio; -// extern crate core2; -// #[macro_use] extern crate log; - -// use keycodes_ascii::{Keycode, KeyAction}; -// use core::str; -use alloc::{ - vec::Vec, - string::String, -}; -// use getopts::Options; -// use path::Path; -// use fs_node::FileOrDir; -// use alloc::collections::BTreeMap; -// use libterm::Terminal; -// use spin::Mutex; -// use stdio::{StdioWriter, KeyEventQueueReader}; -// use core2::io::Write; - -// /// The metadata for each line in the file. -// struct LineSlice { -// /// The starting index in the String for a line. (inclusive) -// start: usize, -// /// The ending index in the String for a line. (exclusive) -// end: usize -// } - -// /// Read the whole file to a String. -// fn get_content_string(file_path: String) -> Result { -// let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { -// return Err("failed to get current task".to_string()); -// }; -// let path = Path::new(file_path); - -// // navigate to the filepath specified by first argument -// match path.get(&curr_wd) { -// Some(file_dir_enum) => { -// match file_dir_enum { -// FileOrDir::Dir(directory) => { -// Err(format!("{:?} is a directory, cannot 'less' non-files.", directory.lock().get_name())) -// } -// FileOrDir::File(file) => { -// let mut file_locked = file.lock(); -// let file_size = file_locked.len(); -// let mut string_slice_as_bytes = vec![0; file_size]; -// let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { -// Ok(num) => num, -// Err(e) => { -// return Err(format!("Failed to read {:?}, error {:?}", -// file_locked.get_name(), e)) -// } -// }; -// let read_string = match str::from_utf8(&string_slice_as_bytes) { -// Ok(string_slice) => string_slice, -// Err(utf8_err) => { -// return Err(format!("File {:?} was not a printable UTF-8 text file: {}", -// file_locked.get_name(), utf8_err)) -// } -// }; -// Ok(read_string.to_string()) -// } -// } -// }, -// _ => { -// Err(format!("Couldn't find file at path {}", path)) -// } -// } -// } - -// /// This function parses the text file. It scans through the whole file and records the string slice -// /// for each line. This function has full UTF-8 support, which means that the case where a single character -// /// occupies multiple bytes are well considered. The slice index returned by this function is guaranteed -// /// not to cause panic. -// fn parse_content(content: &String) -> Result, &'static str> { -// // Get the width and height of the terminal screen. -// let (width, _height) = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")? -// .lock() -// .get_text_dimensions(); - -// // Record the slice index of each line. -// let mut map: BTreeMap = BTreeMap::new(); -// // Number of the current line. -// let mut cur_line_num: usize = 0; -// // Number of characters in the current line. -// let mut char_num_in_line: usize = 0; -// // Starting index in the String of the current line. -// let mut line_start_idx: usize = 0; -// // The previous character during the iteration. Set '\0' as the initial value since we don't expect -// // to encounter this character in the beginning of the file. -// let mut previous_char: char = '\0'; - -// // Iterate through the whole file. -// // `c` is the current character. `str_idx` is the index of the first byte of the current character. -// for (str_idx, c) in content.char_indices() { -// // When we need to begin a new line, record the previous line in the map. -// if char_num_in_line == width || previous_char == '\n' { -// map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); -// char_num_in_line = 0; -// line_start_idx = str_idx; -// cur_line_num += 1; -// } -// char_num_in_line += 1; -// previous_char = c; -// } -// map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: content.len() }); - -// Ok(map) -// } - -// /// Display part of the file (may be whole file if the file is short) to the terminal, starting -// /// at line number `line_start`. -// fn display_content(content: &String, map: &BTreeMap, -// line_start: usize, terminal: &Arc>) -// -> Result<(), &'static str> { -// // Get exclusive control of the terminal. It is locked through the whole function to -// // avoid the overhead of locking it multiple times. -// let mut locked_terminal = terminal.lock(); - -// // Calculate the last line to display. Make sure we don't extend over the end of the file. -// let (_width, height) = locked_terminal.get_text_dimensions(); -// let mut line_end: usize = line_start + height; -// if line_end > map.len() { -// line_end = map.len(); -// } - -// // Refresh the terminal with the lines we've selected. -// let start_indices = match map.get(&line_start) { -// Some(indices) => indices, -// None => return Err("failed to get the byte indices of the first line") -// }; -// let end_indices = match map.get(&(line_end - 1)) { -// Some(indices) => indices, -// None => return Err("failed to get the byte indices of the last line") -// }; -// locked_terminal.clear(); -// locked_terminal.print_to_terminal( -// content[start_indices.start..end_indices.end].to_string() -// ); -// locked_terminal.refresh_display() -// } - -// /// Handle user keyboard strikes and perform corresponding operations. -// fn event_handler_loop(content: &String, map: &BTreeMap, -// key_event_queue: &KeyEventQueueReader) -// -> Result<(), &'static str> { - -// // Get a reference to this task's terminal. The terminal is *not* locked here. -// let terminal = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")?; - -// // Display the beginning of the file. -// let mut line_start: usize = 0; -// display_content(content, map, 0, &terminal)?; - -// // Handle user keyboard strikes. -// loop { -// match key_event_queue.read_one() { -// Some(keyevent) => { -// if keyevent.action != KeyAction::Pressed { continue; } -// match keyevent.keycode { -// // Quit the program on "Q". -// Keycode::Q => { -// let mut locked_terminal = terminal.lock(); -// locked_terminal.clear(); -// return locked_terminal.refresh_display() -// }, -// // Scroll down a line on "Down". -// Keycode::Down => { -// if line_start + 1 < map.len() { -// line_start += 1; -// } -// display_content(content, map, line_start, &terminal)?; -// }, -// // Scroll up a line on "Up". -// Keycode::Up => { -// if line_start > 0 { -// line_start -= 1; -// } -// display_content(content, map, line_start, &terminal)?; -// } -// _ => {} -// } -// }, -// _ => {} -// } -// } -// } - - pub fn main(_args: Vec) -> isize { // // Get stdout. From 06a2788e27f6df38b35868b27db206d534651f2c Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 23:49:07 -0500 Subject: [PATCH 45/56] revert Cargo changes --- applications/less/Cargo.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/applications/less/Cargo.toml b/applications/less/Cargo.toml index 11c493f060..67296dcc41 100644 --- a/applications/less/Cargo.toml +++ b/applications/less/Cargo.toml @@ -29,13 +29,8 @@ path = "../../kernel/app_io" [dependencies.stdio] path = "../../libs/stdio" -[dependencies.shell] -path = "../shell" - - [dependencies.log] version = "0.4.8" - [lib] crate-type = ["rlib"] From 2c34fffd1213fac45534538b313a7b63df4c1cb8 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 23:52:45 -0500 Subject: [PATCH 46/56] fix line issue --- applications/less/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/less/Cargo.toml b/applications/less/Cargo.toml index 67296dcc41..77806ecf34 100644 --- a/applications/less/Cargo.toml +++ b/applications/less/Cargo.toml @@ -32,5 +32,6 @@ path = "../../libs/stdio" [dependencies.log] version = "0.4.8" + [lib] crate-type = ["rlib"] From 075ab3e1673341572b7bb63b20140e9e6c5352a0 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 23:54:58 -0500 Subject: [PATCH 47/56] Fix unnnecessary changes --- kernel/app_io/Cargo.toml | 3 --- kernel/app_io/src/lib.rs | 6 ------ kernel/libterm/src/lib.rs | 34 ---------------------------------- 3 files changed, 43 deletions(-) diff --git a/kernel/app_io/Cargo.toml b/kernel/app_io/Cargo.toml index f60af2cd53..807702c826 100644 --- a/kernel/app_io/Cargo.toml +++ b/kernel/app_io/Cargo.toml @@ -17,9 +17,6 @@ path = "../../kernel/task" [dependencies.stdio] path = "../../libs/stdio" -#[dependencies.libterm] -#path = "../../kernel/libterm" - [dependencies.sync_block] path = "../../kernel/sync_block" diff --git a/kernel/app_io/src/lib.rs b/kernel/app_io/src/lib.rs index 0c030ed1e2..440d3563f2 100644 --- a/kernel/app_io/src/lib.rs +++ b/kernel/app_io/src/lib.rs @@ -18,8 +18,6 @@ use alloc::{format, sync::Arc}; use core2::io::{self, Error, ErrorKind, Read, Write}; use stdio::{StdioReader, StdioWriter}; use tty::{LineDiscipline, Slave}; -use sync_block::MutexGuard; -use hashbrown::HashMap; pub trait ImmutableRead: Send + Sync + 'static { fn read(&self, buf: &mut [u8]) -> io::Result; @@ -106,10 +104,6 @@ mod shared_maps { } } -pub fn get_my_terminal() -> MutexGuard<'static, HashMap> { - shared_maps::lock_stream_map() -} - /// Shells call this function to store queue stdio streams for applications. If /// there are any existing readers/writers for the task (which should not /// happen in normal practice), it returns the old one, otherwise returns None. diff --git a/kernel/libterm/src/lib.rs b/kernel/libterm/src/lib.rs index 478bce8811..bea9ab297c 100644 --- a/kernel/libterm/src/lib.rs +++ b/kernel/libterm/src/lib.rs @@ -449,40 +449,6 @@ impl Terminal { Ok(terminal) } - - pub fn text_editor(s: String) -> Result { - let wm_ref = window_manager::WINDOW_MANAGER.get().ok_or("The window manager is not initialized")?; - let (window_width, window_height) = { - let wm = wm_ref.lock(); - wm.get_screen_size() - }; - - let window = window::Window::new( - Coord::new(0, 0), - window_width, - window_height, - FONT_BACKGROUND_COLOR, - )?; - - let area = window.area(); - let text_display = TextDisplay::new(area.width(), area.height(), FONT_FOREGROUND_COLOR, FONT_BACKGROUND_COLOR)?; - - let mut terminal = Terminal { - window, - scrollback_buffer: String::new(), - scroll_start_idx: 0, - is_scroll_end: true, - text_display, - cursor: Cursor::default(), - }; - terminal.display_text()?; - - terminal.print_to_terminal(s.clone()); - Ok(terminal) - } - - - /// Adds a string to be printed to the terminal to the terminal scrollback buffer. /// Note that one needs to call `refresh_display` to get things actually printed. pub fn print_to_terminal(&mut self, s: String) { From 52d62c8443ad4eef8404ae84872204de1d20d0ae Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Wed, 4 Dec 2024 23:59:54 -0500 Subject: [PATCH 48/56] fix again --- Cargo.lock | 1 - applications/shell/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b8c50b07b..5bc9205de2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1776,7 +1776,6 @@ dependencies = [ "libterm", "log", "path", - "shell", "spin 0.9.4", "stdio", "task", diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 87c066d518..0e4578e5b3 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1286,7 +1286,7 @@ impl Shell { /// The print queue is handled first inside the loop iteration, which means that all print events in the print /// queue will always be printed to the text display before input events or any other managerial functions are handled. /// This allows for clean appending to the scrollback buffer and prevents interleaving of text. - pub fn start(mut self) -> Result<(), &'static str> { + fn start(mut self) -> Result<(), &'static str> { let mut need_refresh = false; let mut need_prompt = false; self.redisplay_prompt(); From 179e743017299b59c8f28c99ffed112e97f779af Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Thu, 5 Dec 2024 19:10:39 -0500 Subject: [PATCH 49/56] fix clippy errors --- applications/shell/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 0e4578e5b3..941b8c0e4d 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1419,7 +1419,7 @@ impl Shell { } fn execute_internal_less(&mut self, args: Vec<&str>) -> Result<(), &'static str> { - if args.len() < 1 { + if args.is_empty() { self.terminal.lock().print_to_terminal("not enough arguments provided.\n".to_string()); self.clear_cmdline(false)?; self.redisplay_prompt(); @@ -1509,7 +1509,7 @@ impl Shell { }; let prompt = self.env.lock().working_dir.lock().get_absolute_path(); - let full_path = format!("{}/{}", prompt.to_string(), file_path.to_string()); + let full_path = format!("{}/{}", prompt, file_path); let path = Path::new(full_path.as_str()); // navigate to the filepath specified by first argument @@ -1545,7 +1545,7 @@ impl Shell { } }, None => { - self.terminal.lock().print_to_terminal(format!("Path not found: {}\n", path.to_string()).to_string()); + self.terminal.lock().print_to_terminal(format!("Path not found: {}\n", path).to_string()); Ok("".to_string()) // Example: return empty string or a default value } } From cd6f199409aafa0e070a3470d0c95bc18f988ff8 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Thu, 5 Dec 2024 19:29:56 -0500 Subject: [PATCH 50/56] Revert changes to applications/less/src/lib.rs --- applications/less/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index 3f8718ecaf..aea7f9cc65 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -260,4 +260,4 @@ pub fn main(_args: Vec) -> isize { // } // const USAGE: &'static str = "Usage: less file -// read files"; \ No newline at end of file +// read files"; From 29e33e2f4f6d98428e0cda3a64d5600f808f8cc9 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Thu, 5 Dec 2024 22:33:11 -0500 Subject: [PATCH 51/56] Update sytling --- applications/shell/src/lib.rs | 61 ++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 941b8c0e4d..d1e8c88c79 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -506,7 +506,7 @@ impl Shell { return Ok(()); } - + // Check if the Q key is pressed and exit from 'less' mode if keyevent.keycode == Keycode::Q && self.less{ self.less = false; self.terminal.lock().clear(); @@ -514,21 +514,21 @@ impl Shell { return Ok(()); } - // Cycles to the next previous command + // Check if the Up key is pressed and scroll up in 'less' mode if keyevent.keycode == Keycode::Up && self.less { if self.line_start > 0 { self.line_start -= 1; } - let _ = self.display_content_slice(); + self.display_content_slice()?; return Ok(()); } - // Cycles to the next previous command + // Check if the Down key is pressed and scroll down in 'less' mode if keyevent.keycode == Keycode::Down && self.less { if self.line_start + 1 < self.map.len() { self.line_start += 1; } - let _ = self.display_content_slice(); + self.display_content_slice()?; return Ok(()); } @@ -1418,6 +1418,7 @@ impl Shell { } } + /// Executes the 'less' internal command to display the content of a file. fn execute_internal_less(&mut self, args: Vec<&str>) -> Result<(), &'static str> { if args.is_empty() { self.terminal.lock().print_to_terminal("not enough arguments provided.\n".to_string()); @@ -1426,24 +1427,24 @@ impl Shell { return Ok(()) } - self.less = true; let file_path = args[0]; - let _ = self.get_content_string(file_path.to_string()); + self.less = true; + self.get_content_string(file_path.to_string()); self.terminal.lock().clear(); self.clear_cmdline(false)?; self.parse_content(); - let _ = self.display_content_slice(); + self.display_content_slice()?; self.redisplay_prompt(); Ok(()) } + + /// Display part of the file (may be whole file if the file is short) to the terminal, indicated + /// by line_start attribute fn display_content_slice(&mut self) -> Result<(), &'static str> { - // Get exclusive control of the terminal. It is locked through the whole function to - // avoid the overhead of locking it multiple times. - // Calculate the last line to display. Make sure we don't extend over the end of the file. let (_width, height) = self.terminal.lock().get_text_dimensions(); - let mut line_end: usize = self.line_start + (height-20); + let mut line_end: usize = self.line_start + (height) - 5; if line_end > self.map.len() { line_end = self.map.len(); @@ -1467,6 +1468,8 @@ impl Shell { self.terminal.lock().refresh_display() } + /// This function parses the text file. It scans through the whole file and records the string slice + /// for each line. It stores index of each starting and ending char position of each line (separated by '\n) fn parse_content(&mut self) { // Get the width and height of the terminal screen. let (width, height) = self.terminal.lock().get_text_dimensions(); @@ -1503,13 +1506,15 @@ impl Shell { } } - fn get_content_string(&mut self, file_path: String) -> Result { + /// Stores the entire file as a string to be parsed by 'less' operation + fn get_content_string(&mut self, file_path: String) { let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { - return Err("failed to get current task".to_string()); + self.terminal.lock().print_to_terminal("failed to get current task".to_string()); + return; }; - let prompt = self.env.lock().working_dir.lock().get_absolute_path(); - let full_path = format!("{}/{}", prompt, file_path); + let curr_dir = self.env.lock().working_dir.lock().get_absolute_path(); + let full_path = format!("{}/{}", curr_dir, file_path); let path = Path::new(full_path.as_str()); // navigate to the filepath specified by first argument @@ -1517,36 +1522,34 @@ impl Shell { Some(file_dir_enum) => { match file_dir_enum { + // Checks if it is a directory FileOrDir::Dir(directory) => { - Err(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())) + self.terminal.lock().print_to_terminal(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())); + return; } + // Checks if it is a file and reads it into a utf8 string FileOrDir::File(file) => { let mut file_locked = file.lock(); let file_size = file_locked.len(); let mut string_slice_as_bytes = vec![0; file_size]; - let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { - Ok(num) => num, - Err(e) => { - self.terminal.lock().print_to_terminal("Failed to read error ".to_string()); - return Err(format!("Failed to file size: {:?}", e)); - } - }; + if let Err(_e) = file_locked.read_at(&mut string_slice_as_bytes, 0) { + self.terminal.lock().print_to_terminal("Failed to read error".to_string()); + return; + } let read_string = match str::from_utf8(&string_slice_as_bytes) { Ok(string_slice) => string_slice, - Err(utf8_err) => { + Err(_utf8_err) => { self.terminal.lock().print_to_terminal("File was not a printable UTF-8 text file".to_string()); - return Err(format!("Failed to read file: {:?}", utf8_err)); + return; } }; - //self.terminal.lock().print_to_terminal(read_string.to_string()); + // Stores the content of the file as a string self.content = read_string.to_string(); - Ok(read_string.to_string()) } } }, None => { self.terminal.lock().print_to_terminal(format!("Path not found: {}\n", path).to_string()); - Ok("".to_string()) // Example: return empty string or a default value } } } From b11c169afd4c3502889373f220857b9c26b66277 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Thu, 5 Dec 2024 22:47:09 -0500 Subject: [PATCH 52/56] Fix clippy errors again --- applications/shell/src/lib.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index d1e8c88c79..ea0d4a7ce8 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1444,7 +1444,7 @@ impl Shell { fn display_content_slice(&mut self) -> Result<(), &'static str> { // Calculate the last line to display. Make sure we don't extend over the end of the file. let (_width, height) = self.terminal.lock().get_text_dimensions(); - let mut line_end: usize = self.line_start + (height) - 5; + let mut line_end: usize = self.line_start + (height - 20); if line_end > self.map.len() { line_end = self.map.len(); @@ -1472,9 +1472,7 @@ impl Shell { /// for each line. It stores index of each starting and ending char position of each line (separated by '\n) fn parse_content(&mut self) { // Get the width and height of the terminal screen. - let (width, height) = self.terminal.lock().get_text_dimensions(); - - self.terminal.lock().print_to_terminal(format!("{} {}\n", width, height).to_string()); + let (width, _) = self.terminal.lock().get_text_dimensions(); // Number of the current line. let mut cur_line_num: usize = 0; @@ -1510,7 +1508,6 @@ impl Shell { fn get_content_string(&mut self, file_path: String) { let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { self.terminal.lock().print_to_terminal("failed to get current task".to_string()); - return; }; let curr_dir = self.env.lock().working_dir.lock().get_absolute_path(); @@ -1525,7 +1522,6 @@ impl Shell { // Checks if it is a directory FileOrDir::Dir(directory) => { self.terminal.lock().print_to_terminal(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())); - return; } // Checks if it is a file and reads it into a utf8 string FileOrDir::File(file) => { @@ -1534,13 +1530,11 @@ impl Shell { let mut string_slice_as_bytes = vec![0; file_size]; if let Err(_e) = file_locked.read_at(&mut string_slice_as_bytes, 0) { self.terminal.lock().print_to_terminal("Failed to read error".to_string()); - return; } let read_string = match str::from_utf8(&string_slice_as_bytes) { Ok(string_slice) => string_slice, Err(_utf8_err) => { self.terminal.lock().print_to_terminal("File was not a printable UTF-8 text file".to_string()); - return; } }; // Stores the content of the file as a string From b8c6b0c4679593ffe13cbdd392f355aed7f7d331 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Thu, 5 Dec 2024 23:13:11 -0500 Subject: [PATCH 53/56] fix clippy errors again --- applications/shell/src/lib.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index ea0d4a7ce8..7d4786d53c 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1429,7 +1429,7 @@ impl Shell { let file_path = args[0]; self.less = true; - self.get_content_string(file_path.to_string()); + let _ = self.get_content_string(file_path.to_string()); self.terminal.lock().clear(); self.clear_cmdline(false)?; self.parse_content(); @@ -1461,6 +1461,7 @@ impl Shell { None => return Err("failed to get the byte indices of the last line") }; + info!("{}", self.content.len() - 1); self.terminal.lock().clear(); self.terminal.lock().print_to_terminal( self.content[start_indices.start..end_indices.end].to_string() @@ -1498,16 +1499,13 @@ impl Shell { previous_char = c; } self.map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: self.content.len() }); - - for (line_num, line_slice) in &self.map { - self.terminal.lock().print_to_terminal(format!("Line {}: start = {}, end = {}\n", line_num, line_slice.start, line_slice.end).to_string()); - } } /// Stores the entire file as a string to be parsed by 'less' operation - fn get_content_string(&mut self, file_path: String) { + fn get_content_string(&mut self, file_path: String) -> Result{ let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { self.terminal.lock().print_to_terminal("failed to get current task".to_string()); + return Err("failed to get current task".to_string()); }; let curr_dir = self.env.lock().working_dir.lock().get_absolute_path(); @@ -1522,6 +1520,7 @@ impl Shell { // Checks if it is a directory FileOrDir::Dir(directory) => { self.terminal.lock().print_to_terminal(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())); + return Err(format!("Failed to read directory").to_string()) } // Checks if it is a file and reads it into a utf8 string FileOrDir::File(file) => { @@ -1530,20 +1529,24 @@ impl Shell { let mut string_slice_as_bytes = vec![0; file_size]; if let Err(_e) = file_locked.read_at(&mut string_slice_as_bytes, 0) { self.terminal.lock().print_to_terminal("Failed to read error".to_string()); + return Err(format!("Failed to read file")); } let read_string = match str::from_utf8(&string_slice_as_bytes) { Ok(string_slice) => string_slice, Err(_utf8_err) => { self.terminal.lock().print_to_terminal("File was not a printable UTF-8 text file".to_string()); + return Err(format!("File was not a printable UTF-8 text file").to_string()); } }; // Stores the content of the file as a string self.content = read_string.to_string(); + Ok(read_string.to_string()) } } }, None => { self.terminal.lock().print_to_terminal(format!("Path not found: {}\n", path).to_string()); + return Err(format!("File was not found").to_string()); } } } From ad9724e75a8d133f65817cdc3c858bf3850b265c Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Thu, 5 Dec 2024 23:38:42 -0500 Subject: [PATCH 54/56] restore get get content string to return result --- applications/shell/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 7d4786d53c..100a775c68 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1446,7 +1446,7 @@ impl Shell { let (_width, height) = self.terminal.lock().get_text_dimensions(); let mut line_end: usize = self.line_start + (height - 20); - if line_end > self.map.len() { + if self.map.len() < line_end { line_end = self.map.len(); } @@ -1461,7 +1461,6 @@ impl Shell { None => return Err("failed to get the byte indices of the last line") }; - info!("{}", self.content.len() - 1); self.terminal.lock().clear(); self.terminal.lock().print_to_terminal( self.content[start_indices.start..end_indices.end].to_string() From e7d127fdb353c32bb383ce62ac0ce94692815f65 Mon Sep 17 00:00:00 2001 From: "nathanhsiao123@gmail.com" Date: Fri, 6 Dec 2024 00:20:11 -0500 Subject: [PATCH 55/56] Fix issue with map initialization --- applications/shell/src/lib.rs | 41 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 100a775c68..22018e14a6 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1483,7 +1483,9 @@ impl Shell { // The previous character during the iteration. Set '\0' as the initial value since we don't expect // to encounter this character in the beginning of the file. let mut previous_char: char = '\0'; - + // Clear contents in map + self.map.clear(); + // Iterate through the whole file. // `c` is the current character. `str_idx` is the index of the first byte of the current character. for (str_idx, c) in self.content.char_indices() { @@ -1498,17 +1500,20 @@ impl Shell { previous_char = c; } self.map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: self.content.len() }); + + for (line_num, line_slice) in &self.map { + info!("Line {}: start = {}, end = {}\n", line_num, line_slice.start, line_slice.end); + } } /// Stores the entire file as a string to be parsed by 'less' operation - fn get_content_string(&mut self, file_path: String) -> Result{ + fn get_content_string(&mut self, file_path: String) -> Result { let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { - self.terminal.lock().print_to_terminal("failed to get current task".to_string()); return Err("failed to get current task".to_string()); }; - let curr_dir = self.env.lock().working_dir.lock().get_absolute_path(); - let full_path = format!("{}/{}", curr_dir, file_path); + let prompt = self.env.lock().working_dir.lock().get_absolute_path(); + let full_path = format!("{}/{}", prompt, file_path); let path = Path::new(full_path.as_str()); // navigate to the filepath specified by first argument @@ -1516,36 +1521,36 @@ impl Shell { Some(file_dir_enum) => { match file_dir_enum { - // Checks if it is a directory FileOrDir::Dir(directory) => { - self.terminal.lock().print_to_terminal(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())); - return Err(format!("Failed to read directory").to_string()) + Err(format!("{:?} a directory, cannot 'less' non-files.", directory.lock().get_name())) } - // Checks if it is a file and reads it into a utf8 string FileOrDir::File(file) => { let mut file_locked = file.lock(); let file_size = file_locked.len(); let mut string_slice_as_bytes = vec![0; file_size]; - if let Err(_e) = file_locked.read_at(&mut string_slice_as_bytes, 0) { - self.terminal.lock().print_to_terminal("Failed to read error".to_string()); - return Err(format!("Failed to read file")); - } + let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { + Ok(num) => num, + Err(e) => { + self.terminal.lock().print_to_terminal("Failed to read error ".to_string()); + return Err(format!("Failed to file size: {:?}", e)); + } + }; let read_string = match str::from_utf8(&string_slice_as_bytes) { Ok(string_slice) => string_slice, - Err(_utf8_err) => { + Err(utf8_err) => { self.terminal.lock().print_to_terminal("File was not a printable UTF-8 text file".to_string()); - return Err(format!("File was not a printable UTF-8 text file").to_string()); + return Err(format!("Failed to read file: {:?}", utf8_err)); } }; - // Stores the content of the file as a string + //self.terminal.lock().print_to_terminal(read_string.to_string()); self.content = read_string.to_string(); - Ok(read_string.to_string()) + Ok(read_string.to_string()) } } }, None => { self.terminal.lock().print_to_terminal(format!("Path not found: {}\n", path).to_string()); - return Err(format!("File was not found").to_string()); + Ok("File not found".to_string()) } } } From f21345c568457e22a84f43704e6e6e2e404c6bbc Mon Sep 17 00:00:00 2001 From: nathan hsiao Date: Fri, 6 Dec 2024 11:33:12 -0600 Subject: [PATCH 56/56] Clean up code --- applications/shell/src/lib.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 22018e14a6..88a3f3d2be 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -136,6 +136,7 @@ enum AppErr { SpawnErr(String) } +/// This bundles start and end index char into one structure struct LineSlice { // The starting index in the String for a line. (inclusive) start: usize, @@ -177,11 +178,11 @@ pub struct Shell { terminal: Arc>, /// The indicator to show "text editing" mode less: bool, - // string to print + // String to store file content: String, // BTree Map to keep track of file new line indices map: BTreeMap, - // Line + // Line to start the display line_start: usize } @@ -1500,10 +1501,6 @@ impl Shell { previous_char = c; } self.map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: self.content.len() }); - - for (line_num, line_slice) in &self.map { - info!("Line {}: start = {}, end = {}\n", line_num, line_slice.start, line_slice.end); - } } /// Stores the entire file as a string to be parsed by 'less' operation @@ -1542,7 +1539,6 @@ impl Shell { return Err(format!("Failed to read file: {:?}", utf8_err)); } }; - //self.terminal.lock().print_to_terminal(read_string.to_string()); self.content = read_string.to_string(); Ok(read_string.to_string()) }