left and right arrow keys work

This commit is contained in:
2025-01-29 18:17:40 -05:00
parent ee0ed28f85
commit c91b486214
5 changed files with 121 additions and 49 deletions

View File

@ -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<PathBuf>);
impl ChangeDirectory {
fn set_current_dir(&self, new_path: &Path) -> Option<PathBuf> {
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<PathBuf> {
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<PathBuf> {
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<PathBuf> {
home::home_dir().map_or(self.set_dir(Path::new("/")), |home_pathbuf| self.set_dir(&home_pathbuf))
}
fn previous_dir(&self) -> Option<PathBuf> {
unimplemented!()
fn previous_dir(&mut self) -> Option<PathBuf> {
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<PathBuf> {
@ -43,11 +50,11 @@ impl ChangeDirectory {
}
}
fn cd_args(&self, vec_args: Vec<String>) -> Option<PathBuf> {
fn cd_args(&mut self, vec_args: Vec<String>) -> Option<PathBuf> {
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<PathBuf> {
fn change_directory(&mut self, args: SplitWhitespace) -> Option<PathBuf> {
let vec_args: Vec<String> = 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)
}
}
}

View File

@ -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<RefCell<String>>,
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) {

View File

@ -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<T> = Result<T, InputHandleError>;
#[allow(dead_code)]
fn debug<S: fmt::Display>(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.literal.chars().count() {
true => {
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)
}

View File

@ -29,8 +29,13 @@ impl UserData for Shell {
fn add_methods<M: UserDataMethods<Self>>(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(())
});