jal3/editor.nim

131 lines
3.4 KiB
Nim
Raw Normal View History

2022-12-07 21:01:59 +01:00
import unicode
import strutils
import strformat
import posix
import terminalUtils/buffer
import terminalUtils/terminalGetInfo
import terminalUtils/keycodes
2022-12-07 21:01:59 +01:00
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
screenBuffer: Buffer
2022-12-07 21:01:59 +01:00
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
2022-12-28 18:12:29 +01:00
EditorResult* = enum
erEnter, erCtrlC, erCtrlD, erError
2022-12-07 21:01:59 +01:00
# 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) =
ed.screenBuffer.clearLine()
ed.screenBuffer.write(ed.prompt)
ed.screenBuffer.write(ed.buffer[ed.y])
ed.screenBuffer.redraw()
2022-12-07 21:01:59 +01:00
template cline(ed: EditorState): var string =
ed.buffer[ed.y]
2022-12-28 18:12:29 +01:00
proc read*(ed: EditorState): (EditorResult, string) =
var editorResult = erError
let (_, cursorY) = termGetCursorPos(stdout)
ed.screenBuffer = newBuffer(0, cursorY, termGetWidth(), termGetHeight() - cursorY, stdout)
2022-12-28 18:12:29 +01:00
2022-12-07 21:01:59 +01:00
ed.render()
while true:
let key = getKey()
case key:
of jkEnter.int:
2022-12-28 18:12:29 +01:00
editorResult = erEnter
2022-12-07 21:01:59 +01:00
break
of jkBackspace.int:
if ed.x == 0:
discard # merge two lines TODO
elif ed.x > ed.cline().high():
# TODO UTF8
2022-12-07 21:01:59 +01:00
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
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
2022-12-28 18:12:29 +01:00
of 3: # ctrl+c
editorResult = erCtrlC
break
of 4: # ctrl+d
if ed.buffer.len() <= 1 and ed.cline().len() == 0:
editorResult = erCtrlD
break
2022-12-07 21:01:59 +01:00
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
2022-12-28 18:12:29 +01:00
result = (editorResult, ed.buffer.join("\n"))
2022-12-07 21:01:59 +01:00
# cleanup
stdout.write("\n")
2022-12-07 21:01:59 +01:00
ed.history.add(ed.buffer)
ed.buffer = @[""]
ed.y = 0
ed.x = 0
ed.screenBuffer = nil
2022-12-07 21:01:59 +01:00