175 lines
4.0 KiB
Nim
175 lines
4.0 KiB
Nim
# multiline.nim
|
|
|
|
import line
|
|
|
|
import strutils
|
|
|
|
type
|
|
Multiline* = ref object
|
|
lines: seq[Line]
|
|
y: int
|
|
|
|
# getters/setters
|
|
|
|
proc X*(ml: Multiline): int =
|
|
ml.lines[ml.y].X
|
|
|
|
proc MX*(ml: Multiline): int =
|
|
ml.lines[ml.y].MX
|
|
|
|
proc Y*(ml: Multiline): int =
|
|
ml.y
|
|
|
|
# constructor
|
|
|
|
proc newMultiline*(initEmpty: bool = true): Multiline =
|
|
new(result)
|
|
result.lines = @[]
|
|
if initEmpty:
|
|
result.lines.add(newLine())
|
|
result.y = 0
|
|
|
|
proc copy*(ml: Multiline): Multiline =
|
|
new(result)
|
|
for l in ml.lines:
|
|
result.lines.add(l.copy())
|
|
result.y = ml.y
|
|
|
|
# methods
|
|
|
|
proc lineLen*(ml: Multiline): int =
|
|
ml.lines[ml.y].len()
|
|
|
|
proc lineHigh*(ml: Multiline): int =
|
|
ml.lines[ml.y].high()
|
|
|
|
proc len*(ml: Multiline): int =
|
|
ml.lines.len()
|
|
|
|
proc high*(ml: Multiline): int =
|
|
ml.lines.high()
|
|
|
|
# internal setter
|
|
proc sety(ml: Multiline, target: int) =
|
|
ml.lines[target].navigateToMx(ml.lines[ml.y].MX)
|
|
ml.y = target
|
|
|
|
# warning check before calling them if y lands in illegal territory
|
|
# these are unsafe!
|
|
proc decy(ml: Multiline) =
|
|
ml.sety(ml.y-1)
|
|
|
|
proc incy(ml: Multiline) =
|
|
ml.sety(ml.y+1)
|
|
|
|
# publically callable movement methods
|
|
proc left*(ml: Multiline) =
|
|
ml.lines[ml.y].left()
|
|
|
|
proc right*(ml: Multiline) =
|
|
ml.lines[ml.y].right()
|
|
|
|
proc up*(ml: Multiline) =
|
|
if ml.y > 0:
|
|
ml.decy
|
|
|
|
proc down*(ml: Multiline) =
|
|
if ml.y < ml.lines.high():
|
|
ml.incy
|
|
|
|
proc home*(ml: Multiline) =
|
|
ml.lines[ml.y].home()
|
|
|
|
proc `end`*(ml: Multiline) =
|
|
ml.lines[ml.y].`end`()
|
|
|
|
proc vhome*(ml: Multiline) =
|
|
ml.sety(0)
|
|
|
|
proc vend*(ml: Multiline) =
|
|
ml.sety(ml.high())
|
|
|
|
proc insert*(ml: Multiline, str: string) =
|
|
ml.lines[ml.y].insert(str)
|
|
|
|
proc delete*(ml: Multiline) =
|
|
if ml.lines[ml.y].X < ml.lineLen():
|
|
ml.lines[ml.y].delete()
|
|
else:
|
|
discard # TODO merge two lines
|
|
|
|
proc backspace*(ml: Multiline) =
|
|
if ml.lines[ml.y].X > 0:
|
|
ml.lines[ml.y].backspace()
|
|
else:
|
|
discard # TODO merge two lines
|
|
|
|
proc insertline*(ml: Multiline) =
|
|
# the default behaviour of command mode o
|
|
if ml.y == ml.lines.high():
|
|
ml.lines.add(newLine())
|
|
else:
|
|
ml.lines.insert(newLine(), ml.y + 1)
|
|
inc ml.y # this automatically moves x and mx to 0, since the newLine creates one with these coords
|
|
|
|
proc enter*(ml: Multiline) = # TODO REDO THIS
|
|
# the default behaviour of enter in normie editors
|
|
if ml.lines[ml.y].X > ml.lineHigh():
|
|
ml.insertline() # when end of line, it's just an insertline
|
|
else:
|
|
let cut = ml.lines[ml.y].range(ml.x, ml.lineHigh())
|
|
ml.lines[ml.y].delete(ml.x, ml.lineHigh())
|
|
ml.insertline()
|
|
ml.lines[ml.y].insert(cut, 0)
|
|
|
|
proc clearline*(ml: Multiline) =
|
|
ml.lines[ml.y].clearLine()
|
|
|
|
proc removeline*(ml: Multiline) =
|
|
ml.lines.delete(ml.y)
|
|
if ml.lines.len() == 0:
|
|
ml.lines.add(newLine())
|
|
if ml.y > ml.lines.high():
|
|
dec ml.y #TODO: preserve x
|
|
|
|
proc getLine*(ml: Multiline, line: int = -1): string =
|
|
if line == -1:
|
|
ml.lines[ml.y].content
|
|
else:
|
|
if line >= 0 and line <= ml.lines.high():
|
|
ml.lines[line].content
|
|
else:
|
|
""
|
|
|
|
# without the extra args these work together to convert a multiline to a single
|
|
# line string. With extra args customizable
|
|
proc serialize*(ml: Multiline, sep: string = r"\n", replaceBS: bool = true): string =
|
|
# replaceBS = replace backslash
|
|
for line in ml.lines:
|
|
if replaceBS:
|
|
result &= line.content.replace(r"\", r"\\") & sep
|
|
else:
|
|
result &= line.content & sep
|
|
result[0..result.high() - sep.len()]
|
|
|
|
proc deserialize*(str: string, sep: string = r"\n", replaceBS: bool = true): Multiline =
|
|
result = newMultiline(initEmpty = false)
|
|
for line in str.split(sep):
|
|
if replaceBS:
|
|
result.lines.add(newLine(line.replace(r"\\", r"\")))
|
|
else:
|
|
result.lines.add(newLine(line))
|
|
|
|
result.y = result.high()
|
|
result.x = result.lineLen()
|
|
|
|
proc fromString*(str: string): Multiline =
|
|
# simple load of string to multiline
|
|
deserialize(str, sep = "\n", replaceBS = false)
|
|
|
|
proc getContent*(ml: Multiline): string =
|
|
# simple convert of multiline to string
|
|
ml.serialize(sep = "\n", replaceBS = false)
|
|
|
|
|