import gleam/erlang/atom import gleam/list pub type Key { Char(String) Left Right Up Down Continue Empty Unknown } pub const esc = "\u{001B}" // control sequence introducer pub const csi = "[" pub fn from_list(l: List(String)) -> Key { case l { [e, c, "D"] if e == esc && c == csi -> Left [e, c, "C"] if e == esc && c == csi -> Right [e, c, "A"] if e == esc && c == csi -> Up [e, c, "B"] if e == esc && c == csi -> Down [e, c] if e == esc && c == csi -> Continue [e] if e == esc -> Continue [char] -> Char(char) [] -> Continue _ -> Unknown } } pub fn start_raw_shell() { let no_shell = atom.create("noshell") let raw = atom.create("raw") shell_start_interactive(#(no_shell, raw)) } // TODO map key to something like Control, to not leak `Continue` etc. pub fn read_input_until_key(l: List(String)) -> Key { let l = read_input() |> list.wrap |> list.append(l, _) case from_list(l) { Continue -> read_input_until_key(l) k -> k } } fn read_input() -> String { io_get_chars("", 1) } pub type NotUsed // https://www.erlang.org/doc/apps/stdlib/shell.html#start_interactive/1 @external(erlang, "shell", "start_interactive") fn shell_start_interactive(options: #(atom.Atom, atom.Atom)) -> NotUsed // https://www.erlang.org/doc/apps/stdlib/io.html#get_line/1 @external(erlang, "io", "get_chars") fn io_get_chars(prompt: String, count: Int) -> String