4 Commits

6 changed files with 161 additions and 67 deletions

24
Cargo.lock generated
View File

@ -26,9 +26,9 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.16.0" version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]] [[package]]
name = "cc" name = "cc"
@ -241,9 +241,9 @@ dependencies = [
[[package]] [[package]]
name = "luau0-src" name = "luau0-src"
version = "0.11.2+luau653" version = "0.12.0+luau657"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02313a53daf1fae25e82f7e7ca56180b72d1f08c514426672877cd957298201c" checksum = "3a4b4c16b82ddf60e0fa93ca5a6afc504a6db22310cc82ecec629cd2e405b2ca"
dependencies = [ dependencies = [
"cc", "cc",
] ]
@ -274,9 +274,9 @@ dependencies = [
[[package]] [[package]]
name = "mlua" name = "mlua"
version = "0.10.2" version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea43c3ffac2d0798bd7128815212dd78c98316b299b7a902dabef13dc7b6b8d" checksum = "d3f763c1041eff92ffb5d7169968a327e1ed2ebfe425dac0ee5a35f29082534b"
dependencies = [ dependencies = [
"bstr", "bstr",
"either", "either",
@ -289,9 +289,9 @@ dependencies = [
[[package]] [[package]]
name = "mlua-sys" name = "mlua-sys"
version = "0.6.6" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63a11d485edf0f3f04a508615d36c7d50d299cf61a7ee6d3e2530651e0a31771" checksum = "1901c1a635a22fe9250ffcc4fcc937c16b47c2e9e71adba8784af8bca1f69594"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
@ -390,9 +390,9 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.43" version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
@ -502,9 +502,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.14" version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"

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 uzers::User;
use crate::{session::{MapDisplay, Pse}, valid_pbuf::IsValid}; use crate::{session::{MapDisplay, Pse}, valid_pbuf::IsValid};
@ -12,18 +12,25 @@ impl PathBufIsValid for PathBuf {
} }
} }
struct ChangeDirectory; struct ChangeDirectory(Option<PathBuf>);
impl ChangeDirectory { impl ChangeDirectory {
fn set_current_dir(&self, new_path: &Path) -> Option<PathBuf> { fn set_dir(&mut self, new_path: &Path) -> Option<PathBuf> {
std::env::set_current_dir(new_path).map_or_display_none(|()| Some(new_path.to_path_buf())) 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> { fn home_dir(&mut self) -> Option<PathBuf> {
home::home_dir().map_or(self.set_current_dir(Path::new("/")), |home_pathbuf| self.set_current_dir(&home_pathbuf)) home::home_dir().map_or(self.set_dir(Path::new("/")), |home_pathbuf| self.set_dir(&home_pathbuf))
} }
fn previous_dir(&self) -> Option<PathBuf> { fn previous_dir(&mut self) -> Option<PathBuf> {
unimplemented!() 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> { 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 string_path = vec_args.concat();
let new_path = Path::new(string_path.as_str()); let new_path = Path::new(string_path.as_str());
match new_path.is_dir() { match new_path.is_dir() {
true => self.set_current_dir(new_path), true => self.set_dir(new_path),
false => { false => {
match new_path.file_name() { match new_path.file_name() {
Some(file_name) => println!("cd: {:?} is not a directory.", 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(); let vec_args: Vec<String> = args.map(|arg| arg.to_owned()).collect();
match vec_args.first() { match vec_args.first() {
None => self.home_dir(), None => self.home_dir(),
Some(arg) => match arg.as_str() { Some(arg) => match arg.as_str() {
"/" => self.set_current_dir(Path::new("/")), "/" => self.set_dir(Path::new("/")),
"-" => self.previous_dir(), "-" => self.previous_dir(),
arg_str => { arg_str => {
let mut arg_chars = arg_str.chars(); let mut arg_chars = arg_str.chars();
@ -85,15 +92,17 @@ pub trait Command {
} }
impl Command for Pse { impl Command for Pse {
fn spawn_sys_cmd(&mut self) { 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() { if let Some(command) = args.next() {
match command { 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.rt.history.add(self.rt.input.literal.as_str())
},
command => if let Ok(mut child) = process::Command::new(command).args(args).spawn() { command => if let Ok(mut child) = process::Command::new(command).args(args).spawn() {
self.history.add(self.rt.input.as_str()); self.rt.history.add(self.rt.input.literal.as_str());
child.wait().ok(); child.wait().ok();
} else { } else {
println!("\npse: Unknown command: {}", self.rt.input) println!("pse: Unknown command: {}", self.rt.input.literal)
} }
} }
} }

View File

@ -4,38 +4,39 @@ use crate::{rc::{self}, session::{self, MapDisplay}, valid_pbuf::IsValid};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct History { pub struct History {
fs_history: Option<Vec<String>>, pub history: Vec<String>,
history: Vec<String>, pub index: isize,
file: Option<PathBuf>, file: Option<PathBuf>,
} }
impl History { impl History {
pub fn init() -> Self { pub fn init() -> Self {
let mut history = Vec::new();
let file = rc::config_dir().map(|mut config_dir| { let file = rc::config_dir().map(|mut config_dir| {
config_dir.push(".history"); config_dir.push(".history");
config_dir.is_valid_file_or_create(b""); config_dir.is_valid_file_or_create(b"");
config_dir config_dir
}); });
let fs_history = file.as_ref().and_then(|file| { file.as_ref().and_then(|file| {
File::open(file).map_or_display_none(|file| { File::open(file).map_or_display_none(|file| {
Some(BufReader::new(file).lines().map_while(Result::ok).collect::<Vec<String>>()) let mut fs_history_vec = BufReader::new(file).lines().map_while(Result::ok).collect::<Vec<String>>();
fs_history_vec.dedup();
fs_history_vec.reverse();
Some(fs_history_vec)
}) })
}); }).inspect(|fs_history_vec| history = fs_history_vec.clone());
Self {
history: Vec::new(), Self { history, file, index: -1 }
fs_history,
file,
}
} }
pub fn write_to_file_fallible(&mut self) { pub fn write_to_file_fallible(&mut self) {
if self.history.is_empty() { return; } if self.history.is_empty() { return; }
if let (Some(history_file), Some(fs_history)) = (&self.file, &self.fs_history) { if let Some(history_file) = &self.file {
OpenOptions::new() OpenOptions::new()
.append(true) .append(true)
.open(history_file.as_path()) .open(history_file.as_path())
.map_or_display(|mut file| { .map_or_display(|mut file| {
let newline_maybe = if fs_history.is_empty() { "" } else { "\n" }; let newline_maybe = if self.history.is_empty() { "" } else { "\n" };
let formatted = format!("{newline_maybe}{}", self.history.join("\n")); let formatted = format!("{newline_maybe}{}", self.history.join("\n"));
file.write_all(formatted.as_bytes()).unwrap_or_else(session::shell_error) file.write_all(formatted.as_bytes()).unwrap_or_else(session::shell_error)
}); });

View File

@ -41,29 +41,36 @@ pub struct Config {
pub norc: bool, pub norc: bool,
pub vm: VmConfig, pub vm: VmConfig,
} }
#[derive(Debug, Clone)]
pub struct Input {
pub literal: String,
pub cursor: u16,
}
pub struct Rt { pub struct Rt {
pub input: Input,
pub ps: Rc<RefCell<String>>, pub ps: Rc<RefCell<String>>,
pub input: String,
pub vm: Luau, pub vm: Luau,
pub history: History,
} }
pub struct Pse { pub struct Pse {
pub config: Config, pub config: Config,
pub history: History,
pub rt: Rt pub rt: Rt
} }
impl Pse { impl Pse {
const DEFAULT_PS: &str = concat!("pse-", env!("CARGO_PKG_VERSION"), "$ "); const DEFAULT_PS: &str = concat!("pse-", env!("CARGO_PKG_VERSION"), "$ ");
pub fn create(config: Config) -> Self { pub fn create(config: Config) -> Self {
Self { let rt = Rt {
rt: Rt {
ps: Rc::new(RefCell::new(Self::DEFAULT_PS.to_owned())), ps: Rc::new(RefCell::new(Self::DEFAULT_PS.to_owned())),
input: String::new(),
vm: Luau::new(), vm: Luau::new(),
},
history: History::init(), history: History::init(),
config, input: Input {
} literal: String::new(),
cursor: u16::MIN,
},
};
Self { rt, config }
} }
pub fn start(&mut self) { pub fn start(&mut self) {
@ -72,6 +79,6 @@ impl Pse {
fs::read_to_string(conf_file).map_or_display(|luau_conf| self.vm_exec(luau_conf)); fs::read_to_string(conf_file).map_or_display(|luau_conf| self.vm_exec(luau_conf));
} }
}; };
self.term_input_processor().map_or_display(|()| self.history.write_to_file_fallible()) self.term_input_processor().map_or_display(|()| self.rt.history.write_to_file_fallible())
} }
} }

View File

@ -1,4 +1,5 @@
use crossterm::{cursor, event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, execute, terminal}; use crossterm::{cursor, event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, execute, terminal};
use core::fmt;
use std::io::{self, Write}; use std::io::{self, Write};
use thiserror::Error; use thiserror::Error;
@ -23,8 +24,15 @@ pub enum InputHandleError {
} }
type InputResult<T> = Result<T, 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 { trait SpecificKeybinds {
const EXIT_1: &str; const EXIT_ID_1: &str;
const KEY_SPACE: char; const KEY_SPACE: char;
fn key_literal(&mut self, keycode: KeyCode) -> InputResult<()>; fn key_literal(&mut self, keycode: KeyCode) -> InputResult<()>;
fn key_ctrl(&mut self, input_key: KeyEvent, keycode: KeyCode) -> InputResult<()>; fn key_ctrl(&mut self, input_key: KeyEvent, keycode: KeyCode) -> InputResult<()>;
@ -32,9 +40,11 @@ trait SpecificKeybinds {
fn key_backspace(&mut self) -> InputResult<()>; fn key_backspace(&mut self) -> InputResult<()>;
fn key_arrow_right(&mut self) -> InputResult<()>; fn key_arrow_right(&mut self) -> InputResult<()>;
fn key_arrow_left(&mut self) -> InputResult<()>; fn key_arrow_left(&mut self) -> InputResult<()>;
fn key_arrow_up(&mut self) -> InputResult<()>;
fn key_arrow_down(&mut self) -> InputResult<()>;
} }
impl SpecificKeybinds for Pse { impl SpecificKeybinds for Pse {
const EXIT_1: &str = "exit"; const EXIT_ID_1: &str = "exit";
const KEY_SPACE: char = ' '; const KEY_SPACE: char = ' ';
fn key_literal(&mut self, keycode: KeyCode) -> InputResult<()> { fn key_literal(&mut self, keycode: KeyCode) -> InputResult<()> {
@ -48,6 +58,7 @@ impl SpecificKeybinds for Pse {
match input_key.modifiers.contains(KeyModifiers::CONTROL) { match input_key.modifiers.contains(KeyModifiers::CONTROL) {
true => match keycode { true => match keycode {
KeyCode::Char('c') => Err(InputHandleError::Sigint), KeyCode::Char('c') => Err(InputHandleError::Sigint),
KeyCode::Char('r') => unimplemented!(),
_ => Ok(()) _ => Ok(())
}, },
false => self.key_literal(keycode) false => self.key_literal(keycode)
@ -55,47 +66,105 @@ impl SpecificKeybinds for Pse {
} }
fn key_enter(&mut self) -> InputResult<()> { fn key_enter(&mut self) -> InputResult<()> {
if self.rt.input == Self::EXIT_1 { return Err(InputHandleError::UserExit) }; if self.rt.input.literal == Self::EXIT_ID_1 { return Err(InputHandleError::UserExit) };
terminal::disable_raw_mode().map_err(InputHandleError::DisableRaw)?; terminal::disable_raw_mode().map_err(InputHandleError::DisableRaw)?;
println!();
self.spawn_sys_cmd(); self.spawn_sys_cmd();
self.rt.input.clear(); self.rt.input.literal.clear();
self.rt.input.cursor = u16::MIN;
self.term_render_ps() self.term_render_ps()
} }
fn key_backspace(&mut self) -> InputResult<()> { fn key_backspace(&mut self) -> InputResult<()> {
if self.rt.input.pop().is_some() { if self.rt.input.literal.pop().is_some() {
execute!( execute!(
io::stdout(), io::stdout(),
cursor::MoveLeft(1), cursor::MoveLeft(1),
terminal::Clear(terminal::ClearType::UntilNewLine) terminal::Clear(terminal::ClearType::UntilNewLine)
).map_err(InputHandleError::Flush) ).map_err(InputHandleError::Flush)?;
} else { self.rt.input.cursor-=1;
//the string is empty, do terminal beep }
Ok(())
}
fn key_arrow_right(&mut self) -> InputResult<()> {
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<()> {
match self.term_input_cursor_move_left() {
Some(()) => execute!(io::stdout(), cursor::MoveLeft(1)).map_err(InputHandleError::Flush),
None => Ok(())
}
}
fn key_arrow_up(&mut self) -> InputResult<()> {
self.rt.history.index += 1;
if self.rt.history.index != self.rt.history.history.len() as isize {
self.term_render_reset()?;
self.term_render(self.rt.history.history[self.rt.history.index as usize].clone())?;
}
Ok(())
}
fn key_arrow_down(&mut self) -> InputResult<()> {
self.rt.history.index -= 1;
if self.rt.history.index != -1 {
self.term_render_reset()?;
self.term_render(self.rt.history.history[self.rt.history.index as usize].clone())?;
}
Ok(()) Ok(())
} }
} }
fn key_arrow_right(&mut self) -> InputResult<()> { pub trait TermInputCursor {
execute!(io::stdout(), cursor::MoveRight(1)).map_err(InputHandleError::Flush) 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 == u16::MIN { None } else {
match self.rt.input.cursor>u16::MIN {
true => { self.rt.input.cursor-=1; Some(()) }
false => None
}
}
} }
fn key_arrow_left(&mut self) -> InputResult<()> { fn term_input_cursor_move_right(&mut self) -> Option<()> {
execute!(io::stdout(), cursor::MoveLeft(1)).map_err(InputHandleError::Flush) if self.rt.input.cursor == u16::MAX { None } else {
match self.rt.input.cursor<self.rt.input.literal.chars().count() as u16 {
true => { self.rt.input.cursor+=1; Some(()) },
false => None
}
}
} }
} }
pub trait TermProcessor { pub trait TermProcessor {
fn term_render(&mut self, def_string: String) -> InputResult<()>; fn term_render(&mut self, def_string: String) -> InputResult<()>;
fn term_render_ps(&self) -> InputResult<()>; fn term_render_ps(&self) -> InputResult<()>;
fn term_render_reset(&mut self) -> InputResult<()>;
fn term_input_handler(&mut self, input_key: KeyEvent) -> Option<()>; fn term_input_handler(&mut self, input_key: KeyEvent) -> Option<()>;
fn term_input_mainthread(&mut self) -> io::Result<()>; fn term_input_mainthread(&mut self) -> io::Result<()>;
fn term_input_processor(&mut self) -> io::Result<()>; fn term_input_processor(&mut self) -> io::Result<()>;
} }
impl TermProcessor for Pse { impl TermProcessor for Pse {
fn term_render(&mut self, def_string: String) -> InputResult<()> { fn term_render(&mut self, text: String) -> InputResult<()> {
self.rt.input.push_str(&def_string); self.rt.input.literal.insert_str(self.rt.input.cursor.into(), &text);
write!(io::stdout(), "{def_string}").map_err(InputHandleError::Write)?; let input_literal_size = self.rt.input.literal.chars().count() as u16;
self.rt.input.cursor = input_literal_size;
if self.rt.input.cursor != input_literal_size {
execute!(io::stdout(), terminal::Clear(terminal::ClearType::UntilNewLine)).map_err(InputHandleError::Flush)?;
let slice = &self.rt.input.literal[(self.rt.input.cursor as usize)..];
write!(io::stdout(), "{text}{slice}").map_err(InputHandleError::Write)?;
} else {
write!(io::stdout(), "{text}").map_err(InputHandleError::Write)?;
}
io::stdout().flush().map_err(InputHandleError::Flush) io::stdout().flush().map_err(InputHandleError::Flush)
} }
@ -104,6 +173,13 @@ impl TermProcessor for Pse {
io::stdout().flush().map_err(InputHandleError::Flush) io::stdout().flush().map_err(InputHandleError::Flush)
} }
fn term_render_reset(&mut self) -> InputResult<()> {
execute!(io::stdout(), cursor::MoveLeft(self.rt.input.cursor)).map_err(InputHandleError::Flush)?;
self.rt.input.cursor = u16::MIN;
self.rt.input.literal.clear();
execute!(io::stdout(), terminal::Clear(terminal::ClearType::UntilNewLine)).map_err(InputHandleError::Flush)
}
fn term_input_handler(&mut self, input_key: KeyEvent) -> Option<()> { fn term_input_handler(&mut self, input_key: KeyEvent) -> Option<()> {
let input_handle = match input_key.code { let input_handle = match input_key.code {
KeyCode::Enter => self.key_enter(), KeyCode::Enter => self.key_enter(),
@ -111,8 +187,8 @@ impl TermProcessor for Pse {
KeyCode::Tab => todo!(), KeyCode::Tab => todo!(),
KeyCode::Right => self.key_arrow_right(), KeyCode::Right => self.key_arrow_right(),
KeyCode::Left => self.key_arrow_left(), KeyCode::Left => self.key_arrow_left(),
KeyCode::Up => todo!(), KeyCode::Up => self.key_arrow_up(),
KeyCode::Down => todo!(), KeyCode::Down => self.key_arrow_down(),
keycode => self.key_ctrl(input_key, keycode) keycode => self.key_ctrl(input_key, keycode)
}; };
input_handle.map_or_else(|inp_err| match inp_err { input_handle.map_or_else(|inp_err| match inp_err {

View File

@ -39,6 +39,7 @@ fn text_styles_funcs(luau: &Luau) -> lResult<(Table, Table)> {
underline_grey underline_black underline_green underline_yellow underline_grey underline_black underline_green underline_yellow
underline_blue underline_magenta underline_cyan underline_white underline_blue underline_magenta underline_cyan underline_white
bold bold
crossed_out
); );
background_styles_luau!(luau, background_table, background_styles_luau!(luau, background_table,
on_dark_grey on_dark_red on_dark_green on_dark_cyan on_dark_grey on_dark_red on_dark_green on_dark_cyan