diff options
author | bptato <nincsnevem662@gmail.com> | 2023-09-14 01:41:47 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-09-14 02:01:21 +0200 |
commit | c1b8338045716b25d664c0b8dd91eac0cb76480e (patch) | |
tree | a9c0a6763f180c2b6dd380aa880253ffc7685d85 /src/types | |
parent | db0798acccbedcef4b16737f6be0cf7388cc0528 (diff) | |
download | chawan-c1b8338045716b25d664c0b8dd91eac0cb76480e.tar.gz |
move around more modules
* ips -> io/ * loader related stuff -> loader/ * tempfile -> extern/ * buffer, forkserver -> server/ * lineedit, window -> display/ * cell -> types/ * opt -> types/
Diffstat (limited to 'src/types')
-rw-r--r-- | src/types/buffersource.nim | 2 | ||||
-rw-r--r-- | src/types/cell.nim | 298 | ||||
-rw-r--r-- | src/types/cookie.nim | 2 | ||||
-rw-r--r-- | src/types/opt.nim | 109 |
4 files changed, 409 insertions, 2 deletions
diff --git a/src/types/buffersource.nim b/src/types/buffersource.nim index a5e3e21f..97126ed1 100644 --- a/src/types/buffersource.nim +++ b/src/types/buffersource.nim @@ -3,7 +3,7 @@ import options when defined(posix): import posix -import io/request +import loader/request import types/url import chakasu/charset diff --git a/src/types/cell.nim b/src/types/cell.nim new file mode 100644 index 00000000..87148389 --- /dev/null +++ b/src/types/cell.nim @@ -0,0 +1,298 @@ +import options +import tables + +import css/stylednode +import types/color +import utils/twtstr + +type + FormatFlags* = enum + FLAG_BOLD + FLAG_ITALIC + FLAG_UNDERLINE + FLAG_REVERSE + FLAG_STRIKE + FLAG_OVERLINE + FLAG_BLINK + + Format* = object + fgcolor*: CellColor + bgcolor*: CellColor + flags*: set[FormatFlags] + + # A FormatCell *starts* a new terminal formatting context. + # If no FormatCell exists before a given cell, the default formatting is used. + FormatCell* = object + format*: Format + pos*: int + node*: StyledNode + + SimpleFormatCell* = object + format*: Format + pos*: int + + FlexibleLine* = object + str*: string + formats*: seq[FormatCell] + + SimpleFlexibleLine* = object + str*: string + formats*: seq[SimpleFormatCell] + + FlexibleGrid* = seq[FlexibleLine] + + SimpleFlexibleGrid* = seq[SimpleFlexibleLine] + + FixedCell* = object + str*: string + format*: Format + + FixedGrid* = object + width*, height*: int + cells*: seq[FixedCell] + +proc `[]=`*(grid: var FixedGrid, i: int, cell: FixedCell) = grid.cells[i] = cell +proc `[]=`*(grid: var FixedGrid, i: BackwardsIndex, cell: FixedCell) = grid.cells[i] = cell +proc `[]`*(grid: var FixedGrid, i: int): var FixedCell = grid.cells[i] +proc `[]`*(grid: var FixedGrid, i: BackwardsIndex): var FixedCell = grid.cells[i] +proc `[]`*(grid: FixedGrid, i: int): FixedCell = grid.cells[i] +proc `[]`*(grid: FixedGrid, i: BackwardsIndex): FixedCell = grid.cells[i] +iterator items*(grid: FixedGrid): FixedCell {.inline.} = + for cell in grid.cells: yield cell +proc len*(grid: FixedGrid): int = grid.cells.len +proc high*(grid: FixedGrid): int = grid.cells.high + +const FormatCodes*: array[FormatFlags, tuple[s, e: uint8]] = [ + FLAG_BOLD: (1u8, 22u8), + FLAG_ITALIC: (3u8, 23u8), + FLAG_UNDERLINE: (4u8, 24u8), + FLAG_REVERSE: (7u8, 27u8), + FLAG_STRIKE: (9u8, 29u8), + FLAG_OVERLINE: (53u8, 55u8), + FLAG_BLINK: (5u8, 25u8), +] + +const FormatCodeMap = block: + var res: Table[uint8, tuple[flag: FormatFlags, reverse: bool]] + for x in FormatFlags: + res[FormatCodes[x][0]] = (x, false) + res[FormatCodes[x][1]] = (x, true) + res + +template flag_template(format: Format, val: bool, flag: FormatFlags) = + if val: format.flags.incl(flag) + else: format.flags.excl(flag) + +template `italic=`*(f: var Format, b: bool) = flag_template f, b, FLAG_ITALIC +template `bold=`*(f: var Format, b: bool) = flag_template f, b, FLAG_BOLD +template `underline=`*(f: var Format, b: bool) = flag_template f, b, FLAG_UNDERLINE +template `reverse=`*(f: var Format, b: bool) = flag_template f, b, FLAG_REVERSE +template `strike=`*(f: var Format, b: bool) = flag_template f, b, FLAG_STRIKE +template `overline=`*(f: var Format, b: bool) = flag_template f, b, FLAG_OVERLINE +template `blink=`*(f: var Format, b: bool) = flag_template f, b, FLAG_BLINK + +func newFixedGrid*(w: int, h: int = 1): FixedGrid = + return FixedGrid(width: w, height: h, cells: newSeq[FixedCell](w * h)) + +func width*(line: FlexibleLine): int = + return line.str.width() + +func width*(cell: FixedCell): int = + return cell.str.width() + +func newFormat*(): Format = + return Format(fgcolor: defaultColor, bgcolor: defaultColor) + +# Get the first format cell after pos, if any. +func findFormatN*(line: FlexibleLine|SimpleFlexibleLine, pos: int): int = + var i = 0 + while i < line.formats.len: + if line.formats[i].pos > pos: + break + inc i + return i + +func findFormat*(line: FlexibleLine, pos: int): FormatCell = + let i = line.findFormatN(pos) - 1 + if i != -1: + result = line.formats[i] + else: + result.pos = -1 + +func findFormat*(line: SimpleFlexibleLine, pos: int): SimpleFormatCell = + let i = line.findFormatN(pos) - 1 + if i != -1: + result = line.formats[i] + else: + result.pos = -1 + +func findNextFormat*(line: FlexibleLine, pos: int): FormatCell = + let i = line.findFormatN(pos) + if i < line.formats.len: + result = line.formats[i] + else: + result.pos = -1 + +func findNextFormat*(line: SimpleFlexibleLine, pos: int): SimpleFormatCell = + let i = line.findFormatN(pos) + if i < line.formats.len: + result = line.formats[i] + else: + result.pos = -1 + +proc addLine*(grid: var FlexibleGrid) = + grid.add(FlexibleLine()) + +proc insertFormat*(line: var FlexibleLine, pos, i: int, format: Format, node: StyledNode = nil) = + line.formats.insert(FormatCell(format: format, node: node, pos: pos), i) + +proc addFormat*(line: var FlexibleLine, pos: int, format: Format, node: StyledNode = nil) = + line.formats.add(FormatCell(format: format, node: node, pos: pos)) + +# https://www.ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf +type + AnsiCodeParseState* = enum + PARSE_START, PARSE_PARAMS, PARSE_INTERM, PARSE_FINAL, PARSE_DONE + + AnsiCodeParser* = object + state*: AnsiCodeParseState + params: string + +proc getParam(parser: AnsiCodeParser, i: var int, colon = false): string = + while i < parser.params.len and + not (parser.params[i] == ';' or colon and parser.params[i] == ':'): + result &= parser.params[i] + inc i + if i < parser.params.len: + inc i + +template getParamU8(parser: AnsiCodeParser, i: var int, + colon = false): uint8 = + if i >= parser.params.len: + return false + let u = parseUInt8(parser.getParam(i)) + if u.isNone: + return false + u.get + +proc parseSGRDefColor(parser: AnsiCodeParser, format: var Format, + i: var int, isfg: bool): bool = + let u = parser.getParamU8(i, colon = true) + template set_color(c: CellColor) = + if isfg: + format.fgcolor = c + else: + format.bgcolor = c + if u == 2: + let param0 = parser.getParamU8(i, colon = true) + if i < parser.params.len: + let r = param0 + let g = parser.getParamU8(i, colon = true) + let b = parser.getParamU8(i, colon = true) + set_color cellColor(rgb(r, g, b)) + else: + set_color cellColor(gray(param0)) + elif u == 5: + let param0 = parser.getParamU8(i, colon = true) + if param0 in 0u8..7u8: + set_color cellColor(ANSIColor(param0)) + elif param0 in 8u8..15u8: + format.bold = true + set_color cellColor(ANSIColor(param0 - 8)) + elif param0 in 16u8..255u8: + set_color cellColor(EightBitColor(param0)) + else: + return false + +proc parseSGRColor(parser: AnsiCodeParser, format: var Format, + i: var int, u: uint8): bool = + if u in 30u8..37u8: + format.fgcolor = cellColor(ANSIColor(u - 30)) + elif u == 38: + return parser.parseSGRDefColor(format, i, isfg = true) + elif u == 39: + format.fgcolor = defaultColor + elif u in 40u8..47u8: + format.bgcolor = cellColor(ANSIColor(u - 40)) + elif u == 48: + return parser.parseSGRDefColor(format, i, isfg = false) + elif u == 49: + format.bgcolor = defaultColor + elif u in 90u8..97u8: + format.fgcolor = cellColor(ANSIColor(u - 90u8)) + format.bold = true + elif u in 100u8..107u8: + format.bgcolor = cellColor(ANSIColor(u - 90u8)) + format.bold = true + else: + return false + return true + +proc parseSGRAspect(parser: AnsiCodeParser, format: var Format, + i: var int): bool = + let u = parser.getParamU8(i) + if u in FormatCodeMap: + let entry = FormatCodeMap[u] + if entry.reverse: + format.flags.excl(entry.flag) + else: + format.flags.incl(entry.flag) + return true + elif u == 0: + format = newFormat() + return true + else: + return parser.parseSGRColor(format, i, u) + +proc parseSGR(parser: AnsiCodeParser, format: var Format) = + if parser.params.len == 0: + format = newFormat() + else: + var i = 0 + while i < parser.params.len: + if not parser.parseSGRAspect(format, i): + break + +proc parseControlFunction(parser: var AnsiCodeParser, format: var Format, + f: char) = + case f + of 'm': + parser.parseSGR(format) + else: discard # unknown + +proc reset*(parser: var AnsiCodeParser) = + parser.state = PARSE_START + parser.params = "" + +proc parseAnsiCode*(parser: var AnsiCodeParser, format: var Format, + c: char): bool = + case parser.state + of PARSE_START: + if 0x40 <= int(c) and int(c) <= 0x5F: + if c != '[': + #C1, TODO? + parser.state = PARSE_DONE + else: + parser.state = PARSE_PARAMS + else: + parser.state = PARSE_DONE + return true + of PARSE_PARAMS: + if 0x30 <= int(c) and int(c) <= 0x3F: + parser.params &= c + else: + parser.state = PARSE_INTERM + return parser.parseAnsiCode(format, c) + of PARSE_INTERM: + if 0x20 <= int(c) and int(c) <= 0x2F: + discard + else: + parser.state = PARSE_FINAL + return parser.parseAnsiCode(format, c) + of PARSE_FINAL: + parser.state = PARSE_DONE + if 0x40 <= int(c) and int(c) <= 0x7E: + parser.parseControlFunction(format, c) + else: + return true + of PARSE_DONE: discard diff --git a/src/types/cookie.nim b/src/types/cookie.nim index 141b62e5..6d7ab79a 100644 --- a/src/types/cookie.nim +++ b/src/types/cookie.nim @@ -6,7 +6,7 @@ import js/error import js/javascript import js/regex import types/url -import utils/opt +import types/opt import utils/twtstr type diff --git a/src/types/opt.nim b/src/types/opt.nim new file mode 100644 index 00000000..cdf69633 --- /dev/null +++ b/src/types/opt.nim @@ -0,0 +1,109 @@ +# Inspired by nim-results. + +type + Result*[T, E] = object + when E is void and T is void: # weirdness + has*: bool + elif E is void and not (T is void): # opt + case has*: bool + of true: + val*: T + else: + discard + elif not (E is void) and T is void: # err + case has*: bool + of true: + discard + else: + ex*: E + else: # result + case has*: bool + of true: + val*: T + else: + ex*: E + + Opt*[T] = Result[T, void] + + Err*[E] = Result[void, E] + +template ok*[E](t: type Err[E]): Err[E] = + Err[E](has: true) + +template ok*[T, E](t: type Result[T, E], x: T): Result[T, E] = + Result[T, E](val: x, has: true) + +template ok*[T](x: T): auto = + ok(typeof(result), x) + +template ok*(): auto = + ok(typeof(result)) + +template ok*[T, E](res: var Result[T, E], x: T) = + res = Result[T, E](has: true, val: x) + +template ok*[E](res: var Result[void, E]) = + res = Result[void, E](has: true) + +template err*[T, E](t: type Result[T, E], e: E): Result[T, E] = + Result[T, E](has: false, ex: e) + +template err*[T](t: type Result[T, ref object]): auto = + t(has: false, ex: nil) + +template err*[T](t: type Result[T, void]): Result[T, void] = + Result[T, void](has: false) + +template err*(): auto = + err(typeof(result)) + +template err*[T, E](res: var Result[T, E], e: E) = + res = Result[T, E](has: false, ex: e) + +template err*[T, E](res: var Result[T, E]) = + res = Result[T, E](has: false) + +template err*[E](e: E): auto = + err(typeof(result), e) + +template opt*[T](v: T): auto = + ok(Opt[T], v) + +template opt*(t: typedesc): auto = + err(Result[t, void]) + +template opt*[T, E: not void](r: Result[T, E]): Opt[T] = + if r.isOk: + Opt[T].ok(r.get) + else: + Opt[T].err() + +template isOk*(res: Result): bool = res.has +template isErr*(res: Result): bool = not res.has +template isSome*(res: Result): bool = res.isOk +template isNone*(res: Result): bool = res.isErr +func get*[T, E](res: Result[T, E]): T {.inline.} = res.val +func get*[T, E](res: var Result[T, E]): var T = res.val +func get*[T, E](res: Result[T, E], v: T): T = + if res.has: + res.val + else: + v +func error*[T, E](res: Result[T, E]): E {.inline.} = res.ex +template valType*[T, E](res: type Result[T, E]): auto = T +template errType*[T, E](res: type Result[T, E]): auto = E + +template `?`*[T, E](res: Result[T, E]): auto = + let x = res # for when res is a funcall + if x.has: + when not (T is void): + x.get + else: + discard + else: + when typeof(result) is Result[T, E]: + return x + elif not (E is void) and typeof(result).errType is E: + return err(x.error) + else: + return err() |