use std::io;
use tokio::io::AsyncWriteExt;
use tokio::process::ChildStdin;
use crate::cmd::{Cmd, IOTCM};
use crate::pos::InteractionPoint;
use crate::resp::{AgdaError, DisplayInfo, Resp};
use super::{send_command, AgdaRead};
pub struct ReplState {
pub stdin: ChildStdin,
pub agda: AgdaRead,
pub file: String,
pub(super) interaction_points: Vec<InteractionPoint>,
pub(super) iotcm: IOTCM,
}
pub type AgdaResult<T> = Result<T, String>;
pub type NextResult<T> = io::Result<AgdaResult<T>>;
pub fn preprint_agda_result<T>(t: AgdaResult<T>) -> Option<T> {
t.map_err(|e| eprintln!("Errors:\n{}", e)).ok()
}
impl ReplState {
pub fn print_goal_list(&self) {
let ips = self.interaction_points();
if ips.is_empty() {
println!("No goals, you're all set.");
}
for interaction_point in ips {
let range = &interaction_point.range;
debug_assert_eq!(range.len(), 1);
let interval = &range[0];
println!("?{} at line {}", interaction_point.id, interval.start.line)
}
}
pub async fn reload_file(&mut self) -> io::Result<()> {
self.command(Cmd::load_simple(self.file.clone())).await
}
pub async fn command(&mut self, cmd: Cmd) -> io::Result<()> {
self.iotcm.command = cmd;
send_command(&mut self.stdin, &self.iotcm).await
}
pub async fn command_raw(&mut self, raw_command: &str) -> io::Result<()> {
self.stdin.write(raw_command.as_bytes()).await?;
self.stdin.flush().await
}
pub async fn shutdown(&mut self) -> io::Result<()> {
self.stdin.shutdown().await
}
pub async fn next_display_info(&mut self) -> io::Result<DisplayInfo> {
loop {
match self.response().await? {
Resp::DisplayInfo { info: Some(info) } => break Ok(info),
_ => {}
}
}
}
pub fn interaction_points(&self) -> &[InteractionPoint] {
&self.interaction_points
}
pub async fn next_goals(&mut self) -> io::Result<()> {
use Resp::*;
self.interaction_points = loop {
match self.response().await? {
InteractionPoints { interaction_points } => break interaction_points,
_ => {}
}
};
Ok(())
}
pub async fn next_error(&mut self) -> io::Result<AgdaError> {
use crate::resp::DisplayInfo::Error as DisError;
loop {
match self.next_display_info().await? {
DisError(e) => break Ok(e),
_ => {}
}
}
}
}
macro_rules! next_resp_of {
($f:ident, $p:ident, $d:literal) => {
next_resp_of!($f, $p, crate::resp::$p, $d);
};
($f:ident, $p:ident, $t:ty, $d:literal) => {
impl ReplState {
#[doc($d)]
pub async fn $f(&mut self) -> NextResult<$t> {
loop {
match self.response().await? {
Resp::$p(ga) => break Ok(Ok(ga)),
Resp::DisplayInfo {
info: Some(crate::resp::DisplayInfo::Error(e)),
} => break Ok(e.into()),
_ => {}
}
}
}
}
};
}
next_resp_of!(next_give_action, GiveAction, "Skip until next give-action.");
next_resp_of!(next_make_case, MakeCase, "Skip until next make-case.");
next_resp_of!(
next_highlight,
HighlightingInfo,
"Skip until next highlight."
);
macro_rules! next_disp_of {
($f:ident, $p:ident, $d:literal) => {
next_disp_of!($f, $p, crate::resp::$p, $d);
};
($f:ident, $p:ident, $t:ty, $d:literal) => {
impl ReplState {
#[doc($d)]
pub async fn $f(&mut self) -> NextResult<$t> {
loop {
match self.next_display_info().await? {
DisplayInfo::Error(e) => break Ok(e.into()),
DisplayInfo::$p(agw) => break Ok(Ok(agw)),
_ => {}
}
}
}
}
};
}
next_disp_of!(
next_all_goals_warnings,
AllGoalsWarnings,
"Skip until next interaction point (goal) list."
);
next_disp_of!(
next_goal_specific,
GoalSpecific,
"Skip until next goal specific information."
);
next_disp_of!(
next_module_contents,
ModuleContents,
"Skip until next module contents response."
);
next_disp_of!(next_normal_form, NormalForm, "Skip until next normal form.");
next_disp_of!(next_context, Context, "Skip until next context.");
next_disp_of!(
next_inferred_type,
InferredType,
"Skip until next inferred type."
);