import unicode import strutils import terminal import strformat import posix import keycodes type EditorState* = ref object # config prompt: string # prompt to display maxRowsGoal: int # if terminal resizes, how to compute max rows # positive: it will set max rows to maxRowsGoal, or terminal height, whichever is lower # negative: it will set it to terminal height - maxRowsGoal or terminal height, whichever is lower # 0: terminal height # state reached during execution x: int # current cursor x position on screen bx: int # current x position in buffer (not same as x due to UTF-8) y: int # which row are we editing size: int # size of the row being edited in characters buffer: seq[string] # current text history: seq[seq[string]] # past texts historyIndex: int # where are we in history cols: int # number of columns in the terminal maxRows: int # max number of rows allowed # for editors var editors: seq[EditorState] # resize support onSignal(28): discard proc newEditor*(prompt: string = "> ", multiline: bool = true): EditorState = new(result) result.prompt = prompt result.maxRowsGoal = if multiline: 0 else: 1 result.buffer.add("") 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 render(ed: EditorState) = eraseLine() setCursorXPos(0) write stdout, ed.prompt write stdout, ed.buffer[ed.y] template cline(ed: EditorState): var string = ed.buffer[ed.y] proc read*(ed: EditorState): string = ed.render() while true: let key = getKey() case key: of jkEnter.int: break of jkBackspace.int: if ed.x == 0: discard # merge two lines TODO elif ed.x > ed.cline().high(): ed.cline() = ed.cline[0..ed.cline().high()-1] dec ed.x dec ed.bx else: # TODO UTF8 ed.cline() = ed.cline[0..ed.x-1] & ed.cline[ed.x+1..ed.cline().high()] dec ed.x dec ed.bx else: if key > 31 and key < 127: # ascii char let ch = key.char() if ed.x == 0: ed.cline() = $ch & ed.cline elif ed.x > ed.cline.high(): ed.cline() = ed.cline & $ch else: ed.cline() = ed.cline[0..ed.x-1] & $ch & ed.cline[ed.x..ed.cline().high()] inc ed.x inc ed.bx ed.render() # return val result = ed.buffer.join("\n") # cleanup write(stdout, "\n") ed.history.add(ed.buffer) ed.buffer = @[""] ed.y = 0 ed.x = 0