diff --git a/editor b/editor new file mode 100755 index 0000000..d846a13 Binary files /dev/null and b/editor differ diff --git a/editor.nim b/editor.nim index 8d6c88d..c9e98d2 100644 --- a/editor.nim +++ b/editor.nim @@ -1,12 +1,11 @@ import unicode import strutils -import terminal import strformat import posix -import termBuffer/buffer - -import keycodes +import terminalUtils/buffer +import terminalUtils/terminalGetInfo +import terminalUtils/keycodes type EditorState* = ref object @@ -44,7 +43,6 @@ proc newEditor*(prompt: string = "> ", multiline: bool = true): EditorState = result.prompt = prompt result.maxRowsGoal = if multiline: 0 else: 1 result.buffer.add("") - result.screenBuffer = newBuffer(0, 0, terminalWidth(), terminalHeight(), stdout) editors.add(result) proc destroyEditor*(oldEditor: EditorState) = @@ -63,13 +61,11 @@ proc render(ed: EditorState) = template cline(ed: EditorState): var string = ed.buffer[ed.y] -proc print*(ed: EditorState, text: string) = - ed.screenBuffer.print(text) - ed.screenBuffer.redraw() - proc read*(ed: EditorState): (EditorResult, string) = var editorResult = erError + let (_, cursorY) = termGetCursorPos(stdout) + ed.screenBuffer = newBuffer(0, cursorY, termGetWidth(), termGetHeight() - cursorY, stdout) ed.render() while true: @@ -126,8 +122,10 @@ proc read*(ed: EditorState): (EditorResult, string) = result = (editorResult, ed.buffer.join("\n")) # cleanup + stdout.write("\n") ed.history.add(ed.buffer) ed.buffer = @[""] ed.y = 0 ed.x = 0 + ed.screenBuffer = nil \ No newline at end of file diff --git a/example.nim b/example.nim index 821a60a..3e0178d 100644 --- a/example.nim +++ b/example.nim @@ -1,10 +1,10 @@ import editor -let e = newEditor("> ", false) +let e = newEditor(">>> ", false) while true: let (res, text) = e.read() if res in {erCtrlC, erCtrlD} or text == "quit": - e.print("") break - e.print(text) \ No newline at end of file + if text.len() > 0: + echo text \ No newline at end of file diff --git a/termBuffer/escapeSequences.nim b/termBuffer/escapeSequences.nim deleted file mode 100644 index c02a70c..0000000 --- a/termBuffer/escapeSequences.nim +++ /dev/null @@ -1,14 +0,0 @@ -# list of functions to generate escape sequences - -import strformat -import strutils - -func escCursorPos*(x, y: int): string = - &"\e[{y+1};{x+1}H" - -func escAttributes*(attributes: seq[uint8]): string = - let joined = attributes.join(";") - &"\e[{joined}m" - -func escEraseCharacters*(n: int): string = - &"\e[{n}X" \ No newline at end of file diff --git a/termBuffer/buffer.nim b/terminalUtils/buffer.nim similarity index 89% rename from termBuffer/buffer.nim rename to terminalUtils/buffer.nim index e1535c9..3889f83 100644 --- a/termBuffer/buffer.nim +++ b/terminalUtils/buffer.nim @@ -100,22 +100,7 @@ proc write*(buf: Buffer, text: string) = for i in fromPos..toPos: buf.buffered[i] = Cell(text: runes[i-fromPos], attributes: buf.cursorAttributes) - buf.bufferX += runes.len() - -proc print*(buf: Buffer, text: string) = - # write, but makes sure it's on its own line - if buf.bufferX > 0: - # TODO scroll implementation - buf.bufferX = 0 - if buf.bufferY < buf.height - 1: - buf.bufferY += 1 - - buf.write(text) - - # TODO scroll implementation - if text.len() > 0 and buf.bufferY < buf.height - 1: - buf.bufferY += 1 - + buf.bufferX += runes.len() proc redraw*(buf: Buffer, force: bool = false) = var toPrint = "" @@ -130,7 +115,7 @@ proc redraw*(buf: Buffer, force: bool = false) = let y = i div buf.width let x = i mod buf.width # go to given place - toPrint &= escCursorPos(x+buf.positionX, y+buf.positionY) + toPrint &= escSetCursorPos(x+buf.positionX, y+buf.positionY) # get the right attributes # first reset @@ -149,7 +134,7 @@ proc redraw*(buf: Buffer, force: bool = false) = # finish toPrint &= escAttributes(@[fsReset.uint8]) - toPrint &= escCursorPos(buf.bufferX + buf.positionX, buf.bufferY + buf.positionY) + toPrint &= escSetCursorPos(buf.bufferX + buf.positionX, buf.bufferY + buf.positionY) buf.stdout.write(toPrint) @@ -157,6 +142,7 @@ func getSize*(buf: Buffer): (int, int) = (buf.width, buf.height) proc resize*(buf: Buffer, newX, newY: int) = + # TODO raise newException(Defect, "Not implemented") func getPosition*(buf: Buffer): (int, int) = diff --git a/terminalUtils/escapeSequences.nim b/terminalUtils/escapeSequences.nim new file mode 100644 index 0000000..2fa11d6 --- /dev/null +++ b/terminalUtils/escapeSequences.nim @@ -0,0 +1,45 @@ +# list of functions to generate escape sequences +# in the future, this can be expanded to support more platforms + +import strformat +import strutils +import terminal + +func escSetCursorPos*(x, y: int): string = + &"\e[{y+1};{x+1}H" + +const escGetCursorPos* = "\e[6n" + +type InvalidResponseError* = object of CatchableError + +proc termGetCursorPos*(ouput: File): (int, int) = + + ouput.write(escGetCursorPos) + + var response = "" + if getch() != '\e': + raise newException(InvalidResponseError, "Unsupported terminal - can't get cursor position.") + if getch() != '[': + raise newException(InvalidResponseError, "Can't parse response in getCursorPos") + var newChar = getch() + while newChar != ';': + response &= newChar + newChar = getch() + let y = parseInt(response) - 1 + response = "" + newChar = getch() + while newChar != 'R': + response &= newChar + newChar = getch() + let x = parseInt(response) - 1 + + return (x, y) + + + +func escAttributes*(attributes: seq[uint8]): string = + let joined = attributes.join(";") + &"\e[{joined}m" + +func escEraseCharacters*(n: int): string = + &"\e[{n}X" \ No newline at end of file diff --git a/keycodes.nim b/terminalUtils/keycodes.nim similarity index 100% rename from keycodes.nim rename to terminalUtils/keycodes.nim diff --git a/terminalUtils/terminalGetInfo.nim b/terminalUtils/terminalGetInfo.nim new file mode 100644 index 0000000..886c838 --- /dev/null +++ b/terminalUtils/terminalGetInfo.nim @@ -0,0 +1,34 @@ +import terminal +import strutils + +import escapeSequences + +proc termGetCursorPos*(ouput: File): (int, int) = + + ouput.write(escGetCursorPos) + + var response = "" + if getch() != '\e': + raise newException(InvalidResponseError, "Unsupported terminal - can't get cursor position.") + if getch() != '[': + raise newException(InvalidResponseError, "Can't parse response in getCursorPos") + var newChar = getch() + while newChar != ';': + response &= newChar + newChar = getch() + let y = parseInt(response) - 1 + response = "" + newChar = getch() + while newChar != 'R': + response &= newChar + newChar = getch() + let x = parseInt(response) - 1 + + return (x, y) + +proc termGetWidth*: int = + terminalWidth() + +proc termGetHeight*: int = + terminalHeight() +