basic editor skeleton now uses the buffer
This commit is contained in:
parent
9376ed14f9
commit
4e1ee4d089
27
editor.nim
27
editor.nim
|
@ -4,6 +4,8 @@ import terminal
|
|||
import strformat
|
||||
import posix
|
||||
|
||||
import termBuffer/buffer
|
||||
|
||||
import keycodes
|
||||
|
||||
type
|
||||
|
@ -21,6 +23,7 @@ type
|
|||
y: int # which row are we editing
|
||||
size: int # size of the row being edited in characters
|
||||
buffer: seq[string] # current text
|
||||
screenBuffer: Buffer
|
||||
history: seq[seq[string]] # past texts
|
||||
historyIndex: int # where are we in history
|
||||
cols: int # number of columns in the terminal
|
||||
|
@ -41,6 +44,7 @@ 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) =
|
||||
|
@ -51,15 +55,17 @@ proc destroyEditor*(oldEditor: EditorState) =
|
|||
return
|
||||
|
||||
proc render(ed: EditorState) =
|
||||
eraseLine()
|
||||
setCursorXPos(0)
|
||||
write stdout, ed.prompt
|
||||
write stdout, ed.buffer[ed.y]
|
||||
ed.screenBuffer.clearLine()
|
||||
ed.screenBuffer.write(ed.prompt)
|
||||
ed.screenBuffer.write(ed.buffer[ed.y])
|
||||
ed.screenBuffer.redraw()
|
||||
|
||||
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) =
|
||||
|
||||
|
@ -76,6 +82,7 @@ proc read*(ed: EditorState): (EditorResult, string) =
|
|||
if ed.x == 0:
|
||||
discard # merge two lines TODO
|
||||
elif ed.x > ed.cline().high():
|
||||
# TODO UTF8
|
||||
ed.cline() = ed.cline[0..ed.cline().high()-1]
|
||||
dec ed.x
|
||||
dec ed.bx
|
||||
|
@ -84,6 +91,15 @@ proc read*(ed: EditorState): (EditorResult, string) =
|
|||
ed.cline() = ed.cline[0..ed.x-1] & ed.cline[ed.x+1..ed.cline().high()]
|
||||
dec ed.x
|
||||
dec ed.bx
|
||||
of jkDelete.int:
|
||||
if ed.x > ed.cline.high():
|
||||
discard # merge two lines TODO
|
||||
elif ed.x == 0:
|
||||
# TODO UTF8
|
||||
ed.cline() = ed.cline[1..ed.cline.high()]
|
||||
else:
|
||||
# TODO UTF8
|
||||
discard # TODO implement
|
||||
of 3: # ctrl+c
|
||||
editorResult = erCtrlC
|
||||
break
|
||||
|
@ -110,7 +126,6 @@ proc read*(ed: EditorState): (EditorResult, string) =
|
|||
result = (editorResult, ed.buffer.join("\n"))
|
||||
|
||||
# cleanup
|
||||
write(stdout, "\n")
|
||||
ed.history.add(ed.buffer)
|
||||
ed.buffer = @[""]
|
||||
ed.y = 0
|
||||
|
|
|
@ -5,5 +5,6 @@ let e = newEditor("> ", false)
|
|||
while true:
|
||||
let (res, text) = e.read()
|
||||
if res in {erCtrlC, erCtrlD} or text == "quit":
|
||||
e.print("")
|
||||
break
|
||||
echo text
|
||||
e.print(text)
|
|
@ -1,5 +1,6 @@
|
|||
import unicode
|
||||
import escapeSequences
|
||||
import sequtils
|
||||
|
||||
type
|
||||
Buffer* = ref object
|
||||
|
@ -40,6 +41,7 @@ type
|
|||
attributes: seq[uint8]
|
||||
text: Rune
|
||||
|
||||
|
||||
func getCursorPos*(buf: Buffer): (int, int) =
|
||||
(buf.bufferX, buf.bufferY)
|
||||
|
||||
|
@ -57,7 +59,6 @@ proc setCursorXPos*(buf: Buffer, x: int) =
|
|||
raise newException(ValueError, "Provided x is out of bounds.")
|
||||
buf.bufferX = x
|
||||
|
||||
|
||||
proc setAttributes*(buf: Buffer, attributes: seq[uint8]) =
|
||||
buf.cursorAttributes = attributes
|
||||
|
||||
|
@ -99,24 +100,56 @@ 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 redraw*(buf: Buffer) =
|
||||
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
|
||||
|
||||
|
||||
proc redraw*(buf: Buffer, force: bool = false) =
|
||||
var toPrint = ""
|
||||
# save cursor state
|
||||
let oldDisplayX = buf.displayX
|
||||
let oldDisplayY = buf.displayY
|
||||
|
||||
# go over display and buffer to find any differences
|
||||
for i in 0..buf.buffered.high():
|
||||
let cellBuffer = buf.buffered[i]
|
||||
let cellDisplay = buf.displayed[i]
|
||||
# TODO continue here :)
|
||||
|
||||
|
||||
|
||||
# TODO: this is a spot for obvious optimization
|
||||
if cellBuffer != cellDisplay or force:
|
||||
let y = i div buf.width
|
||||
let x = i mod buf.width
|
||||
# go to given place
|
||||
toPrint &= escCursorPos(x+buf.positionX, y+buf.positionY)
|
||||
|
||||
# get the right attributes
|
||||
# first reset
|
||||
toPrint &= escAttributes(@[fsReset.uint8])
|
||||
# set cell attributes
|
||||
toPrint &= escAttributes(cellBuffer.attributes)
|
||||
# write the character if it's a non null
|
||||
if cellBuffer.text != Rune(0):
|
||||
toPrint &= $cellBuffer.text
|
||||
else:
|
||||
# erase a character since it's a null
|
||||
toPrint &= escEraseCharacters(1)
|
||||
|
||||
# update displayed buffer
|
||||
buf.displayed[i] = cellBuffer
|
||||
|
||||
# finish
|
||||
toPrint &= escCursorPos(oldDisplayX, oldDisplayY)
|
||||
toPrint &= escAttributes(@[fsReset.uint8])
|
||||
toPrint &= escCursorPos(buf.bufferX + buf.positionX, buf.bufferY + buf.positionY)
|
||||
buf.stdout.write(toPrint)
|
||||
|
||||
|
||||
|
@ -131,4 +164,22 @@ func getPosition*(buf: Buffer): (int, int) =
|
|||
|
||||
proc setPosition*(buf: Buffer, x, y: int) =
|
||||
buf.positionX = x
|
||||
buf.positionY = y
|
||||
buf.positionY = y
|
||||
|
||||
proc newBuffer*(x, y, w, h: int, stdout: File): Buffer =
|
||||
new(result)
|
||||
result.positionX = x
|
||||
result.positionY = y
|
||||
result.width = w
|
||||
result.height = h
|
||||
result.stdout = stdout
|
||||
let length = w * h
|
||||
result.buffered = repeat(Cell(text: Rune(0)), length)
|
||||
result.displayed = repeat(Cell(text: Rune(0)), length)
|
||||
result.bufferX = 0
|
||||
result.bufferY = 0
|
||||
result.displayX = 0
|
||||
result.displayY = 0
|
||||
result.cursorAttributes = @[]
|
||||
result.redraw(force = true)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import strformat
|
|||
import strutils
|
||||
|
||||
func escCursorPos*(x, y: int): string =
|
||||
&"\e[{x+1};{y+1}H"
|
||||
&"\e[{y+1};{x+1}H"
|
||||
|
||||
func escAttributes*(attributes: seq[uint8]): string =
|
||||
let joined = attributes.join(";")
|
||||
|
|
Loading…
Reference in New Issue