use std::cell::RefCell;
use praxis_debugger::repl::{Control, StoppedHost};
use praxis_runtime::{BreakpointStop, Resume};
use crate::color_mode::ColorMode;
use crate::debug_mode::DebugMode;
thread_local! {
static HOST: RefCell<Option<HostState>> = const { RefCell::new(None) };
}
struct HostState {
db: praxis_typeck::TypeDb,
source_text: String,
source_name: String,
color: ColorMode,
prompt: bool,
}
pub fn install(
db: &praxis_typeck::TypeDb,
source_text: &str,
source_name: &str,
debug: DebugMode,
color: ColorMode,
) {
if debug == DebugMode::Never {
return;
}
HOST.with(|slot| {
*slot.borrow_mut() = Some(HostState {
db: db.clone(),
source_text: source_text.to_string(),
source_name: source_name.to_string(),
color,
prompt: debug.wants_repl(),
})
});
praxis_runtime::install_breakpoint_handler(handle);
}
pub fn disarm() {
praxis_runtime::clear_breakpoint_handler();
HOST.with(|slot| *slot.borrow_mut() = None);
}
fn handle(stop: BreakpointStop) -> Resume {
HOST.with(|slot| {
let borrow = slot.borrow();
let Some(state) = borrow.as_ref() else {
return Resume::Continue;
};
if !state.prompt {
report(&stop, state);
return Resume::Continue;
}
if praxis_debugger::tui::should_use_tui() {
show_tui(stop, state)
} else {
show_prompt(stop, state)
}
})
}
fn report(stop: &BreakpointStop, state: &HostState) {
use std::io::Write;
let _ = std::io::stdout().flush();
let ctx = praxis_debugger::render::RenderCtx::new(&state.db, &state.source_text);
let _ = praxis_debugger::render::render_breakpoint_stop(
&mut std::io::stderr(),
&stop.frames,
stop.span,
stop.hits,
state.color.palette(),
&ctx,
);
}
fn show_tui(stop: BreakpointStop, state: &HostState) -> Resume {
use std::io::Write;
let _ = std::io::stdout().flush();
let span = stop.span;
let repl = stopped_repl(stop, state);
let tui = praxis_debugger::tui::Tui::new(repl, praxis_debugger::tui::Stop::Breakpoint);
match praxis_debugger::tui::run(tui) {
Ok((_, control)) => resume_for(control),
Err(err) => {
let _ = writeln!(
std::io::stderr(),
"error: could not open the debugger at the breakpoint \
(source bytes {}..{}): {err}",
span.0,
span.1
);
Resume::Continue
}
}
}
fn show_prompt(stop: BreakpointStop, state: &HostState) -> Resume {
use std::io::Write;
let _ = std::io::stdout().flush();
let mut repl = stopped_repl(stop, state);
let stdin = std::io::stdin();
let mut stdin = stdin.lock();
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
let control = repl.run(&mut stdin, &mut stderr);
let _ = stderr.flush();
resume_for(control)
}
fn stopped_repl(stop: BreakpointStop, state: &HostState) -> praxis_debugger::repl::Repl {
let host = StoppedHost {
db: state.db.clone(),
source_text: state.source_text.clone(),
source_name: state.source_name.clone(),
hits: stop.hits,
span: stop.span,
};
praxis_debugger::repl::Repl::new_stopped(stop.frames, host)
}
fn resume_for(control: Control) -> Resume {
match control {
Control::Quit => Resume::Detach,
Control::Continue | Control::Resume => Resume::Continue,
}
}