about summary refs log tree commit diff stats
path: root/src/io
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-11-28 19:52:10 +0100
committerbptato <nincsnevem662@gmail.com>2022-11-28 23:00:06 +0100
commiteb2e57c97eb67eec19f068e294a8f6d1375c82f5 (patch)
tree87156c515f6ee9a63f58dc080184bd3127ce6836 /src/io
parent8af10b8b74fd29fe4c9debcd5cbecfaddf53a7b5 (diff)
downloadchawan-eb2e57c97eb67eec19f068e294a8f6d1375c82f5.tar.gz
Add textarea
Editing is implemented using an external editor (like vi).
Diffstat (limited to 'src/io')
-rw-r--r--src/io/lineedit.nim2
-rw-r--r--src/io/term.nim467
2 files changed, 1 insertions, 468 deletions
diff --git a/src/io/lineedit.nim b/src/io/lineedit.nim
index 6673676d..2cd76fff 100644
--- a/src/io/lineedit.nim
+++ b/src/io/lineedit.nim
@@ -5,7 +5,7 @@ import sugar
 
 import bindings/quickjs
 import buffer/cell
-import io/term
+import display/term
 import js/javascript
 import types/color
 import utils/twtstr
diff --git a/src/io/term.nim b/src/io/term.nim
deleted file mode 100644
index 7d3786e0..00000000
--- a/src/io/term.nim
+++ /dev/null
@@ -1,467 +0,0 @@
-import math
-import options
-import os
-import tables
-import terminal
-
-import bindings/termcap
-import buffer/cell
-import config/config
-import io/window
-import types/color
-
-#TODO switch from termcap...
-
-type
-  TermcapCap = enum
-    ce # clear till end of line
-    cd # clear display
-    cm # cursor move
-    ti # terminal init (=smcup)
-    te # terminal end (=rmcup)
-    so # start standout mode
-    md # start bold mode
-    us # start underline mode
-    mr # start reverse mode
-    mb # start blink mode
-    ue # end underline mode
-    se # end standout mode
-    me # end all formatting modes
-    LE # cursor left %1 characters
-    RI # cursor right %1 characters
-
-  Termcap = ref object
-    bp: array[1024, uint8]
-    funcstr: array[256, uint8]
-    caps: array[TermcapCap, cstring]
-
-  Terminal* = ref TerminalObj
-  TerminalObj = object
-    config: Config
-    infile: File
-    outfile: File
-    cleared: bool
-    canvas: FixedGrid
-    pcanvas: FixedGrid
-    attrs*: WindowAttributes
-    mincontrast: float
-    colormode: ColorMode
-    formatmode: FormatMode
-    smcup: bool
-    tc: Termcap
-    tname: string
-
-func hascap(term: Terminal, c: TermcapCap): bool = term.tc.caps[c] != nil
-func cap(term: Terminal, c: TermcapCap): string = $term.tc.caps[c]
-func ccap(term: Terminal, c: TermcapCap): cstring = term.tc.caps[c]
-
-template CSI*(s: varargs[string, `$`]): string =
-  var r = "\e["
-  var first = true
-  for x in s:
-    if not first:
-      r &= ";"
-    first = false
-    r &= x
-  r
-
-when not termcap_found:
-  template DECSET(s: varargs[string, `$`]): string =
-    var r = "\e[?"
-    var first = true
-    for x in s:
-      if not first:
-        r &= ";"
-      first = false
-      r &= x
-    r & "h"
-  template DECRST(s: varargs[string, `$`]): string =
-    var r = "\e[?"
-    var first = true
-    for x in s:
-      if not first:
-        r &= ";"
-      first = false
-      r &= x
-    r & "l"
-  template SMCUP(): string = DECSET(1049)
-  template RMCUP(): string = DECRST(1049)
-  template HVP(s: varargs[string, `$`]): string =
-    CSI(s) & "f"
-  template EL(s: varargs[string, `$`]): string =
-    CSI(s) & "K"
-
-template SGR*(s: varargs[string, `$`]): string =
-  CSI(s) & "m"
-
-const ANSIColorMap = [
-  ColorsRGB["black"],
-  ColorsRGB["red"],
-  ColorsRGB["green"],
-  ColorsRGB["yellow"],
-  ColorsRGB["blue"],
-  ColorsRGB["magenta"],
-  ColorsRGB["cyan"],
-  ColorsRGB["white"],
-]
-
-var goutfile: File
-proc putc(c: char): cint {.cdecl.} =
-  goutfile.write(c)
-
-proc write*(term: Terminal, s: string) =
-  when termcap_found:
-    discard tputs(cstring(s), cint(s.len), putc)
-  else:
-    term.outfile.write(s)
-
-proc flush*(term: Terminal) =
-  term.outfile.flushFile()
-
-proc cursorGoto(term: Terminal, x, y: int): string =
-  when termcap_found:
-    return $tgoto(term.ccap cm, cint(x), cint(y))
-  else:
-    return HVP(y, x)
-
-proc clearEnd(term: Terminal): string =
-  when termcap_found:
-    return term.cap ce
-  else:
-    return EL()
-
-proc resetFormat(term: Terminal): string =
-  when termcap_found:
-    return term.cap me
-  else:
-    return SGR()
-
-#TODO get rid of these
-proc setCursor*(term: Terminal, x, y: int) =
-  term.write(term.cursorGoto(x, y))
-
-proc cursorBackward*(term: Terminal, i: int) =
-  if i > 0:
-    if i == 1:
-      term.write("\b")
-    else:
-      when termcap_found:
-        term.write($tgoto(term.ccap LE, cint(i), 0))
-      else:
-        term.outfile.cursorBackward(i)
-
-proc cursorForward*(term: Terminal, i: int) =
-  if i > 0:
-    when termcap_found:
-      term.write($tgoto(term.ccap RI, cint(i), 0))
-    else:
-      term.outfile.cursorForward(i)
-
-proc cursorBegin*(term: Terminal) =
-  term.write("\r")
-
-proc enableAltScreen(term: Terminal): string =
-  when termcap_found:
-    if term.hascap ti:
-      term.write($term.cap ti)
-  else:
-    return SMCUP()
-
-proc disableAltScreen(term: Terminal): string =
-  when termcap_found:
-    if term.hascap te:
-      term.write($term.cap te)
-  else:
-    return RMCUP()
-
-proc distance(a, b: CellColor): float =
-  let a = if a.rgb:
-    a.rgbcolor
-  elif a == defaultColor:
-    ColorsRGB["black"]
-  else:
-    ANSIColorMap[a.color mod 10]
-  let b = if b.rgb:
-    b.rgbcolor
-  elif b == defaultColor:
-    ColorsRGB["white"]
-  else:
-    ANSIColorMap[b.color mod 10]
-  sqrt(float((a.r - b.r) ^  2 + (a.g - b.b) ^ 2 + (a.g - b.g) ^ 2))
-
-proc invert(color: CellColor, bg: bool): CellColor =
-  if color == defaultColor:
-    if bg:
-      return CellColor(rgb: true, rgbcolor: ColorsRGB["white"])
-    else:
-      return CellColor(rgb: true, rgbcolor: ColorsRGB["black"])
-  elif color.rgb:
-    return CellColor(rgb: true, rgbcolor: RGBColor(0xFFFFFF - uint32(color.rgbcolor)))
-  else:
-    return CellColor(rgb: true, rgbcolor: RGBColor(0xFFFFFF - uint32(ANSIColorMap[color.color mod 10])))
-
-# Use euclidian distance to quantize RGB colors.
-proc approximateANSIColor(rgb: RGBColor, exclude = -1): int =
-  var a = 0
-  var n = -1
-  for i in 0 .. ANSIColorMap.high:
-    if i == exclude: continue
-    let color = ANSIColorMap[i]
-    if color == rgb: return i
-    let b = (color.r - rgb.r) ^  2 + (color.g - rgb.b) ^ 2 + (color.g - rgb.g) ^ 2
-    if n == -1 or b < a:
-      n = i
-      a = b
-  return n
-
-proc processFormat*(term: Terminal, format: var Format, cellf: Format): string =
-  for flag in FormatFlags:
-    if flag in term.formatmode:
-      if flag in format.flags and flag notin cellf.flags:
-        result &= SGR(FormatCodes[flag].e)
-
-  var cellf = cellf
-  if term.mincontrast >= 0 and distance(cellf.bgcolor, cellf.fgcolor) <= term.mincontrast:
-    cellf.fgcolor = invert(cellf.fgcolor, false)
-    if distance(cellf.bgcolor, cellf.fgcolor) <= term.mincontrast:
-      cellf.fgcolor = defaultColor
-      cellf.bgcolor = defaultColor
-  case term.colormode
-  of ANSI, EIGHT_BIT:
-    if cellf.bgcolor.rgb:
-      let color = approximateANSIColor(cellf.bgcolor.rgbcolor)
-      if color == 0: # black
-        cellf.bgcolor = defaultColor
-      else:
-        cellf.bgcolor = ColorsANSIBg[color]
-    if cellf.fgcolor.rgb:
-      if cellf.bgcolor == defaultColor:
-        var color = approximateANSIColor(cellf.fgcolor.rgbcolor)
-        if color == 0:
-          color = 7
-        if color == 7: # white
-          cellf.fgcolor = defaultColor
-        else:
-          cellf.fgcolor = ColorsANSIFg[color]
-      else:
-        cellf.fgcolor = if int(cellf.bgcolor.color) - 40 < 4:
-          defaultColor
-        else:
-          ColorsANSIFg[7]
-  of MONOCHROME:
-    cellf.fgcolor = defaultColor
-    cellf.bgcolor = defaultColor
-  of TRUE_COLOR: discard
-
-  if cellf.fgcolor != format.fgcolor:
-    var color = cellf.fgcolor
-    if color.rgb:
-      let rgb = color.rgbcolor
-      result &= SGR(38, 2, rgb.r, rgb.g, rgb.b)
-    elif color == defaultColor:
-      result &= term.resetFormat()
-      format = newFormat()
-    else:
-      result &= SGR(color.color)
-
-  if cellf.bgcolor != format.bgcolor:
-    var color = cellf.bgcolor
-    if color.rgb:
-      let rgb = color.rgbcolor
-      result &= SGR(48, 2, rgb.r, rgb.g, rgb.b)
-    elif color == defaultColor:
-      result &= term.resetFormat()
-      format = newFormat()
-    else:
-      result &= SGR(color.color)
-
-  for flag in FormatFlags:
-    if flag in term.formatmode:
-      if flag notin format.flags and flag in cellf.flags:
-        result &= SGR(FormatCodes[flag].s)
-
-  format = cellf
-
-proc windowChange*(term: Terminal, attrs: WindowAttributes) =
-  term.attrs = attrs
-  term.canvas = newFixedGrid(attrs.width, attrs.height)
-  term.cleared = false
-
-func generateFullOutput(term: Terminal, grid: FixedGrid): string =
-  var format = newFormat()
-  result &= term.cursorGoto(0, 0)
-  result &= term.resetFormat()
-  for y in 0 ..< grid.height:
-    for x in 0 ..< grid.width:
-      let cell = grid[y * grid.width + x]
-      result &= term.processFormat(format, cell.format)
-      result &= cell.str
-    result &= term.clearEnd()
-    if y != grid.height - 1:
-      result &= "\r\n"
-
-func generateSwapOutput(term: Terminal, grid: FixedGrid, prev: FixedGrid): string =
-  var format = newFormat()
-  var x = 0
-  var line = ""
-  var lr = false
-  for i in 0 ..< grid.cells.len:
-    if x >= grid.width:
-      format = newFormat()
-      if lr:
-        result &= term.cursorGoto(0, i div grid.width - 1)
-        result &= term.resetFormat()
-        result &= term.clearEnd()
-        result &= line
-        lr = false
-      x = 0
-      line = ""
-    lr = lr or (grid[i] != prev[i])
-    line &= term.processFormat(format, grid.cells[i].format)
-    line &= grid.cells[i].str
-    inc x
-  if lr:
-    result &= term.cursorGoto(0, grid.height - 1)
-    result &= term.resetFormat()
-    result &= term.clearEnd()
-    result &= line
-
-proc hideCursor*(term: Terminal) =
-  term.outfile.hideCursor()
-
-proc showCursor*(term: Terminal) =
-  term.outfile.showCursor()
-
-proc writeGrid*(term: Terminal, grid: FixedGrid, x = 0, y = 0) =
-  for ly in y ..< y + grid.height:
-    for lx in x ..< x + grid.width:
-      term.canvas[ly * term.canvas.width + lx] = grid[(ly - y) * grid.width + (lx - x)]
-
-proc outputGrid*(term: Terminal) =
-  term.outfile.write(term.resetFormat())
-  if not term.cleared:
-    term.outfile.write(term.generateFullOutput(term.canvas))
-    term.cleared = true
-  else:
-    term.outfile.write(term.generateSwapOutput(term.canvas, term.pcanvas))
-  term.pcanvas = term.canvas
-
-when defined(posix):
-  import posix
-  import termios
-
-  # see https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
-  var orig_termios: Termios
-  var stdin_fileno: FileHandle
-  proc disableRawMode() {.noconv.} =
-    discard tcSetAttr(stdin_fileno, TCSAFLUSH, addr orig_termios)
-
-  proc enableRawMode(fileno: FileHandle) =
-    stdin_fileno = fileno
-    discard tcGetAttr(fileno, addr orig_termios)
-    var raw = orig_termios
-    raw.c_iflag = raw.c_iflag and not (BRKINT or ICRNL or INPCK or ISTRIP or IXON)
-    raw.c_oflag = raw.c_oflag and not (OPOST)
-    raw.c_cflag = raw.c_cflag or CS8
-    raw.c_lflag = raw.c_lflag and not (ECHO or ICANON or ISIG or IEXTEN)
-    discard tcSetAttr(fileno, TCSAFLUSH, addr raw)
-
-  var orig_flags: cint
-  var stdin_unblocked = false
-  proc unblockStdin*(fileno: FileHandle) =
-    orig_flags = fcntl(fileno, F_GETFL, 0)
-    let flags = orig_flags or O_NONBLOCK
-    discard fcntl(fileno, F_SETFL, flags)
-    stdin_unblocked = true
-
-  proc restoreStdin*(fileno: FileHandle) =
-    if stdin_unblocked:
-      discard fcntl(fileno, F_SETFL, orig_flags)
-      stdin_unblocked = false
-else:
-  proc disableRawMode() =
-    discard
-
-  proc enableRawMode(fileno: FileHandle) =
-    discard
-
-  proc unblockStdin*(): cint =
-    discard
-
-  proc restoreStdin*(flags: cint) =
-    discard
-
-proc isatty*(term: Terminal): bool =
-  term.infile.isatty() and term.outfile.isatty()
-
-proc quit*(term: Terminal) =
-  if term.infile != nil and term.isatty():
-    disableRawMode()
-    if term.smcup:
-      term.write(term.disableAltScreen())
-    else:
-      term.write(term.cursorGoto(0, term.attrs.height - 1))
-    term.outfile.showCursor()
-  term.outfile.flushFile()
-
-when termcap_found:
-  proc loadTermcap(term: Terminal) =
-    assert goutfile == nil
-    goutfile = term.outfile
-    let tc = new Termcap
-    if tgetent(cast[cstring](addr tc.bp), cstring(term.tname)) == 1:
-      term.tc = tc
-      for id in TermcapCap:
-        tc.caps[id] = tgetstr(cstring($id), cast[ptr cstring](addr tc.funcstr))
-    else:
-      raise newException(Defect, "Failed to load termcap description for terminal " & term.tname)
-
-proc detectTermAttributes(term: Terminal) =
-  term.tname = getEnv("TERM")
-  if term.tname == "":
-    term.tname = "dosansi"
-  when termcap_found:
-    term.loadTermcap()
-    if term.tc != nil:
-      term.smcup = term.hascap(ti)
-    term.formatmode = {FLAG_ITALIC, FLAG_OVERLINE, FLAG_STRIKE}
-    if term.hascap(us):
-      term.formatmode.incl(FLAG_UNDERLINE)
-    if term.hascap(md):
-      term.formatmode.incl(FLAG_BOLD)
-    if term.hascap(mr):
-      term.formatmode.incl(FLAG_REVERSE)
-    if term.hascap(mb):
-      term.formatmode.incl(FLAG_BLINK)
-  else:
-    term.smcup = true
-    term.formatmode = {low(FormatFlags)..high(FormatFlags)}
-  if term.config.colormode.isSome:
-    term.colormode = term.config.colormode.get
-  else:
-    term.colormode = ANSI
-    let colorterm = getEnv("COLORTERM")
-    case colorterm
-    of "24bit", "truecolor": term.colormode = TRUE_COLOR
-  if term.config.formatmode.isSome:
-    term.formatmode = term.config.formatmode.get
-  if term.config.altscreen.isSome:
-    term.smcup = term.config.altscreen.get
-  term.mincontrast = term.config.mincontrast
-
-proc start*(term: Terminal, infile: File) =
-  term.infile = infile
-  assert term.outfile.getFileHandle().setInheritable(false)
-  assert term.infile.getFileHandle().setInheritable(false)
-  if term.isatty():
-    enableRawMode(infile.getFileHandle())
-  term.detectTermAttributes()
-  if term.smcup:
-    term.write(term.enableAltScreen())
-
-proc newTerminal*(outfile: File, config: Config, attrs: WindowAttributes): Terminal =
-  let term = new Terminal
-  term.outfile = outfile
-  term.config = config
-  term.windowChange(attrs)
-  return term