mirror of
https://github.com/lune-org/lune.git
synced 2025-04-21 12:24:03 +01:00
100 lines
3 KiB
Rust
100 lines
3 KiB
Rust
use std::{path::PathBuf, process::ExitCode};
|
|
|
|
use anyhow::{Context, Result};
|
|
use directories::UserDirs;
|
|
use rustyline::{error::ReadlineError, DefaultEditor};
|
|
|
|
use lune::Lune;
|
|
|
|
const MESSAGE_WELCOME: &str = concat!("Lune v", env!("CARGO_PKG_VERSION"));
|
|
const MESSAGE_INTERRUPT: &str = "Interrupt: ^C again to exit";
|
|
|
|
enum PromptState {
|
|
Regular,
|
|
Continuation,
|
|
}
|
|
|
|
pub async fn show_interface() -> Result<ExitCode> {
|
|
println!("{MESSAGE_WELCOME}");
|
|
|
|
let history_file_path: &PathBuf = &UserDirs::new()
|
|
.context("Failed to find user home directory")?
|
|
.home_dir()
|
|
.join(".lune_history");
|
|
if !history_file_path.exists() {
|
|
tokio::fs::write(history_file_path, &[]).await?;
|
|
}
|
|
|
|
let mut repl = DefaultEditor::new()?;
|
|
repl.load_history(history_file_path)?;
|
|
|
|
let mut interrupt_counter = 0;
|
|
let mut prompt_state = PromptState::Regular;
|
|
let mut source_code = String::new();
|
|
|
|
let mut lune_instance = Lune::new();
|
|
|
|
loop {
|
|
let prompt = match prompt_state {
|
|
PromptState::Regular => "> ",
|
|
PromptState::Continuation => ">> ",
|
|
};
|
|
|
|
match repl.readline(prompt) {
|
|
Ok(code) => {
|
|
interrupt_counter = 0;
|
|
|
|
// TODO: Should we add history entries for each separate line?
|
|
// Or should we add and save history only when we have complete
|
|
// lua input that may or may not be multiple lines long?
|
|
repl.add_history_entry(&code)?;
|
|
repl.save_history(history_file_path)?;
|
|
|
|
match prompt_state {
|
|
PromptState::Regular => source_code = code,
|
|
PromptState::Continuation => source_code.push_str(&code),
|
|
}
|
|
}
|
|
|
|
Err(ReadlineError::Eof) => break,
|
|
Err(ReadlineError::Interrupted) => {
|
|
interrupt_counter += 1;
|
|
|
|
// NOTE: We actually want the user to do ^C twice to exit,
|
|
// and if we get an interrupt we should continue to the next
|
|
// readline loop iteration so we don't run input code twice
|
|
if interrupt_counter == 1 {
|
|
println!("{MESSAGE_INTERRUPT}");
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Err(err) => {
|
|
eprintln!("REPL ERROR: {err}");
|
|
return Ok(ExitCode::FAILURE);
|
|
}
|
|
};
|
|
|
|
// TODO: Preserve context here somehow?
|
|
let eval_result = lune_instance.run("REPL", &source_code).await;
|
|
|
|
match eval_result {
|
|
Ok(_) => prompt_state = PromptState::Regular,
|
|
|
|
Err(err) => {
|
|
if err.is_incomplete_input() {
|
|
prompt_state = PromptState::Continuation;
|
|
source_code.push('\n');
|
|
} else {
|
|
eprintln!("{err}");
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
repl.save_history(history_file_path)?;
|
|
|
|
Ok(ExitCode::SUCCESS)
|
|
}
|