import posix import unicode import terminalUtils/buffer import terminalUtils/terminalGetInfo import terminalUtils/keycodes import textBuffer import renderer type EditorState* = ref object # config prompt: string # prompt to display textBuffer: TextBuffer # current text termBuffer: Buffer history: seq[TextBuffer] # past texts historyIndex: int # where are we in history EditorResult* = enum erEnter, erCtrlC, erCtrlD, erError # for editors var editors: seq[EditorState] # resize support onSignal(28): discard proc newEditor*(prompt: string = "> "): EditorState = new(result) result.prompt = prompt editors.add(result) proc destroyEditor*(oldEditor: EditorState) = for i in 0..editors.high(): let ed = editors[i] if ed == oldEditor: editors.del(i) return proc read*(ed: EditorState): (EditorResult, string) = var editorResult = erError let (_, cursorY) = termGetCursorPos(stdout) ed.termBuffer = newBuffer(0, cursorY, termGetWidth(), 1, stdout) ed.textBuffer = newTextBuffer() ed.history.add(ed.textBuffer) ed.historyIndex = ed.history.high() var scroll: Scroll = new(Scroll) template moveInHistory(delta: int) = let newHistoryIndex = min(max(ed.historyIndex + delta, 0), ed.history.high()) if newHistoryIndex != ed.historyIndex: ed.textBuffer = ed.history[newHistoryIndex] scroll.reset() ed.historyIndex = newHistoryIndex while true: render(ed.textBuffer, ed.termBuffer, ed.prompt, scroll) let (getKeyResult, key) = getKey() case getKeyResult: of gkChar: ed.textBuffer.insertRune(key.uint32().Rune()) of gkControl: let control = key.JaleKeycode() case control: of jkEnter: if ed.textBuffer.isLineEmpty() and ed.textBuffer.isLastLine(): ed.textBuffer.stripFinalNewline() editorResult = erEnter break else: ed.textBuffer.enter() of {jkBackspace, jkAltBackspace}: ed.textBuffer.backspace() of jkDelete: ed.textBuffer.delete() of jkCtrlC: # ctrl+c editorResult = erCtrlC break of jkCtrlD: # ctrl+d if ed.textBuffer.isEmpty(): editorResult = erCtrlD break of jkLeft: ed.textBuffer.moveCursor(-1, 0) of jkRight: ed.textBuffer.moveCursor(1, 0) of jkDown: if ed.textBuffer.isLastLine(): moveInHistory(1) else: ed.textBuffer.moveCursor(0, 1) of jkUp: if ed.textBuffer.isFistLine(): moveInHistory(-1) else: ed.textBuffer.moveCursor(0, -1) of jkEnd: ed.textBuffer.moveToEnd(true) of jkHome: ed.textBuffer.moveToEnd(false) of jkPageDown: let termHeight = termGetHeight() ed.textBuffer.moveCursor(0, termHeight div 2) of jkPageUp: let termHeight = termGetHeight() ed.textBuffer.moveCursor(0, -(termHeight div 2)) of jkCtrlDown: ed.textBuffer.newLine() else: discard # not implemented # return val, strip final newline (due to how double enter is how you enter) let cont = ed.textBuffer.getContent() result = (editorResult, cont) # cleanup stdout.write("\n") # don't add empty lines to history if ed.history[ed.history.high()].getContent() == "": ed.history.del(ed.history.high())