# 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, back = fn(self, n) do let i = 0 while i < n do puts("\x08") i += 1 end end, blanks = fn(self, n) do let i = 0 while i < n do puts(" ") i += 1 end end, insert = fn(self, ch) do const pre = self.line[0..self.cursor] const post = self.line[self.cursor..] self.line = pre ~ ch ~ post puts(ch ~ post) self:back(post:len()) self.cursor += 1 end, backspace = fn(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, move_left = fn(self) do if self.cursor > 0 do puts("\x08") self.cursor -= 1 end end, move_right = fn(self) do if self.cursor < self.line:len() do puts(self.line[self.cursor]) self.cursor += 1 end end, home = fn(self) do self:back(self.cursor) self.cursor = 0 end, to_end = fn(self) do puts(self.line[self.cursor..]) self.cursor = self.line:len() end, kill = fn(self) do self:back(self.cursor) self:blanks(self.line:len()) self:back(self.line:len()) self.line = "" self.cursor = 0 end, erase_word = fn(self) do if self.cursor == 0 return :nil let at = self.cursor while (at > 0 and self.line[at - 1] == " ") do at -= 1 end while (at > 0 and self.line[at - 1] != " ") do at -= 1 end let count = self.cursor - at while count > 0 do self:backspace() count -= 1 end end, replace_line = fn(self, text) do self:kill() self.line = text puts(text) self.cursor = text:len() end, history_prev = fn(self) do if self.history:len() == 0 return :nil if self.hist_index > 0 do self.hist_index -= 1 end self:replace_line(self.history[self.hist_index]) end, history_next = fn(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, submit = fn(self) do puts("\n") const entered = self.line if entered:len() > 0 do self.history:push(entered) end self.hist_index = self.history:len() self.line = "" self.cursor = 0 run(entered) end, cancel = fn(self) do puts("^C\n") self.line = "" self.cursor = 0 self.hist_index = self.history:len() puts(prompt()) end, clear_screen = fn(self) do console:clear() puts(prompt()) puts(self.line) self:back(self.line:len() - self.cursor) end, feed = fn(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 if byte == 65 do self:history_prev() end # up else if byte == 66 do self:history_next() end # down else if byte == 67 do self:move_right() end # right else if byte == 68 do self:move_left() end # left else if byte == 72 do self:home() end # home else if byte == 70 do self:to_end() end # end return :nil end if byte == 27 do self.esc = 1 return :nil end # ESC, start a sequence if byte == 10 do self:submit() return :nil end # enter if byte == 8 or byte == 127 do self:backspace() return :nil end # bs / del if byte == 1 do self:home() return :nil end # ctrl-a if byte == 5 do self:to_end() return :nil end # ctrl-e if byte == 3 do self:cancel() return :nil end # ctrl-c if byte == 12 do self:clear_screen() return :nil end # ctrl-l if byte == 21 do self:kill() return :nil end # ctrl-u if byte == 23 do self:erase_word() return :nil end # ctrl-w if byte < 32 return :nil # ctrl-d and other ctrls: ignore if byte > 126 return :nil self:insert(string.of_ascii(byte)) end, }