Merge pull request from eatmynerds/rustyline-keyevents

Rustyline KeyEvents
This commit is contained in:
Chance 2024-12-05 10:29:55 -05:00 committed by BitSyndicate
commit 1840f141a6
Signed by untrusted user: bitsyndicate
GPG key ID: 443E4198D6BBA6DE
6 changed files with 94 additions and 52 deletions

View file

@ -32,5 +32,4 @@ incremental = true
codegen-units = 512 codegen-units = 512
[workspace.dependencies] [workspace.dependencies]
anyhow = "1.0.93"
zephyr = { path = "./subcrates/zephyr" } zephyr = { path = "./subcrates/zephyr" }

View file

@ -6,14 +6,13 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.93" anyhow = "1.0.93"
chrono = "0.4.38" chrono = "0.4.38"
clap = { version = "4.5.21", features = ["derive"] }
colored = "2.1.0" colored = "2.1.0"
lazy_static = "1.5.0" lazy_static = "1.5.0"
log = "0.4.22" log = "0.4.22"
once_cell = "1.20.2" once_cell = "1.20.2"
parking_lot = "0.12.3" parking_lot = "0.12.3"
regex = "1.11.1" regex = "1.11.1"
rustyline = "15.0.0" rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] }
thiserror = "2.0.3" thiserror = "2.0.3"
tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] }
wgpu = "23.0.1" wgpu = "23.0.1"

View file

@ -1,6 +1,5 @@
use super::COMMAND_LIST; use super::COMMAND_LIST;
use std::process::Command; use std::process::Command;
use log::debug;
pub(crate) fn say_hello() { pub(crate) fn say_hello() {
println!("Hello, World!"); println!("Hello, World!");
@ -18,10 +17,8 @@ pub(crate) fn exit() {
pub(crate) fn clear() { pub(crate) fn clear() {
println!("Clearing screen..., running command"); println!("Clearing screen..., running command");
let _result = if cfg!(target_os = "windows") { let _result = if cfg!(target_os = "windows") {
debug!("target_os is windows");
Command::new("cmd").args(["/c", "cls"]).spawn() Command::new("cmd").args(["/c", "cls"]).spawn()
} else { } else {
debug!("target_os is unix");
Command::new("clear").spawn() Command::new("clear").spawn()
}; };
} }

View file

@ -1,10 +1,68 @@
use super::{commands, Callable, COMMAND_LIST}; use crate::{
core::repl::{commands, Callable, COMMAND_LIST},
utils::logger::LOGGER,
};
use anyhow::Result; use anyhow::Result;
use chrono::Local; use chrono::Local;
use colored::Colorize; use colored::Colorize;
use log::debug; use log::debug;
use regex::Regex; use regex::Regex;
use rustyline::DefaultEditor; use rustyline::{
error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, history::DefaultHistory,
Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, EventHandler, Helper,
Hinter, KeyEvent, RepeatCount, Validator,
};
use std::{
borrow::Cow::{self, Borrowed, Owned},
sync::{Arc, Mutex},
};
#[derive(Completer, Helper, Hinter, Validator)]
struct MyHelper(#[rustyline(Hinter)] HistoryHinter);
impl Highlighter for MyHelper {
fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
&'s self,
prompt: &'p str,
default: bool,
) -> Cow<'b, str> {
if default {
Owned(prompt.bright_black().bold().to_string())
} else {
Borrowed(prompt)
}
}
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
Owned(hint.bold().to_string())
}
}
#[derive(Clone)]
struct BacktickEventHandler {
toggle_state: Arc<Mutex<bool>>, // Tracks whether logging is enabled or disabled
}
impl ConditionalEventHandler for BacktickEventHandler {
fn handle(&self, evt: &Event, _: RepeatCount, _: bool, _: &EventContext) -> Option<Cmd> {
if let Some(k) = evt.get(0) {
if *k == KeyEvent::from('`') {
let mut state = self.toggle_state.lock().unwrap();
if *state {
LOGGER.write_to_stdout();
} else {
LOGGER.write_to_file("z.log");
}
*state = !*state;
Some(Cmd::Noop)
} else {
None
}
} else {
unreachable!()
}
}
}
fn register_commands() { fn register_commands() {
COMMAND_LIST.add_command( COMMAND_LIST.add_command(
@ -42,13 +100,10 @@ fn register_commands() {
None, None,
); );
// EXAMPLE // Example of adding aliases for commands
// Adding aliases for commands
COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string()); COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string());
} }
fn evaluate_command(input: &str) { fn evaluate_command(input: &str) {
if input.trim().is_empty() { if input.trim().is_empty() {
return; return;
@ -79,35 +134,43 @@ fn evaluate_command(input: &str) {
} }
} }
pub async fn handle_repl() -> rustyline::Result<()> { pub async fn handle_repl() -> Result<()> {
let mut line_editor = DefaultEditor::new()?; let mut rl = Editor::<MyHelper, DefaultHistory>::new()?;
if line_editor.load_history("history.txt").is_err() { rl.set_helper(Some(MyHelper(HistoryHinter::new())));
rl.bind_sequence(
KeyEvent::from('`'),
EventHandler::Conditional(Box::new(BacktickEventHandler {
toggle_state: Arc::new(Mutex::new(false)),
})),
);
if rl.load_history("history.txt").is_err() {
debug!("No previous history."); debug!("No previous history.");
} }
let time = Local::now().format("%H:%M:%S.%3f").to_string();
let prompt = format!("[{}/{}] {}", time,"SHELL", ">>\t");
register_commands(); register_commands();
loop { loop {
let sig = line_editor.readline( let time = Local::now().format("%H:%M:%S.%3f").to_string();
&prompt.bright_white() let prompt = format!("[{}/{}] {}", time, "SHELL", ">>\t");
); let sig = rl.readline(&prompt.bright_white());
match sig { match sig {
Ok(line) => { Ok(line) => {
line_editor.add_history_entry(line.as_str())?; rl.add_history_entry(line.as_str())?;
evaluate_command(line.as_str()); evaluate_command(line.as_str());
} }
Err(rustyline::error::ReadlineError::Interrupted) => { Err(ReadlineError::Interrupted) => {
println!("CTRL+C received, exiting..."); println!("CTRL+C received, exiting...");
std::process::exit(0); std::process::exit(0);
} }
Err(rustyline::error::ReadlineError::Eof) => { Err(ReadlineError::Eof) => {
println!("Error: CTRL+D pressed. Exiting..."); println!("Error: CTRL+D pressed. Exiting...");
std::process::exit(0); std::process::exit(0);
} }
Err(err) => { Err(err) => {
println!("Error: {}", err); println!("Error: {}", err);
} }
} }
} }

View file

@ -1,48 +1,27 @@
use anyhow::Result; use anyhow::Result;
use clap::Parser; use log::LevelFilter;
use log::{info, warn, LevelFilter};
pub mod core; pub mod core;
pub mod utils; pub mod utils;
use utils::{logger::LOGGER, splash::print_splash}; use utils::{logger::LOGGER, splash::print_splash};
#[derive(Parser)]
struct Cli {
#[arg(long, short, help = "Enable logging output")]
log: bool,
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let t = zephyr::add(0, 2); let t = zephyr::add(0, 2);
println!("{}", t); println!("{}", t);
let cli = Cli::parse();
log::set_logger(&*LOGGER).unwrap(); log::set_logger(&*LOGGER).unwrap();
log::set_max_level(LevelFilter::Debug); log::set_max_level(LevelFilter::Debug);
print_splash(); print_splash();
if cli.log { LOGGER.write_to_stdout();
info!("Initializing Engine with logging to stdout enabled");
core::init_renderer()?; let shell_thread = tokio::task::spawn(async { core::repl::repl::handle_repl().await });
} else {
LOGGER.write_to_stdout();
info!("Initializing Engine with logging to stdout disabled");
info!("Writing all logs to file z.log");
LOGGER.write_to_file("z.log"); core::init_renderer()?;
info!("Logging back to file z.log"); let _ = shell_thread.await?;
let shell_thread = tokio::task::spawn(async {
core::repl::repl::handle_repl().await
});
core::init_renderer()?;
shell_thread.await??; // LOL - Caz
}
Ok(()) Ok(())
} }

View file

@ -47,7 +47,12 @@ impl DynamicLogger {
impl Log for DynamicLogger { impl Log for DynamicLogger {
fn enabled(&self, metadata: &Metadata) -> bool { fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Debug let target = metadata.target();
let is_relevant_target = target.starts_with("wgpu")
|| target.starts_with("winit")
|| target.starts_with(env!("CARGO_PKG_NAME")); // Current crate name
is_relevant_target && metadata.level() <= Level::Debug
} }
fn log(&self, record: &Record) { fn log(&self, record: &Record) {