use super::lifecycle::record_formatter_failure;
use super::prompt_kind::PromptKind;
use crate::history::HistoryRuntime;
use crate::repl::history::finalize_history;
use crate::repl::meta_command::{
MetaAction, MetaCommandContext, SessionInfoContext, dir_command_hint,
handle_meta_command_result, process_meta_command_with_ipc_expose,
};
use crate::repl::state::{PendingHistoryContext, ReplState};
use crate::repl::{arf_println, clear_input_lines, execute_shell_command, strip_reprex_output};
use crossterm::{
ExecutableCommand,
terminal::{self, ClearType},
};
use std::io;
use std::ops::ControlFlow;
pub(super) fn handle_line(
state: &mut ReplState,
history_handle: &HistoryRuntime,
prompt_kind: PromptKind,
is_shell_mode: bool,
line: String,
) -> ControlFlow<Option<String>> {
let save_outcome = history_handle.receipt_outcome();
if prompt_kind.is_other() {
finalize_history(Some(history_handle), save_outcome, false);
return ControlFlow::Break(Some(line));
}
let mut meta_ctx = MetaCommandContext {
prompt_config: &mut state.prompt_config,
reprex: &mut state.reprex,
r_history: &state.r_history,
shell_history: &state.shell_history,
r_source_status: &state.r_source_status,
dir_stack: &mut state.dir_stack,
history_session_id: state.history_session_id.map(i64::from),
r_home: state.r_home.as_deref(),
};
if let Some(result) = process_meta_command_with_ipc_expose(
&line,
&mut meta_ctx,
prompt_kind.is_command() && !is_shell_mode,
) {
finalize_history(Some(history_handle), save_outcome, true);
state.prompt_config.clear_command_duration();
let ctx = SessionInfoContext {
prompt_config: &state.prompt_config,
reprex: &state.reprex,
config_file_info: &state.config_file_info,
history_location: &state.history_location,
r_history: &state.r_history,
shell_history: &state.shell_history,
r_source_status: &state.r_source_status,
};
return match handle_meta_command_result(result, &ctx) {
MetaAction::Continue => ControlFlow::Continue(()),
MetaAction::Exit => {
state.should_exit = true;
ControlFlow::Break(None)
}
};
}
finalize_history(Some(history_handle), save_outcome, false);
if is_shell_mode {
let trimmed = line.trim();
if !trimmed.is_empty() {
if trimmed == "exit" || trimmed == "logout" {
state.prompt_config.set_shell(false);
arf_println!("Returned to R mode.");
return ControlFlow::Continue(());
}
if let Some(hint) = dir_command_hint(trimmed) {
arf_println!("{}", hint);
}
execute_shell_command(trimmed);
}
return ControlFlow::Continue(());
}
let (original_line, line) = if state.reprex.is_enabled() {
(line.clone(), strip_reprex_output(&line))
} else {
(line.clone(), line)
};
let code = match state.reprex.maybe_format_code(&line) {
Ok(code) => code,
Err(error) => {
arf_println!("{}", error);
record_formatter_failure(state, prompt_kind, history_handle, save_outcome);
return ControlFlow::Continue(());
}
};
if state.reprex.is_enabled() && !code.is_empty() {
clear_input_lines(&original_line, &code);
}
if !code.is_empty() {
state.prompt_config.set_command_start();
state.prompt_config.start_spinner();
}
crate::ipc::set_r_at_prompt(false);
if prompt_kind.is_command() && !code.trim().is_empty() {
if crate::ipc::current_execution_id().is_none() {
crate::ipc::begin_terminal_execution(&code);
}
let history_id = save_outcome.and_then(crate::history::HistorySaveOutcome::saved_id);
state.pending_history_context = PendingHistoryContext::Command {
store: history_handle.store(),
history_id,
};
}
ControlFlow::Break(Some(code))
}
fn leave_shell_mode(state: &mut ReplState) -> bool {
if state.prompt_config.is_shell_enabled() {
state.prompt_config.set_shell(false);
arf_println!("Returned to R mode.");
true
} else {
false
}
}
pub(super) fn handle_ctrl_c(state: &mut ReplState) -> ControlFlow<Option<String>> {
let _ = io::stdout().execute(terminal::Clear(ClearType::FromCursorDown));
println!("^C");
if leave_shell_mode(state) {
return ControlFlow::Continue(());
}
ControlFlow::Break(Some(String::new()))
}
pub(super) fn handle_ctrl_d(state: &mut ReplState) -> ControlFlow<Option<String>> {
let _ = io::stdout().execute(terminal::Clear(ClearType::FromCursorDown));
if leave_shell_mode(state) {
return ControlFlow::Continue(());
}
state.should_exit = true;
ControlFlow::Break(None)
}