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 strformat
|
||||||
import posix
|
import posix
|
||||||
|
|
||||||
|
import termBuffer/buffer
|
||||||
|
|
||||||
import keycodes
|
import keycodes
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -21,6 +23,7 @@ type
|
||||||
y: int # which row are we editing
|
y: int # which row are we editing
|
||||||
size: int # size of the row being edited in characters
|
size: int # size of the row being edited in characters
|
||||||
buffer: seq[string] # current text
|
buffer: seq[string] # current text
|
||||||
|
screenBuffer: Buffer
|
||||||
history: seq[seq[string]] # past texts
|
history: seq[seq[string]] # past texts
|
||||||
historyIndex: int # where are we in history
|
historyIndex: int # where are we in history
|
||||||
cols: int # number of columns in the terminal
|
cols: int # number of columns in the terminal
|
||||||
|
@ -41,6 +44,7 @@ proc newEditor*(prompt: string = "> ", multiline: bool = true): EditorState =
|
||||||
result.prompt = prompt
|
result.prompt = prompt
|
||||||
result.maxRowsGoal = if multiline: 0 else: 1
|
result.maxRowsGoal = if multiline: 0 else: 1
|
||||||
result.buffer.add("")
|
result.buffer.add("")
|
||||||
|
result.screenBuffer = newBuffer(0, 0, terminalWidth(), terminalHeight(), stdout)
|
||||||
editors.add(result)
|
editors.add(result)
|
||||||
|
|
||||||
proc destroyEditor*(oldEditor: EditorState) =
|
proc destroyEditor*(oldEditor: EditorState) =
|
||||||
|
@ -51,15 +55,17 @@ proc destroyEditor*(oldEditor: EditorState) =
|
||||||
return
|
return
|
||||||
|
|
||||||
proc render(ed: EditorState) =
|
proc render(ed: EditorState) =
|
||||||
eraseLine()
|
ed.screenBuffer.clearLine()
|
||||||
setCursorXPos(0)
|
ed.screenBuffer.write(ed.prompt)
|
||||||
write stdout, ed.prompt
|
ed.screenBuffer.write(ed.buffer[ed.y])
|
||||||
write stdout, ed.buffer[ed.y]
|
ed.screenBuffer.redraw()
|
||||||
|
|
||||||
template cline(ed: EditorState): var string =
|
template cline(ed: EditorState): var string =
|
||||||
ed.buffer[ed.y]
|
ed.buffer[ed.y]
|
||||||
|
|
||||||
|
proc print*(ed: EditorState, text: string) =
|
||||||
|
ed.screenBuffer.print(text)
|
||||||
|
ed.screenBuffer.redraw()
|
||||||
|
|
||||||
proc read*(ed: EditorState): (EditorResult, string) =
|
proc read*(ed: EditorState): (EditorResult, string) =
|
||||||
|
|
||||||
|
@ -76,6 +82,7 @@ proc read*(ed: EditorState): (EditorResult, string) =
|
||||||
if ed.x == 0:
|
if ed.x == 0:
|
||||||
discard # merge two lines TODO
|
discard # merge two lines TODO
|
||||||
elif ed.x > ed.cline().high():
|
elif ed.x > ed.cline().high():
|
||||||
|
# TODO UTF8
|
||||||
ed.cline() = ed.cline[0..ed.cline().high()-1]
|
ed.cline() = ed.cline[0..ed.cline().high()-1]
|
||||||
dec ed.x
|
dec ed.x
|
||||||
dec ed.bx
|
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()]
|
ed.cline() = ed.cline[0..ed.x-1] & ed.cline[ed.x+1..ed.cline().high()]
|
||||||
dec ed.x
|
dec ed.x
|
||||||
dec ed.bx
|
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
|
of 3: # ctrl+c
|
||||||
editorResult = erCtrlC
|
editorResult = erCtrlC
|
||||||
break
|
break
|
||||||
|
@ -110,7 +126,6 @@ proc read*(ed: EditorState): (EditorResult, string) =
|
||||||
result = (editorResult, ed.buffer.join("\n"))
|
result = (editorResult, ed.buffer.join("\n"))
|
||||||
|
|
||||||
# cleanup
|
# cleanup
|
||||||
write(stdout, "\n")
|
|
||||||
ed.history.add(ed.buffer)
|
ed.history.add(ed.buffer)
|
||||||
ed.buffer = @[""]
|
ed.buffer = @[""]
|
||||||
ed.y = 0
|
ed.y = 0
|
||||||
|
|
|
@ -5,5 +5,6 @@ let e = newEditor("> ", false)
|
||||||
while true:
|
while true:
|
||||||
let (res, text) = e.read()
|
let (res, text) = e.read()
|
||||||
if res in {erCtrlC, erCtrlD} or text == "quit":
|
if res in {erCtrlC, erCtrlD} or text == "quit":
|
||||||
|
e.print("")
|
||||||
break
|
break
|
||||||
echo text
|
e.print(text)
|
|
@ -1,5 +1,6 @@
|
||||||
import unicode
|
import unicode
|
||||||
import escapeSequences
|
import escapeSequences
|
||||||
|
import sequtils
|
||||||
|
|
||||||
type
|
type
|
||||||
Buffer* = ref object
|
Buffer* = ref object
|
||||||
|
@ -40,6 +41,7 @@ type
|
||||||
attributes: seq[uint8]
|
attributes: seq[uint8]
|
||||||
text: Rune
|
text: Rune
|
||||||
|
|
||||||
|
|
||||||
func getCursorPos*(buf: Buffer): (int, int) =
|
func getCursorPos*(buf: Buffer): (int, int) =
|
||||||
(buf.bufferX, buf.bufferY)
|
(buf.bufferX, buf.bufferY)
|
||||||
|
|
||||||
|
@ -57,7 +59,6 @@ proc setCursorXPos*(buf: Buffer, x: int) =
|
||||||
raise newException(ValueError, "Provided x is out of bounds.")
|
raise newException(ValueError, "Provided x is out of bounds.")
|
||||||
buf.bufferX = x
|
buf.bufferX = x
|
||||||
|
|
||||||
|
|
||||||
proc setAttributes*(buf: Buffer, attributes: seq[uint8]) =
|
proc setAttributes*(buf: Buffer, attributes: seq[uint8]) =
|
||||||
buf.cursorAttributes = attributes
|
buf.cursorAttributes = attributes
|
||||||
|
|
||||||
|
@ -99,24 +100,56 @@ proc write*(buf: Buffer, text: string) =
|
||||||
for i in fromPos..toPos:
|
for i in fromPos..toPos:
|
||||||
buf.buffered[i] = Cell(text: runes[i-fromPos], attributes: buf.cursorAttributes)
|
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 = ""
|
var toPrint = ""
|
||||||
# save cursor state
|
|
||||||
let oldDisplayX = buf.displayX
|
|
||||||
let oldDisplayY = buf.displayY
|
|
||||||
|
|
||||||
# go over display and buffer to find any differences
|
# go over display and buffer to find any differences
|
||||||
for i in 0..buf.buffered.high():
|
for i in 0..buf.buffered.high():
|
||||||
let cellBuffer = buf.buffered[i]
|
let cellBuffer = buf.buffered[i]
|
||||||
let cellDisplay = buf.displayed[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
|
# finish
|
||||||
toPrint &= escCursorPos(oldDisplayX, oldDisplayY)
|
toPrint &= escAttributes(@[fsReset.uint8])
|
||||||
|
toPrint &= escCursorPos(buf.bufferX + buf.positionX, buf.bufferY + buf.positionY)
|
||||||
buf.stdout.write(toPrint)
|
buf.stdout.write(toPrint)
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,4 +164,22 @@ func getPosition*(buf: Buffer): (int, int) =
|
||||||
|
|
||||||
proc setPosition*(buf: Buffer, x, y: int) =
|
proc setPosition*(buf: Buffer, x, y: int) =
|
||||||
buf.positionX = x
|
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
|
import strutils
|
||||||
|
|
||||||
func escCursorPos*(x, y: int): string =
|
func escCursorPos*(x, y: int): string =
|
||||||
&"\e[{x+1};{y+1}H"
|
&"\e[{y+1};{x+1}H"
|
||||||
|
|
||||||
func escAttributes*(attributes: seq[uint8]): string =
|
func escAttributes*(attributes: seq[uint8]): string =
|
||||||
let joined = attributes.join(";")
|
let joined = attributes.join(";")
|
||||||
|
|
Loading…
Reference in New Issue