jale/editor.nim

166 lines
4.1 KiB
Nim
Raw Normal View History

2021-02-15 20:18:31 +01:00
import strformat
import strutils
import tables
import terminal
import line
import multiline
import keycodes
import event
import renderer
type
JaleEvent* = enum
jeKeypress, jeQuit, jeFinish, jeHistoryChange
2021-02-15 20:18:31 +01:00
LineEditor* = ref object
content*: Multiline
historyIndex*: int # TODO to be private
history*: seq[Multiline] # TODO to be private
2021-02-15 20:18:31 +01:00
keystrokes*: Event[int]
events*: Event[JaleEvent]
prompt*: string
lastKeystroke*: int
2021-02-15 20:59:40 +01:00
finished: bool
2021-02-15 20:18:31 +01:00
rendered: int # how many lines were printed last full refresh
2021-02-15 20:59:40 +01:00
# getter/setter sorts
proc unfinish*(le: LineEditor) =
le.finished = false
proc finish*(le: LineEditor) =
le.finished = true
# can be overwritten to false, inside the event
le.events.call(jeFinish)
# constructor
2021-02-15 20:18:31 +01:00
proc newLineEditor*: LineEditor =
new(result)
result.content = newMultiline()
result.history = @[]
result.history.add(result.content)
result.historyIndex = 0
result.keystrokes.new()
result.events.new()
result.prompt = ""
result.rendered = 0
2021-02-15 20:59:40 +01:00
# priv/pub methods
proc historyMove*(editor: LineEditor, delta: int) =
# broken behaviour: history should not be modified, it should clone
# the modified one that is eventually submitted and add it to the end
# TODO
if editor.historyIndex + delta < 0:
editor.content = editor.history[0]
editor.historyIndex = 0
elif editor.historyIndex + delta >= editor.history.high():
editor.content = editor.history[editor.history.high()]
editor.historyIndex = editor.history.high()
else:
editor.content = editor.history[editor.historyIndex + delta]
editor.historyIndex += delta
editor.events.call(jeHistoryChange)
2021-02-15 20:59:40 +01:00
proc reset(editor: LineEditor) =
editor.unfinish()
editor.rendered = 0
proc flush*(editor: LineEditor) =
# kinda like reset, it moves the current element one down in history
# and adds a new one
editor.reset()
editor.content = newMultiline()
editor.history.add(editor.content)
editor.historyIndex = editor.history.high()
2021-02-15 20:18:31 +01:00
proc render(editor: LineEditor, line: int = -1, hscroll: bool = true) =
var y = line
if y == -1:
y = editor.content.Y
renderLine(
(
if y == 0:
editor.prompt
else:
" ".repeat(editor.prompt.len())
),
editor.content.getLine(y),
0
)
proc clearLine =
write stdout, "\r" & " ".repeat(terminalWidth())
2021-02-15 20:18:31 +01:00
proc fullRender(editor: LineEditor) =
2021-02-15 20:59:40 +01:00
# from the top cursor pos, it draws the entire multiline prompt, then
# moves cursor to current y
2021-02-15 20:18:31 +01:00
for i in countup(0, editor.content.high()):
editor.render(i, false)
2021-02-15 20:59:40 +01:00
if i < editor.rendered:
cursorDown(1)
else:
write stdout, "\n"
inc editor.rendered
var extraup = 0
while editor.content.len() < editor.rendered:
clearLine()
cursorDown(1)
dec editor.rendered
inc extraup
cursorUp(editor.content.len() - editor.content.Y + extraup)
2021-02-15 20:18:31 +01:00
proc moveCursorToEnd(editor: LineEditor) =
# only called when read finished
2021-02-15 20:59:40 +01:00
if editor.content.high() > editor.content.Y:
cursorDown(editor.content.high() - editor.content.Y)
2021-02-15 20:18:31 +01:00
write stdout, "\n"
# TODO don't use globals, but allow for event removal
var histchange = false
proc changeHistory =
histchange = true
2021-02-15 20:18:31 +01:00
proc read*(editor: LineEditor): string =
2021-02-15 20:59:40 +01:00
# starts at the top, full render moves it into the right y
2021-02-15 20:18:31 +01:00
editor.fullRender()
2021-02-15 20:59:40 +01:00
# TODO: must remove event at end
# otherwise there'll be more instances of the same
editor.events.add(jeHistoryChange, changeHistory)
2021-02-15 20:18:31 +01:00
while not editor.finished:
2021-02-15 20:59:40 +01:00
# refresh current line every time
2021-02-15 20:18:31 +01:00
editor.render()
setCursorXPos(editor.content.X + editor.prompt.len())
2021-02-15 20:59:40 +01:00
# get key (with escapes)
2021-02-15 20:18:31 +01:00
let key = getKey()
2021-02-15 20:59:40 +01:00
# record y pos
2021-02-15 20:18:31 +01:00
let preY = editor.content.Y
2021-02-15 20:59:40 +01:00
# call the events
2021-02-15 20:18:31 +01:00
editor.lastKeystroke = key
editor.keystrokes.call(key)
editor.events.call(jeKeypress)
2021-02-15 20:59:40 +01:00
# redraw everything if y changed
if histchange or preY != editor.content.Y:
2021-02-15 20:59:40 +01:00
# move to the top
if preY > 0:
cursorUp(preY)
# move to the right y
2021-02-15 20:18:31 +01:00
editor.fullRender()
if histchange:
histchange = false
2021-02-15 20:18:31 +01:00
2021-02-15 20:59:40 +01:00
# move cursor to end
2021-02-15 20:18:31 +01:00
editor.moveCursorToEnd()
2021-02-15 20:59:40 +01:00
editor.reset()
2021-02-15 20:18:31 +01:00
return editor.content.getContent()