let () = Fmt.set_style_renderer Format.str_formatter `Ansi_tty module Make (S : Types.State) (E : Types.Exec) = struct module Eval = Eval.Make (S) (E) let pp_colored c pp fmt v = Fmt.pf fmt "%a" (Fmt.styled (`Fg c) pp) v let subst_tilde path = match Eunix.find_env "HOME" with | None -> path | Some home -> ( match Fpath.rem_prefix (Fpath.v home) path with | Some rel -> Fpath.(v "~" // rel) | None -> path) let default_prompt (ctx : Eval.ctx Exit.t) = let state = match ctx with | Exit.Zero ctx | Exit.Nonzero { value = ctx; _ } -> ctx.state in let pp_status ppf = function | Exit.Zero _ -> () | Exit.Nonzero { exit_code; _ } -> Fmt.pf ppf "[%a] " (pp_colored `Red Fmt.int) exit_code in Fmt.pf Format.str_formatter "%a%a:%s >\n%!" pp_status ctx Fmt.(pp_colored `Yellow string) (Eunix.get_user_and_host ()) (Fpath.normalize @@ S.cwd state |> subst_tilde |> Fpath.to_string); Format.flush_str_formatter () let _with_stdin_in_raw_mode fn = let saved_tio = Unix.tcgetattr Unix.stdin in let tio = { saved_tio with (* input modes *) c_ignpar = true; c_istrip = false; c_inlcr = false; c_igncr = false; c_ixon = false; (* c_ixany = false; *) (* c_iuclc = false; *) c_ixoff = false; (* output modes *) c_opost = true; (* control modes *) c_isig = false; c_icanon = false; c_echo = false; c_echoe = false; c_echok = false; c_echonl = false; (* c_iexten = false; *) (* special characters *) c_vmin = 1; c_vtime = 0; } in Unix.tcsetattr Unix.stdin TCSADRAIN tio; Fun.protect ~finally:(fun () -> Unix.tcsetattr Unix.stdin TCSADRAIN saved_tio) fn let run ?(prompt = default_prompt) initial_ctx = let rec loop (ctx : Eval.ctx Exit.t) = let p = prompt ctx in Fmt.pr "%s\r%!" p; match LNoise.linenoise "" with | None -> Fmt.pr "exit\n%!"; exit 0 | Some c -> let ast = Ast.of_string c in let ctx', _ast = Eval.run ctx ast in (* TODO: Make better History abstraction *) let _ : (unit, string) result = LNoise.history_add c in loop ctx' | exception Sys.Break -> let c = Exit.value ctx in loop (Exit.nonzero c 130) in loop initial_ctx end