128 lines
3.3 KiB
Nim
128 lines
3.3 KiB
Nim
# a multiline utf8 text buffer
|
|
|
|
import strutils
|
|
import unicode
|
|
import sequtils
|
|
|
|
type
|
|
TextBuffer* = ref object
|
|
content: seq[seq[Rune]]
|
|
cursorX: int # rune index
|
|
cursorY: int
|
|
|
|
proc newTextBuffer*: TextBuffer =
|
|
new(result)
|
|
result.content = @[]
|
|
result.content.add(@[])
|
|
result.cursorX = 0
|
|
result.cursorY = 0
|
|
|
|
proc getLine*(buf: TextBuffer): seq[Rune] =
|
|
buf.content[buf.cursorY]
|
|
|
|
proc getContent*(buf: TextBuffer): string =
|
|
buf.content.join("\n")
|
|
|
|
proc cline(buf: TextBuffer): var seq[Rune] =
|
|
buf.content[buf.cursorY]
|
|
|
|
proc insertRune*(buf: TextBuffer, rune: Rune) =
|
|
buf.cline().insert(rune, buf.cursorX)
|
|
buf.cursorX += 1
|
|
|
|
proc isEmpty*(buf: TextBuffer): bool =
|
|
buf.content.len() == 1 and buf.content[0].len() == 0
|
|
|
|
proc delete*(buf: TextBuffer) =
|
|
# emulates a delete key
|
|
if buf.cursorX == 0 and buf.cline().len() > 0:
|
|
buf.cline() = buf.cline[1..buf.cline.high()]
|
|
elif buf.cursorX > buf.cline.high():
|
|
if buf.cursorY < buf.content.high():
|
|
# merging lines
|
|
let left = buf.cline()
|
|
buf.content.delete(buf.cursorY)
|
|
buf.cline() = left & buf.cline()
|
|
else:
|
|
buf.cline() = buf.cline[0..buf.cursorX-1] & buf.cline[buf.cursorX+1..buf.cline().high()]
|
|
|
|
|
|
proc backspace*(buf: TextBuffer) =
|
|
# emulates a backspace key
|
|
if buf.cursorX == 0:
|
|
if buf.cursorY > 0:
|
|
# merging lines
|
|
let right = buf.cline()
|
|
buf.content.delete(buf.cursorY)
|
|
dec buf.cursorY
|
|
buf.cursorX = buf.cline().len()
|
|
buf.cline() &= right
|
|
elif buf.cursorX > buf.cline().high():
|
|
buf.cline() = buf.cline[0..buf.cline().high()-1]
|
|
dec buf.cursorX
|
|
else:
|
|
buf.cline() = buf.cline[0..buf.cursorX-2] & buf.cline[buf.cursorX..buf.cline().high()]
|
|
dec buf.cursorX
|
|
|
|
proc moveCursor*(buf: TextBuffer, dx, dy: int) =
|
|
if dx != 0:
|
|
buf.cursorX += dx
|
|
if buf.cursorX < 0:
|
|
buf.cursorX = 0 # TODO switching over lines
|
|
elif buf.cursorX > buf.cline().len():
|
|
buf.cursorX = buf.cline().len() # TODO switching over lines
|
|
if dy != 0:
|
|
buf.cursorY += dy
|
|
if buf.cursorY < 0:
|
|
buf.cursorY = 0
|
|
elif buf.cursorY > buf.content.high():
|
|
buf.cursorY = buf.content.high()
|
|
|
|
if buf.cursorX > buf.cline().len():
|
|
buf.cursorX = buf.cline().len()
|
|
|
|
|
|
proc getCursorPos*(buf: TextBuffer): (int, int) =
|
|
(buf.cursorX, buf.cursorY)
|
|
|
|
proc isCursorAtEnd*(buf: TextBuffer): bool =
|
|
return buf.cursorY == buf.content.high() and buf.cursorX > buf.cline().high()
|
|
|
|
proc moveToEnd*(buf: TextBuffer, forward: bool) =
|
|
# home/end keys
|
|
if forward:
|
|
buf.cursorX = buf.cline().len()
|
|
else:
|
|
buf.cursorX = 0
|
|
|
|
proc enter*(buf: TextBuffer) =
|
|
# emulates an enter in text editors
|
|
let pre = buf.cline[0..buf.cursorX-1]
|
|
let post = buf.cline[buf.cursorX..^1]
|
|
buf.cline() = pre
|
|
inc buf.cursorY
|
|
buf.content.insert(post, buf.cursorY)
|
|
buf.cursorX = 0
|
|
|
|
proc newLine*(buf: TextBuffer) =
|
|
# inserts a new empty line below the current one
|
|
# and moves cursor to it
|
|
inc buf.cursorY
|
|
let empty: seq[Rune] = @[]
|
|
buf.content.insert(empty, buf.cursorY)
|
|
buf.cursorX = 0
|
|
|
|
proc getLineCount*(buf: TextBuffer): int =
|
|
buf.content.len()
|
|
|
|
proc lines*(buf: TextBuffer): seq[seq[Rune]] =
|
|
buf.content
|
|
|
|
proc isLastLine*(buf: TextBuffer): bool =
|
|
buf.cursorY == buf.content.high()
|
|
|
|
proc isFistLine*(buf: TextBuffer): bool =
|
|
buf.cursorY == 0
|
|
|
|
proc isLineEmpty*(buf: TextBuffer): bool =
|
|
buf.cline().len() == 0 |