diff --git a/Cargo.lock b/Cargo.lock index 681ce2d..61f9929 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "cc" @@ -241,9 +241,9 @@ dependencies = [ [[package]] name = "luau0-src" -version = "0.11.2+luau653" +version = "0.12.0+luau657" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02313a53daf1fae25e82f7e7ca56180b72d1f08c514426672877cd957298201c" +checksum = "3a4b4c16b82ddf60e0fa93ca5a6afc504a6db22310cc82ecec629cd2e405b2ca" dependencies = [ "cc", ] @@ -274,9 +274,9 @@ dependencies = [ [[package]] name = "mlua" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea43c3ffac2d0798bd7128815212dd78c98316b299b7a902dabef13dc7b6b8d" +checksum = "d3f763c1041eff92ffb5d7169968a327e1ed2ebfe425dac0ee5a35f29082534b" dependencies = [ "bstr", "either", @@ -289,9 +289,9 @@ dependencies = [ [[package]] name = "mlua-sys" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63a11d485edf0f3f04a508615d36c7d50d299cf61a7ee6d3e2530651e0a31771" +checksum = "1901c1a635a22fe9250ffcc4fcc937c16b47c2e9e71adba8784af8bca1f69594" dependencies = [ "cc", "cfg-if", @@ -390,9 +390,9 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", @@ -502,9 +502,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-xid" diff --git a/src/commands.rs b/src/commands.rs index 2549d55..265fbaa 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,4 +1,4 @@ -use std::{process, str::SplitWhitespace, path::{Path, PathBuf}}; +use std::{env, path::{Path, PathBuf}, process, str::SplitWhitespace}; use uzers::User; use crate::{session::{MapDisplay, Pse}, valid_pbuf::IsValid}; @@ -12,18 +12,25 @@ impl PathBufIsValid for PathBuf { } } -struct ChangeDirectory; +struct ChangeDirectory(Option); impl ChangeDirectory { - fn set_current_dir(&self, new_path: &Path) -> Option { - std::env::set_current_dir(new_path).map_or_display_none(|()| Some(new_path.to_path_buf())) + fn set_dir(&mut self, new_path: &Path) -> Option { + let past_dir = env::current_dir().ok(); + env::set_current_dir(new_path).map_or_display_none(|()| { + self.0 = past_dir; + Some(new_path.to_path_buf()) + }) } - fn home_dir(&self) -> Option { - home::home_dir().map_or(self.set_current_dir(Path::new("/")), |home_pathbuf| self.set_current_dir(&home_pathbuf)) + fn home_dir(&mut self) -> Option { + home::home_dir().map_or(self.set_dir(Path::new("/")), |home_pathbuf| self.set_dir(&home_pathbuf)) } - fn previous_dir(&self) -> Option { - unimplemented!() + fn previous_dir(&mut self) -> Option { + match self.0.as_ref() { + Some(p_buf) => self.set_dir(&p_buf.to_path_buf()), + None => None + } } fn specific_user_dir(&self, requested_user: String) -> Option { @@ -43,11 +50,11 @@ impl ChangeDirectory { } } - fn cd_args(&self, vec_args: Vec) -> Option { + fn cd_args(&mut self, vec_args: Vec) -> Option { let string_path = vec_args.concat(); let new_path = Path::new(string_path.as_str()); match new_path.is_dir() { - true => self.set_current_dir(new_path), + true => self.set_dir(new_path), false => { match new_path.file_name() { Some(file_name) => println!("cd: {:?} is not a directory.", file_name), @@ -58,12 +65,12 @@ impl ChangeDirectory { } } - fn change_directory(&self, args: SplitWhitespace) -> Option { + fn change_directory(&mut self, args: SplitWhitespace) -> Option { let vec_args: Vec = args.map(|arg| arg.to_owned()).collect(); match vec_args.first() { None => self.home_dir(), Some(arg) => match arg.as_str() { - "/" => self.set_current_dir(Path::new("/")), + "/" => self.set_dir(Path::new("/")), "-" => self.previous_dir(), arg_str => { let mut arg_chars = arg_str.chars(); @@ -85,15 +92,17 @@ pub trait Command { } impl Command for Pse { fn spawn_sys_cmd(&mut self) { - let mut args = self.rt.input.split_whitespace(); + let mut args = self.rt.input.literal.split_whitespace(); if let Some(command) = args.next() { match command { - "cd" => if ChangeDirectory.change_directory(args).is_some() { self.history.add(self.rt.input.as_str()) }, + "cd" => if ChangeDirectory(None).change_directory(args).is_some() { + self.history.add(self.rt.input.literal.as_str()) + }, command => if let Ok(mut child) = process::Command::new(command).args(args).spawn() { - self.history.add(self.rt.input.as_str()); + self.history.add(self.rt.input.literal.as_str()); child.wait().ok(); } else { - println!("\npse: Unknown command: {}", self.rt.input) + println!("pse: Unknown command: {}", self.rt.input.literal) } } } diff --git a/src/session.rs b/src/session.rs index 49bc820..0045a36 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,5 +1,5 @@ use mlua::Lua as Luau; -use std::{cell::RefCell, fs, rc::Rc}; +use std::{cell::RefCell, fs, rc::Rc, usize}; use core::fmt; use crate::{ @@ -41,9 +41,14 @@ pub struct Config { pub norc: bool, pub vm: VmConfig, } +#[derive(Debug, Clone)] +pub struct Input { + pub literal: String, + pub cursor: usize, +} pub struct Rt { + pub input: Input, pub ps: Rc>, - pub input: String, pub vm: Luau, } pub struct Pse { @@ -55,15 +60,16 @@ impl Pse { const DEFAULT_PS: &str = concat!("pse-", env!("CARGO_PKG_VERSION"), "$ "); pub fn create(config: Config) -> Self { - Self { - rt: Rt { - ps: Rc::new(RefCell::new(Self::DEFAULT_PS.to_owned())), - input: String::new(), - vm: Luau::new(), + let rt = Rt { + ps: Rc::new(RefCell::new(Self::DEFAULT_PS.to_owned())), + vm: Luau::new(), + input: Input { + literal: String::new(), + cursor: usize::MIN, }, - history: History::init(), - config, - } + }; + + Self { rt, config, history: History::init() } } pub fn start(&mut self) { diff --git a/src/terminal.rs b/src/terminal.rs index 0827f03..c7277bd 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,4 +1,5 @@ use crossterm::{cursor, event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, execute, terminal}; +use core::fmt; use std::io::{self, Write}; use thiserror::Error; @@ -23,6 +24,13 @@ pub enum InputHandleError { } type InputResult = Result; +#[allow(dead_code)] +fn debug(s: S) { + terminal::disable_raw_mode().unwrap(); + println!("{s}"); + terminal::enable_raw_mode().unwrap() +} + trait SpecificKeybinds { const EXIT_1: &str; const KEY_SPACE: char; @@ -55,33 +63,72 @@ impl SpecificKeybinds for Pse { } fn key_enter(&mut self) -> InputResult<()> { - if self.rt.input == Self::EXIT_1 { return Err(InputHandleError::UserExit) }; + if self.rt.input.literal == Self::EXIT_1 { return Err(InputHandleError::UserExit) }; terminal::disable_raw_mode().map_err(InputHandleError::DisableRaw)?; + println!(); self.spawn_sys_cmd(); - self.rt.input.clear(); + self.rt.input.literal.clear(); + self.rt.input.cursor = usize::MIN; self.term_render_ps() } fn key_backspace(&mut self) -> InputResult<()> { - if self.rt.input.pop().is_some() { + if self.rt.input.literal.pop().is_some() { execute!( io::stdout(), cursor::MoveLeft(1), terminal::Clear(terminal::ClearType::UntilNewLine) - ).map_err(InputHandleError::Flush) + ).map_err(InputHandleError::Flush)?; + self.rt.input.cursor-=1; + Ok(()) } else { - //the string is empty, do terminal beep Ok(()) } } fn key_arrow_right(&mut self) -> InputResult<()> { - execute!(io::stdout(), cursor::MoveRight(1)).map_err(InputHandleError::Flush) + match self.term_input_cursor_move_right() { + Some(()) => execute!(io::stdout(), cursor::MoveRight(1)).map_err(InputHandleError::Flush), + None => Ok(()) + } } fn key_arrow_left(&mut self) -> InputResult<()> { - execute!(io::stdout(), cursor::MoveLeft(1)).map_err(InputHandleError::Flush) + match self.term_input_cursor_move_left() { + Some(()) => execute!(io::stdout(), cursor::MoveLeft(1)).map_err(InputHandleError::Flush), + None => Ok(()) + } + } +} + +pub trait TermInputCursor { + fn term_input_cursor_move_left(&mut self) -> Option<()>; + fn term_input_cursor_move_right(&mut self) -> Option<()>; +} +impl TermInputCursor for Pse { + fn term_input_cursor_move_left(&mut self) -> Option<()> { + if self.rt.input.cursor == usize::MIN { None } else { + match self.rt.input.cursor>usize::MIN { + true => { + self.rt.input.cursor-=1; + Some(()) + }, + false => None + } + } + } + + fn term_input_cursor_move_right(&mut self) -> Option<()> { + if self.rt.input.cursor == usize::MAX { None } else { + match self.rt.input.cursor { + self.rt.input.cursor+=1; + Some(()) + }, + false => None + } + } } } @@ -94,8 +141,13 @@ pub trait TermProcessor { } impl TermProcessor for Pse { fn term_render(&mut self, def_string: String) -> InputResult<()> { - self.rt.input.push_str(&def_string); - write!(io::stdout(), "{def_string}").map_err(InputHandleError::Write)?; + self.rt.input.literal.insert_str(self.rt.input.cursor, &def_string); + self.rt.input.cursor+=1; + if self.rt.input.cursor != self.rt.input.literal.chars().count() { + + } else { + write!(io::stdout(), "{}", def_string).map_err(InputHandleError::Write)?; + } io::stdout().flush().map_err(InputHandleError::Flush) } diff --git a/src/vm/shell.rs b/src/vm/shell.rs index 8c8f9a8..fc6ff82 100644 --- a/src/vm/shell.rs +++ b/src/vm/shell.rs @@ -29,8 +29,13 @@ impl UserData for Shell { fn add_methods>(methods: &mut M) { methods.add_meta_method_mut(MetaMethod::NewIndex, |_, this, (t_index, t_value): (String, String)| -> lResult<()> { if t_index == "PROMPT" { - let mut prompt = this.0.borrow_mut(); - *prompt = t_value; + match t_value.len() as u16 >= u16::MAX { + true => { mlua::Error::runtime(format!("SHELL.PROMPT's length exceeded or equals the max size. ({})", u16::MAX)); }, + false => { + let mut prompt = this.0.borrow_mut(); + *prompt = t_value; + }, + } } Ok(()) });