# tty layer as opposed to a future window management server bullshit thingy # this is just tty0 it adds nice shit like ^B and stuff # also has history and shit global tty = { line = "", cursor = 0, esc = 0, history = {}, hist_index = 0, fn back(self, n) do let i = 0 while (i < n) do puts("\x08") i += 1 end end, fn blanks(self, n) do let i = 0 while (i < n) do puts(" ") i += 1 end end, fn insert(self, character) do const pre = self.line[0..self.cursor] const post = self.line[self.cursor..] self.line = pre ~ character ~ post puts(character ~ post) self:back(post:len()) self.cursor += 1 end, fn backspace(self) do if (self.cursor == 0) return :nil const pre = self.line[0..(self.cursor - 1)] const post = self.line[self.cursor..] self.line = pre ~ post self.cursor -= 1 puts("\x08") puts(post ~ " ") self:back(post:len() + 1) end, fn move_left(self) do if self.cursor > 0 do puts("\x08") self.cursor -= 1 end end, fn move_right(self) do if self.cursor < self.line:len() do puts(self.line[self.cursor]) self.cursor += 1 end end, fn home(self) do self:back(self.cursor) self.cursor = 0 end, fn to_end(self) do puts(self.line[self.cursor..]) self.cursor = self.line:len() end, fn kill(self) do self:back(self.cursor) self:blanks(self.line:len()) self:back(self.line:len()) self.line = "" self.cursor = 0 end, fn erase_word(self) do if (self.cursor == 0) return :nil let at = self.cursor while (at > 0 and self.line[at - 1] == " ") at -= 1 while (at > 0 and self.line[at - 1] != " ") at -= 1 let count = self.cursor - at while (count > 0) do self:backspace() count -= 1 end end, fn replace_line(self, text) do self:kill() self.line = text puts(text) self.cursor = text:len() end, fn history_prev(self) do if (self.history:len() == 0) return :nil if (self.hist_index > 0) self.hist_index -= 1 self:replace_line(self.history[self.hist_index]) end, fn history_next(self) do if (self.history:len() == 0) return :nil if self.hist_index < self.history:len() - 1 do self.hist_index += 1 self:replace_line(self.history[self.hist_index]) end else do self.hist_index = self.history:len() self:replace_line("") end end, fn submit(self) do puts("\n") const entered = self.line if (entered:len() > 0) self.history:push(entered) self.hist_index = self.history:len() self.line = "" self.cursor = 0 run(entered) end, fn cancel(self) do puts("^C\n") self.line = "" self.cursor = 0 self.hist_index = self.history:len() puts(prompt()) end, fn clear_screen(self) do console:clear() puts(prompt()) puts(self.line) self:back(self.line:len() - self.cursor) end, fn feed(self, byte) do if (stdin_deliver(byte)) return :nil if self.esc == 1 do if byte == 91 do self.esc = 2 end else do self.esc = 0 end return :nil end if self.esc == 2 do self.esc = 0 match byte | 65 => self:history_prev() # up | 66 => self:history_next() # down | 67 => self:move_right() # right | 68 => self:move_left() # left | 72 => self:home() # home | 70 => self:to_end() # end | _ => :nil return :nil end # ESC, start a sequence if byte == 27 do self.esc = 1 return :nil end match byte | 10 => self:submit() # enter | 8 => self:backspace() # bs | 127 => self:erase_word() # del (ctrl-bs) | 1 => self:home() # ctrl-a | 5 => self:to_end() # ctrl-e | 3 => self:cancel() # ctrl-c | 12 => self:clear_screen() # ctrl-l | 21 => self:kill() # ctrl-u | 23 => self:erase_word() # ctrl-w | _ => do # ctrl-d and other shits if (byte < 32) return :nil if (byte > 126) return :nil self:insert(string.of_ascii(byte)) end end, }