mod input;
mod ipc;
mod lifecycle;
mod prompt_kind;
#[cfg(test)]
mod tests;
use super::state::ReplState;
use super::{REPL_STATE, RPrompt, arf_eprintln};
use crate::history::HistoryRuntime;
use input::{handle_ctrl_c, handle_ctrl_d, handle_line};
use ipc::{dispatch_pre_prompt_ipc, handle_external_break};
use lifecycle::{finish_previous_command, initialize_error_handler_once};
use prompt_kind::{
PromptKind, classify_prompt, should_process_pending_help_requests, should_warn_prompt_ambiguity,
};
use reedline::Signal;
use std::ops::ControlFlow;
pub(super) fn read_console_callback(
r_prompt: &str,
prompt_info: arf_libr::ReadConsolePromptInfo,
) -> Option<String> {
REPL_STATE.with(|state| {
let mut guard = match state.try_borrow_mut() {
Ok(guard) => guard,
Err(_) => {
return None;
}
};
let state = guard.as_mut()?;
if state.should_exit {
return None;
}
if should_warn_prompt_ambiguity(
&mut state.r_prompt_options_ambiguous,
prompt_info.options_are_ambiguous,
) {
arf_eprintln!(
r#"Warning: options("prompt") and options("continue") are identical; arf cannot distinguish top-level and continuation prompts."#
);
}
let prompt_kind = classify_prompt(r_prompt, prompt_info.is_continuation);
initialize_error_handler_once(state, prompt_kind);
crate::ipc::set_r_at_prompt(prompt_kind.is_command());
finish_previous_command(state, prompt_kind);
if should_process_pending_help_requests(prompt_kind, state.prompt_config.is_shell_enabled())
{
super::pager_ui::process_pending_help_requests();
}
if let Some(code) = dispatch_pre_prompt_ipc(state, prompt_kind) {
return Some(code);
}
read_input_loop(state, r_prompt, prompt_kind)
})
}
fn build_prompt(state: &ReplState, r_prompt: &str, prompt_kind: PromptKind) -> RPrompt {
if prompt_kind.is_continuation() {
state.prompt_config.build_cont_prompt(state.reprex.mode)
} else if prompt_kind.is_command() {
state.prompt_config.build_main_prompt(state.reprex.mode)
} else {
RPrompt::new(r_prompt.to_string(), r_prompt.to_string())
}
}
fn read_input_loop(
state: &mut ReplState,
r_prompt: &str,
prompt_kind: PromptKind,
) -> Option<String> {
loop {
let prompt = build_prompt(state, r_prompt, prompt_kind);
let is_shell_mode = state.prompt_config.is_shell_enabled();
let history_handle: HistoryRuntime = if is_shell_mode {
state.shell_history.clone()
} else {
state.r_history.clone()
};
let editor = if is_shell_mode {
&mut state.shell_line_editor
} else {
&mut state.line_editor
};
arf_libr::process_r_events();
let read_result = editor.read_line(&prompt);
if read_result.is_ok()
&& let Err(error) = crate::console_mode::handoff_to_reedline()
{
eprintln!("Error: {}", error);
state.should_exit = true;
return None;
}
let step = match read_result {
Ok(Signal::Success(line)) => {
handle_line(state, &history_handle, prompt_kind, is_shell_mode, line)
}
Ok(Signal::CtrlC) => handle_ctrl_c(state),
Ok(Signal::CtrlD) => handle_ctrl_d(state),
Ok(Signal::ExternalBreak(buffer)) => {
handle_external_break(state, &history_handle, is_shell_mode, &buffer)
}
Ok(_) => ControlFlow::Continue(()),
Err(err) => {
eprintln!("Error: {}", err);
state.should_exit = true;
return None;
}
};
if let ControlFlow::Break(result) = step {
return result;
}
}
}