diff options
author | bptato <nincsnevem662@gmail.com> | 2021-01-31 19:24:27 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-01-31 19:24:27 +0100 |
commit | 5d79801b655aae7801fb27fc3a9422bdd5764882 (patch) | |
tree | 45f56d26d814231adc7044e6a5f05239aae3ea10 | |
parent | 09feae49e82e36b9a9b9f39e933f78cf7abec1ec (diff) | |
download | chawan-5d79801b655aae7801fb27fc3a9422bdd5764882.tar.gz |
new awful html parser that doesn't even work
-rw-r--r-- | buffer.nim | 404 | ||||
-rw-r--r-- | config | 2 | ||||
-rw-r--r-- | config.nim | 5 | ||||
-rw-r--r-- | display.nim | 178 | ||||
-rw-r--r-- | enums.nim | 37 | ||||
-rw-r--r-- | htmlelement.nim | 27 | ||||
-rw-r--r-- | main.nim | 17 | ||||
-rw-r--r-- | parser.nim | 180 | ||||
-rw-r--r-- | termattrs.nim | 7 | ||||
-rw-r--r-- | twtio.nim | 8 | ||||
-rw-r--r-- | twtstr.nim | 10 |
11 files changed, 545 insertions, 330 deletions
diff --git a/buffer.nim b/buffer.nim index fcc24b76..06459d11 100644 --- a/buffer.nim +++ b/buffer.nim @@ -1,3 +1,5 @@ +#beware, awful code ahead + import options import uri import tables @@ -15,20 +17,19 @@ import twtstr type Buffer* = ref BufferObj BufferObj = object - text*: string - rawtext*: string - lines*: seq[int] - rawlines*: seq[int] + fmttext*: seq[string] + rawtext*: seq[string] title*: string hovertext*: string - htmlSource*: XmlNode + htmlsource*: XmlNode width*: int height*: int - cursorX*: int - cursorY*: int + cursorx*: int + cursory*: int + cursorchar*: int xend*: int - fromX*: int - fromY*: int + fromx*: int + fromy*: int nodes*: seq[HtmlNode] links*: seq[HtmlNode] clickables*: seq[HtmlNode] @@ -40,73 +41,50 @@ type document*: Document proc newBuffer*(attrs: TermAttributes): Buffer = - return Buffer(lines: @[0], - rawlines: @[0], - width: attrs.termWidth, + return Buffer(width: attrs.termWidth, height: attrs.termHeight, - cursorY: 1, - document: newDocument()) + attrs: attrs) func lastLine*(buffer: Buffer): int = - assert(buffer.rawlines.len == buffer.lines.len) - return buffer.lines.len - 1 + assert(buffer.fmttext.len == buffer.rawtext.len) + return buffer.fmttext.len - 1 func lastVisibleLine*(buffer: Buffer): int = - return min(buffer.fromY + buffer.height - 1, buffer.lastLine()) - -func currentLine*(buffer: Buffer): int = - return buffer.cursorY - 1 + return min(buffer.fromy + buffer.height - 1, buffer.lastLine()) -func textBetween*(buffer: Buffer, s: int, e: int): string = - return buffer.text.runeSubstr(s, e - s) - -#doesn't include newline -func lineLength*(buffer: Buffer, line: int): int = - assert buffer.lines.len > line - let str = buffer.textBetween(buffer.lines[line - 1], buffer.lines[line]) - let len = mk_wcswidth_cjk(str) - if len >= 0: - return len - else: - return 0 - -func rawLineLength*(buffer: Buffer, line: int): int = - assert buffer.rawlines.len > line - let str = buffer.textBetween(buffer.rawlines[line - 1], buffer.rawlines[line]) - let len = mk_wcswidth_cjk(str) - if len >= 0: - return len - else: - return 0 +func realCurrentLineLength*(buffer: Buffer): int = + return mk_wcswidth_cjk(buffer.rawtext[buffer.cursory]) func currentLineLength*(buffer: Buffer): int = - return buffer.lineLength(buffer.cursorY) - -func currentRawLineLength*(buffer: Buffer): int = - return buffer.rawLineLength(buffer.cursorY) - -func cursorAtLineEnd*(buffer: Buffer): bool = - return buffer.cursorX == buffer.currentRawLineLength() + return buffer.rawtext[buffer.cursory].runeLen() func atPercentOf*(buffer: Buffer): int = - return (100 * buffer.cursorY) div buffer.lastLine() - + return (100 * buffer.cursory) div buffer.lastLine() + +func fmtBetween*(buffer: Buffer, sx: int, sy: int, ex: int, ey: int): string = + if sy < ey: + result &= buffer.rawtext[sy].runeSubstr(sx) + var i = sy + 1 + while i < ey - 1: + result &= buffer.rawtext[i] + inc i + result &= buffer.rawtext[i].runeSubstr(0, ex - sx) + else: + result &= buffer.rawtext[sy].runeSubstr(sx, ex - sx) func visibleText*(buffer: Buffer): string = - return buffer.textBetween(buffer.lines[buffer.fromY], buffer.lines[buffer.lastVisibleLine()]) + return buffer.fmttext[buffer.fromy..buffer.lastVisibleLine()].join("\n") func lastNode*(buffer: Buffer): HtmlNode = return buffer.nodes[^1] -func onNewLine*(buffer: Buffer): bool = - return buffer.text.len == 0 or buffer.text[^1] == '\n' - -func onSpace*(buffer: Buffer): bool = - return buffer.text.len > 0 and buffer.text[^1] == ' ' - func cursorOnNode*(buffer: Buffer, node: HtmlNode): bool = - return buffer.rawlines[buffer.cursorY - 1] + buffer.cursorX >= node.rawchar and - buffer.rawlines[buffer.cursorY - 1] + buffer.cursorX < node.rawend + if node.y == node.ey and node.y == buffer.cursory: + return buffer.cursorx >= node.x and buffer.cursorx < node.ex + else: + return (buffer.cursory == node.y and buffer.cursorx >= node.x) or + (buffer.cursory > node.y and buffer.cursory < node.ey) or + (buffer.cursory == node.ey and buffer.cursorx < node.ex) func findSelectedElement*(buffer: Buffer): Option[HtmlElement] = if buffer.selectedlink != nil and buffer.selectedLink.parentNode of HtmlElement: @@ -117,9 +95,6 @@ func findSelectedElement*(buffer: Buffer): Option[HtmlElement] = if buffer.cursorOnNode(node): return some(HtmlElement(node)) return none(HtmlElement) -func cursorAt*(buffer: Buffer): int = - return buffer.rawlines[buffer.currentLine()] + buffer.cursorX - func canScroll*(buffer: Buffer): bool = return buffer.lastLine() > buffer.height @@ -131,7 +106,7 @@ func getElementById*(buffer: Buffer, id: string): HtmlElement = proc findSelectedNode*(buffer: Buffer): Option[HtmlNode] = for node in buffer.nodes: if node.getFmtLen() > 0 and node.displayed(): - if buffer.cursorY >= node.y and buffer.cursorY <= node.y + node.height and buffer.cursorX >= node.x and buffer.cursorX <= node.x + node.width: + if buffer.cursory >= node.y and buffer.cursory <= node.y + node.height and buffer.cursorx >= node.x and buffer.cursorx <= node.x + node.width: return some(node) return none(HtmlNode) @@ -160,12 +135,12 @@ proc addNode*(buffer: Buffer, htmlNode: HtmlNode) = buffer.idelements[elem.id] = elem proc writefmt*(buffer: Buffer, str: string) = - buffer.text &= str + buffer.fmttext &= str if buffer.printwrite: stdout.write(str) proc writefmt*(buffer: Buffer, c: char) = - buffer.text &= c + buffer.rawtext &= $c if buffer.printwrite: stdout.write(c) @@ -173,7 +148,7 @@ proc writeraw*(buffer: Buffer, str: string) = buffer.rawtext &= str proc writeraw*(buffer: Buffer, c: char) = - buffer.rawtext &= c + buffer.rawtext &= $c proc write*(buffer: Buffer, str: string) = buffer.writefmt(str) @@ -184,10 +159,8 @@ proc write*(buffer: Buffer, c: char) = buffer.writeraw(c) proc clearText*(buffer: Buffer) = - buffer.text = "" - buffer.rawtext = "" - buffer.lines = @[0] - buffer.rawlines = @[0] + buffer.fmttext.setLen(0) + buffer.rawtext.setLen(0) proc clearNodes*(buffer: Buffer) = buffer.nodes.setLen(0) @@ -199,229 +172,236 @@ proc clearNodes*(buffer: Buffer) = proc clearBuffer*(buffer: Buffer) = buffer.clearText() buffer.clearNodes() - buffer.cursorX = 0 - buffer.cursorY = 1 - buffer.fromX = 0 - buffer.fromY = 0 + buffer.cursorx = 0 + buffer.cursory = 0 + buffer.fromx = 0 + buffer.fromy = 0 buffer.hovertext = "" buffer.selectedlink = nil proc scrollTo*(buffer: Buffer, y: int): bool = - if y == buffer.fromY: + if y == buffer.fromy: return false - buffer.fromY = min(max(buffer.lastLine() - buffer.height + 1, 0), y) - buffer.cursorY = min(max(buffer.fromY, buffer.cursorY), buffer.fromY + buffer.height) + buffer.fromy = min(max(buffer.lastLine() - buffer.height + 1, 0), y) + buffer.cursory = min(max(buffer.fromy, buffer.cursory), buffer.fromy + buffer.height) return true proc cursorTo*(buffer: Buffer, x: int, y: int): bool = result = false - buffer.cursorY = min(max(y, 1), buffer.lastLine()) - if buffer.fromY >= buffer.cursorY: - buffer.fromY = max(buffer.cursorY - 1, 0) + buffer.cursory = min(max(y, 0), buffer.lastLine()) + if buffer.fromy > buffer.cursory: + buffer.fromy = max(buffer.cursory, 0) result = true - elif buffer.fromY + buffer.height <= buffer.cursorY: - buffer.fromY = max(buffer.cursorY - buffer.height + 1, 0) + elif buffer.fromy + buffer.height - 1 <= buffer.cursory: + buffer.fromy = max(buffer.cursory - buffer.height + 2, 0) result = true - buffer.cursorX = min(max(x, 0), buffer.currentRawLineLength()) - buffer.fromX = min(max(buffer.currentRawLineLength() - buffer.width + 1, 0), 0) #TODO + buffer.cursorx = min(max(x, 0), buffer.currentLineLength()) + #buffer.fromX = min(max(buffer.currentLineLength() - buffer.width + 1, 0), 0) #TODO proc cursorDown*(buffer: Buffer): bool = - if buffer.cursorY < buffer.lastLine(): - buffer.cursorY += 1 - if buffer.cursorX > buffer.currentRawLineLength(): - if buffer.xend == 0: - buffer.xend = buffer.cursorX - buffer.cursorX = buffer.currentRawLineLength() + if buffer.cursory < buffer.lastLine(): + inc buffer.cursory + if buffer.cursorx >= buffer.currentLineLength(): + buffer.cursorx = max(buffer.currentLineLength() - 1, 0) elif buffer.xend > 0: - buffer.cursorX = min(buffer.currentRawLineLength(), buffer.xend) - if buffer.cursorY > buffer.lastVisibleLine(): - buffer.fromY += 1 + buffer.cursorx = min(buffer.currentLineLength() - 1, buffer.xend) + if buffer.cursory >= buffer.lastVisibleLine() and buffer.lastVisibleLine() != buffer.lastLine(): + inc buffer.fromy return true return false proc cursorUp*(buffer: Buffer): bool = - if buffer.cursorY > 1: - buffer.cursorY -= 1 - if buffer.cursorX > buffer.currentRawLineLength(): - if buffer.xend == 0: - buffer.xend = buffer.cursorX - buffer.cursorX = buffer.currentRawLineLength() + if buffer.cursory > 0: + dec buffer.cursory + if buffer.cursorx > buffer.currentLineLength(): + if buffer.cursorx == 0: + buffer.xend = buffer.cursorx + buffer.cursorx = buffer.currentLineLength() elif buffer.xend > 0: - buffer.cursorX = min(buffer.currentRawLineLength(), buffer.xend) - if buffer.cursorY <= buffer.fromY: - buffer.fromY -= 1 + buffer.cursorx = min(buffer.currentLineLength(), buffer.xend) + if buffer.cursory < buffer.fromy: + dec buffer.fromy return true return false proc cursorRight*(buffer: Buffer): bool = - if buffer.cursorX < buffer.currentRawLineLength(): - buffer.cursorX += mk_wcwidth_cjk(buffer.rawtext.runeAt(buffer.rawlines[buffer.currentLine()] + buffer.cursorX)) + if buffer.cursorx < buffer.currentLineLength() - 1: + inc buffer.cursorx buffer.xend = 0 else: - buffer.xend = buffer.cursorX + buffer.xend = buffer.cursorx return false proc cursorLeft*(buffer: Buffer): bool = - if buffer.cursorX > 0: - buffer.cursorX -= mk_wcwidth_cjk(buffer.rawtext.runeAt(buffer.rawlines[buffer.currentLine()] + buffer.cursorX)) - buffer.cursorX -= 1 + if buffer.cursorx > 0: + dec buffer.cursorx buffer.xend = 0 return false proc cursorLineBegin*(buffer: Buffer) = - buffer.cursorX = 0 + buffer.cursorx = 0 buffer.xend = 0 proc cursorLineEnd*(buffer: Buffer) = - buffer.cursorX = buffer.currentRawLineLength() - buffer.xend = buffer.cursorX + buffer.cursorx = buffer.currentLineLength() - 1 + buffer.xend = buffer.cursorx iterator revnodes*(buffer: Buffer): HtmlNode {.inline.} = var i = buffer.nodes.len - 1 while i >= 0: yield buffer.nodes[i] - i -= 1 - -proc cursorNextNode*(buffer: Buffer): bool = - for node in buffer.nodes: - if node.displayed(): - if node.y > buffer.cursorY or (node.y == buffer.cursorY and node.x > buffer.cursorX): - return buffer.cursorTo(node.x, node.y) - buffer.cursorLineEnd() - return false - -proc cursorPrevNode*(buffer: Buffer): bool = - var prevnode: HtmlNode - for node in buffer.nodes: - if node.displayed(): - if node.y >= buffer.cursorY and node.x >= buffer.cursorX and prevnode != nil: - return buffer.cursorTo(prevnode.x, prevnode.y) - prevnode = node - buffer.cursorLineBegin() - return false + dec i proc cursorNextWord*(buffer: Buffer): bool = - if buffer.cursorAtLineEnd(): - if buffer.cursorY < buffer.lastLine(): - let ret = buffer.cursorDown() - buffer.cursorLineBegin() - return ret - else: - buffer.cursorLineEnd() - return false - - var res = buffer.cursorRight() - while buffer.rawtext.runeAt(buffer.rawlines[buffer.currentLine()] + buffer.cursorX) != Rune(' '): - if buffer.cursorAtLineEnd(): - return res - res = res or buffer.cursorRight() + let llen = buffer.currentLineLength() - 1 + var r: Rune + var x = buffer.cursorx + var y = buffer.cursory + fastRuneAt(buffer.rawtext[y], x, r, false) + + while r != Rune(' '): + if x >= llen: + break + inc x + fastRuneAt(buffer.rawtext[y], x, r, false) + + while r == Rune(' '): + if x >= llen: + break + inc x + fastRuneAt(buffer.rawtext[y], x, r, false) + + if x >= llen: + if y < buffer.lastLine(): + inc y + x = 0 + return buffer.cursorTo(x, y) proc cursorPrevWord*(buffer: Buffer): bool = - if buffer.cursorX <= 1: - if buffer.cursorY > 1: - let ret = buffer.cursorUp() - buffer.cursorLineEnd() - return ret - else: - buffer.cursorLineBegin() - return false - - discard buffer.cursorLeft() - while buffer.rawtext.runeAt(buffer.rawlines[buffer.currentLine()] + buffer.cursorX) != Rune(' '): - if buffer.cursorX == 0: - return false - discard buffer.cursorLeft() + var r: Rune + var x = buffer.cursorx + var y = buffer.cursory + fastRuneAt(buffer.rawtext[y], x, r, false) + + while r != Rune(' '): + if x == 0: + break + dec x + fastRuneAt(buffer.rawtext[y], x, r, false) + + while r == Rune(' '): + if x == 0: + break + dec x + fastRuneAt(buffer.rawtext[y], x, r, false) + + if x == 0: + if y < buffer.lastLine(): + dec y + x = buffer.rawtext[y].runeLen() - 1 + return buffer.cursorTo(x, y) iterator revclickables*(buffer: Buffer): HtmlNode {.inline.} = var i = buffer.clickables.len - 1 while i >= 0: yield buffer.clickables[i] - i -= 1 + dec i proc cursorNextLink*(buffer: Buffer): bool = for node in buffer.clickables: - if node.y > buffer.cursorY or (node.y == buffer.cursorY and node.x > buffer.cursorX): - return buffer.cursorTo(node.x, node.y) + if node.y > buffer.cursory or (node.y == buffer.cursorY and node.x > buffer.cursorx): + result = buffer.cursorTo(node.x, node.y) + if buffer.cursorx < buffer.currentLineLength(): + var r: Rune + fastRuneAt(buffer.rawtext[buffer.cursory], buffer.cursorx, r, false) + if r == Rune(' '): + return result or buffer.cursorNextWord() + return result return false proc cursorPrevLink*(buffer: Buffer): bool = for node in buffer.revclickables: - if node.y < buffer.cursorY or (node.y == buffer.cursorY and node.x < buffer.cursorX): + if node.y < buffer.cursorY or (node.y == buffer.cursorY and node.x < buffer.cursorx): return buffer.cursorTo(node.x, node.y) return false proc cursorFirstLine*(buffer: Buffer): bool = - if buffer.fromY > 0: - buffer.fromY = 0 + if buffer.fromy > 0: + buffer.fromy = 0 result = true else: result = false - buffer.cursorY = 1 + buffer.cursorY = 0 buffer.cursorLineBegin() proc cursorLastLine*(buffer: Buffer): bool = - if buffer.fromY < buffer.lastLine() - buffer.height: - buffer.fromY = buffer.lastLine() - (buffer.height - 1) + if buffer.fromy < buffer.lastLine() - buffer.height: + buffer.fromy = buffer.lastLine() - (buffer.height - 2) result = true else: result = false - buffer.cursorY = buffer.lastLine() + buffer.cursory = buffer.lastLine() buffer.cursorLineBegin() +proc cursorTop*(buffer: Buffer): bool = + buffer.cursorY = buffer.fromy + return false + +proc cursorMiddle*(buffer: Buffer): bool = + buffer.cursorY = min(buffer.fromy + (buffer.height - 2) div 2, buffer.lastLine()) + return false + +proc cursorBottom*(buffer: Buffer): bool = + buffer.cursorY = min(buffer.fromy + buffer.height - 2, buffer.lastLine()) + return false + +proc centerLine*(buffer: Buffer): bool = + let ny = max(min(buffer.cursory - buffer.height div 2, buffer.lastLine() - buffer.height + 2), 0) + if ny != buffer.fromy: + buffer.fromy = ny + return true + return false + proc halfPageUp*(buffer: Buffer): bool = - buffer.cursorY = max(buffer.cursorY - buffer.height div 2 + 1, 1) - if buffer.fromY - 1 > buffer.cursorY or true: - buffer.fromY = max(0, buffer.fromY - buffer.height div 2 + 1) + buffer.cursory = max(buffer.cursorY - buffer.height div 2 + 1, 0) + let nfy = max(0, buffer.fromy - buffer.height div 2 + 1) + if nfy != buffer.fromy: + buffer.fromy = nfy return true return false proc halfPageDown*(buffer: Buffer): bool = - buffer.cursorY = min(buffer.cursorY + buffer.height div 2 - 1, buffer.lastLine()) - buffer.fromY = min(max(buffer.lastLine() - buffer.height + 1, 0), buffer.fromY + buffer.height div 2 - 1) - return true + buffer.cursory = min(buffer.cursorY + buffer.height div 2 - 1, buffer.lastLine()) + let nfy = min(max(buffer.lastLine() - buffer.height + 2, 0), buffer.fromy + buffer.height div 2 - 1) + if nfy != buffer.fromy: + buffer.fromy = nfy + return true + return false proc pageUp*(buffer: Buffer): bool = buffer.cursorY = max(buffer.cursorY - buffer.height + 1, 1) - buffer.fromY = max(0, buffer.fromY - buffer.height) + buffer.fromy = max(0, buffer.fromy - buffer.height) return true proc pageDown*(buffer: Buffer): bool = buffer.cursorY = min(buffer.cursorY + buffer.height div 2 - 1, buffer.lastLine()) - buffer.fromY = min(max(buffer.lastLine() - buffer.height + 1, 0), buffer.fromY + buffer.height div 2) - return true - -proc cursorTop*(buffer: Buffer): bool = - buffer.cursorY = buffer.fromY + 1 - return false - -proc cursorMiddle*(buffer: Buffer): bool = - buffer.cursorY = min(buffer.fromY + buffer.height div 2, buffer.lastLine()) - return false - -proc cursorBottom*(buffer: Buffer): bool = - buffer.cursorY = min(buffer.fromY + buffer.height - 1, buffer.lastLine()) - return false - -proc centerLine*(buffer: Buffer): bool = - if min(buffer.cursorY - buffer.height div 2, buffer.lastLine()) == buffer.fromY: - return false - buffer.fromY = min(buffer.cursorY - buffer.height div 2, buffer.lastLine()) + buffer.fromy = min(max(buffer.lastLine() - buffer.height + 1, 0), buffer.fromy + buffer.height div 2) return true proc scrollDown*(buffer: Buffer): bool = - if buffer.fromY + buffer.height <= buffer.lastLine(): - buffer.fromY += 1 - if buffer.fromY >= buffer.cursorY: + if buffer.fromy + buffer.height - 1 <= buffer.lastLine(): + inc buffer.fromy + if buffer.fromy >= buffer.cursory: discard buffer.cursorDown() return true discard buffer.cursorDown() return false proc scrollUp*(buffer: Buffer): bool = - if buffer.fromY > 0: - buffer.fromY -= 1 - if buffer.fromY + buffer.height <= buffer.cursorY: + if buffer.fromy > 0: + dec buffer.fromy + if buffer.fromy + buffer.height - 1 <= buffer.cursorY: discard buffer.cursorUp() return true discard buffer.cursorUp() @@ -437,6 +417,13 @@ proc checkLinkSelection*(buffer: Buffer): bool = buffer.selectedlink.fmttext = buffer.selectedlink.getFmtText() buffer.selectedlink = nil buffer.hovertext = "" + var stack: seq[HtmlNode] + stack.add(anchor) + while stack.len > 0: + let elem = stack.pop() + elem.fmttext = elem.getFmtText() + for child in elem.childNodes: + stack.add(child) for node in buffer.links: if buffer.cursorOnNode(node): buffer.selectedlink = node @@ -444,7 +431,13 @@ proc checkLinkSelection*(buffer: Buffer): bool = assert(anchor != nil) anchor.selected = true buffer.hovertext = HtmlAnchorElement(anchor).href - node.fmttext = node.getFmtText() + var stack: seq[HtmlNode] + stack.add(anchor) + while stack.len > 0: + let elem = stack.pop() + elem.fmttext = elem.getFmtText() + for child in elem.childNodes: + stack.add(child) return true return false @@ -460,3 +453,12 @@ proc setLocation*(buffer: Buffer, uri: Uri) = proc gotoLocation*(buffer: Buffer, uri: Uri) = buffer.document.location = buffer.document.location.combine(uri) + +proc refreshTermAttrs*(buffer: Buffer): bool = + let newAttrs = getTermAttributes() + if newAttrs != buffer.attrs: + buffer.attrs = newAttrs + buffer.width = newAttrs.termWidth + buffer.height = newAttrs.termHeight + return true + return false diff --git a/config b/config index 48e81660..5724dbc8 100644 --- a/config +++ b/config @@ -11,9 +11,7 @@ nmap \e[C ACTION_CURSOR_RIGHT nmap ^ ACTION_CURSOR_LINEBEGIN nmap $ ACTION_CURSOR_LINEEND nmap b ACTION_CURSOR_PREV_WORD -nmap B ACTION_CURSOR_PREV_NODE nmap w ACTION_CURSOR_NEXT_WORD -nmap W ACTION_CURSOR_NEXT_NODE nmap [ ACTION_CURSOR_PREV_LINK nmap ] ACTION_CURSOR_NEXT_LINK nmap H ACTION_CURSOR_TOP diff --git a/config.nim b/config.nim index 8df30caf..abdd74db 100644 --- a/config.nim +++ b/config.nim @@ -13,7 +13,6 @@ type ACTION_CURSOR_UP, ACTION_CURSOR_DOWN, ACTION_CURSOR_LEFT, ACTION_CURSOR_RIGHT, ACTION_CURSOR_LINEEND, ACTION_CURSOR_LINEBEGIN, ACTION_CURSOR_NEXT_WORD, ACTION_CURSOR_PREV_WORD, - ACTION_CURSOR_NEXT_NODE, ACTION_CURSOR_PREV_NODE, ACTION_CURSOR_NEXT_LINK, ACTION_CURSOR_PREV_LINK, ACTION_PAGE_DOWN, ACTION_PAGE_UP, ACTION_HALF_PAGE_DOWN, ACTION_HALF_PAGE_UP, @@ -48,10 +47,10 @@ proc getRealKey(key: string): string = realk &= c skip = false elif c == 'C': - control += 1 + inc control currchar = c elif c == '-' and control == 1: - control += 1 + inc control elif control == 1: realk &= 'C' & c control = 0 diff --git a/display.nim b/display.nim index 30081c57..78401598 100644 --- a/display.nim +++ b/display.nim @@ -27,8 +27,8 @@ type x: int y: int lastwidth: int - atchar: int - atrawchar: int + fmtline: string + rawline: string centerqueue: seq[HtmlNode] centerlen: int blanklines: int @@ -39,30 +39,35 @@ type listval: int func newRenderState(): RenderState = - return RenderState(y: 1) + return RenderState() + +proc write(state: var RenderState, s: string) = + state.fmtline &= s + state.rawline &= s + +proc write(state: var RenderState, fs: string, rs: string) = + state.fmtline &= fs + state.rawline &= rs proc flushLine(buffer: Buffer, state: var RenderState) = - if buffer.onNewLine(): - state.blanklines += 1 - buffer.write('\n') + if state.rawline.len == 0: + inc state.blanklines + assert(state.rawline.runeLen() < buffer.width, "line too long:\n" & state.rawline) + buffer.writefmt(state.fmtline) + buffer.writeraw(state.rawline) state.x = 0 - state.y += 1 - state.atchar += 1 - state.atrawchar += 1 + inc state.y state.nextspaces = 0 - buffer.lines.add(state.atchar) - buffer.rawlines.add(state.atrawchar) - assert(buffer.onNewLine()) + state.fmtline = "" + state.rawline = "" proc addSpaces(buffer: Buffer, state: var RenderState, n: int) = if state.x + n > buffer.width: buffer.flushLine(state) return state.blankspaces += n - buffer.write(' '.repeat(n)) + state.write(' '.repeat(n)) state.x += n - state.atchar += n - state.atrawchar += n proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) = state.lastwidth = 0 @@ -77,17 +82,12 @@ proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) = for r in w.runes: if r == Rune(' '): - var s: Rune - fastRuneAt(rawword, 0, s, false) - #if s == Rune(' ') and prevl: - # fmtword = fmtword.runeSubstr(1) - # rawword = rawword.runeSubstr(1) - # state.x -= 1 - buffer.writefmt(fmtword) - buffer.writeraw(rawword) - state.atchar += fmtword.runeLen() - state.atrawchar += rawword.runeLen() - var a = rawword + if rawword[0] == ' ' and prevl: #first byte can't fool comparison to ascii + fmtword = fmtword.substr(1) + rawword = rawword.substr(1) + state.x -= 1 + prevl = false + state.write(fmtword, rawword) fmtword = "" rawword = "" @@ -96,29 +96,26 @@ proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) = state.x += mk_wcwidth_cjk(r) - if prevl: - state.x += mk_wcswidth_cjk(rawword) - prevl = false - - if state.x > buffer.width: + if state.x >= buffer.width: state.lastwidth = max(state.lastwidth, state.x) buffer.flushLine(state) - state.x = -1 + state.x = mk_wcswidth_cjk(rawword) prevl = true else: state.lastwidth = max(state.lastwidth, state.x) - n += 1 + inc n + + state.write(fmtword, rawword) + if prevl: + state.x += mk_wcswidth_cjk(rawword) + prevl = false - buffer.writefmt(fmtword) - buffer.writeraw(rawword) - state.atchar += fmtword.runeLen() - state.atrawchar += rawword.runeLen() state.lastwidth = max(state.lastwidth, state.x) proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = let elem = node.nodeAttr() - if not buffer.onNewLine() and node.openblock and state.blanklines == 0: + if state.rawline.len > 0 and node.openblock and state.blanklines == 0: buffer.flushLine(state) if node.openblock: @@ -126,13 +123,13 @@ proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = buffer.flushLine(state) state.indent += elem.indent - if not buffer.onNewLine() and state.blanklines == 0 and node.displayed(): + if state.rawline.len > 0 and state.blanklines == 0 and node.displayed(): buffer.addSpaces(state, state.nextspaces) state.nextspaces = 0 if state.blankspaces < max(elem.margin, elem.marginleft): buffer.addSpaces(state, max(elem.margin, elem.marginleft) - state.blankspaces) - if elem.centered and buffer.onNewLine() and node.displayed(): + if elem.centered and state.rawline.len == 0 and node.displayed(): buffer.addSpaces(state, max(buffer.width div 2 - state.centerlen div 2, 0)) state.centerlen = 0 @@ -143,15 +140,13 @@ proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = of TAG_UL: listchar = "•" of TAG_OL: - state.listval += 1 + inc state.listval listchar = $state.listval & ")" else: return buffer.addSpaces(state, state.indent) - buffer.write(listchar) + state.write(listchar) state.x += listchar.runeLen() - state.atchar += listchar.runeLen() - state.atrawchar += listchar.runeLen() buffer.addSpaces(state, 1) proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = @@ -161,9 +156,10 @@ proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = state.blanklines = 0 state.blankspaces = 0 - if not buffer.onNewLine() and state.blanklines == 0: + if state.rawline.len > 0 and state.blanklines == 0: state.nextspaces += max(elem.margin, elem.marginright) if node.closeblock and (node.isTextNode() or elem.numChildNodes == 0): + state.write($node.nodeAttr().tagType) buffer.flushLine(state) if node.closeblock: @@ -176,6 +172,8 @@ proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = buffer.flushLine(state) proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = + if node.isDocument() or node.parentNode == nil: + return let elem = node.nodeAttr() if elem.tagType == TAG_TITLE: if node.isTextNode(): @@ -213,11 +211,9 @@ proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = node.x = state.x node.y = state.y - node.fmtchar = state.atchar - node.rawchar = state.atrawchar buffer.writeWrappedText(state, node) - node.fmtend = state.atchar - node.rawend = state.atrawchar + node.ex = state.x + node.ey = state.y node.width = state.lastwidth - node.x - 1 node.height = state.y - node.y + 1 @@ -237,10 +233,8 @@ type html*: HtmlNode proc setLastHtmlLine(buffer: Buffer, state: var RenderState) = - if buffer.text.len != buffer.lines[^1]: - state.atchar = buffer.text.len - state.atrawchar = buffer.rawtext.runeLen() - buffer.flushLine(state) + if state.rawline.len != 0: + buffer.flushLine(state) proc renderHtml*(buffer: Buffer) = var stack: seq[XmlHtmlNode] @@ -263,12 +257,30 @@ proc renderHtml*(buffer: Buffer) = if not last and not child.html.hidden: last = true if HtmlElement(currElem.html).display == DISPLAY_BLOCK: + eprint "elem", HtmlElement(currElem.html).tagType, "close @", child.html.nodeAttr().tagType stack[^1].html.closeblock = true if last: + eprint "elem", HtmlElement(currElem.html).tagType, "open @", stack[^1].html.nodeAttr().tagType if HtmlElement(currElem.html).display == DISPLAY_BLOCK: stack[^1].html.openblock = true buffer.setLastHtmlLine(state) +proc nrenderHtml*(buffer: Buffer) = + var stack: seq[HtmlNode] + let first = buffer.document + stack.add(first) + + var state = newRenderState() + while stack.len > 0: + let currElem = stack.pop() + buffer.addNode(currElem) + buffer.renderNode(currElem, state) + if currElem.childNodes.len > 0: + for item in currElem.childNodes: + stack.add(item) + + buffer.setLastHtmlLine(state) + proc drawHtml(buffer: Buffer) = var state = newRenderState() for node in buffer.nodes: @@ -276,7 +288,7 @@ proc drawHtml(buffer: Buffer) = buffer.setLastHtmlLine(state) proc statusMsgForBuffer(buffer: Buffer) = - var msg = $buffer.cursorY & "/" & $buffer.lastLine() & " (" & + var msg = $buffer.cursory & "/" & $buffer.lastLine() & " (" & $buffer.atPercentOf() & "%) " & "<" & buffer.title & ">" if buffer.hovertext.len > 0: @@ -284,9 +296,9 @@ proc statusMsgForBuffer(buffer: Buffer) = statusMsg(msg.maxString(buffer.width), buffer.height) proc cursorBufferPos(buffer: Buffer) = - var x = buffer.cursorX - var y = buffer.cursorY - 1 - buffer.fromY - termGoto(x, y) + var x = buffer.cursorx + var y = buffer.cursory - 1 - buffer.fromY + termGoto(x, y + 1) proc displayBuffer(buffer: Buffer) = eraseScreen() @@ -298,7 +310,8 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool = var s = "" var feedNext = false while true: - cursorBufferPos(buffer) + stdout.showCursor() + buffer.cursorBufferPos() if not feedNext: s = "" else: @@ -320,9 +333,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool = of ACTION_CURSOR_LINEBEGIN: buffer.cursorLineBegin() of ACTION_CURSOR_LINEEND: buffer.cursorLineEnd() of ACTION_CURSOR_NEXT_WORD: redraw = buffer.cursorNextWord() - of ACTION_CURSOR_NEXT_NODE: redraw = buffer.cursorNextNode() of ACTION_CURSOR_PREV_WORD: redraw = buffer.cursorPrevWord() - of ACTION_CURSOR_PREV_NODE: redraw = buffer.cursorPrevNode() of ACTION_CURSOR_NEXT_LINK: redraw = buffer.cursorNextLink() of ACTION_CURSOR_PREV_LINK: redraw = buffer.cursorPrevLink() of ACTION_PAGE_DOWN: redraw = buffer.pageDown() @@ -361,7 +372,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool = buffer.setLocation(parseUri(url)) return true of ACTION_LINE_INFO: - statusMsg("line " & $buffer.cursorY & "/" & $buffer.lastLine() & " col " & $buffer.cursorX & "/" & $buffer.currentRawLineLength(), buffer.width) + statusMsg("line " & $buffer.cursory & "/" & $buffer.lastLine() & " col " & $buffer.cursorx & "/" & $buffer.realCurrentLineLength(), buffer.width) nostatus = true of ACTION_FEED_NEXT: feedNext = true @@ -371,6 +382,37 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool = redraw = true of ACTION_REDRAW: redraw = true else: discard + stdout.hideCursor() + + let prevlink = buffer.selectedlink + let sel = buffer.checkLinkSelection() + if sel: + buffer.clearText() + buffer.drawHtml() + termGoto(0, buffer.selectedlink.y - buffer.fromy) + stdout.eraseLine() + for i in buffer.selectedlink.y..buffer.selectedlink.ey: + if i < buffer.fromy + buffer.height - 1: + let line = buffer.fmttext[i] + print(line) + print('\n') + print("".ansiReset()) + + if prevlink != nil: + buffer.clearText() + buffer.drawHtml() + termGoto(0, prevlink.y - buffer.fromy) + for i in prevlink.y..prevlink.ey: + if i < buffer.fromy + buffer.height - 1: + let line = buffer.fmttext[i] + stdout.eraseLine() + print(line) + print('\n') + print("".ansiReset()) + + if buffer.refreshTermAttrs(): + redraw = true + reshape = true if reshape: buffer.clearText() @@ -378,20 +420,6 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool = if redraw: buffer.displayBuffer() - let prevlink = buffer.selectedlink - let sel = buffer.checkLinkSelection() - if prevlink != nil and prevlink != buffer.selectedlink: - termGoto(prevlink.x - buffer.fromX, prevlink.y - buffer.fromY - 1) - print(buffer.textBetween(prevlink.fmtchar, prevlink.fmtend).ansiReset()) - if sel and buffer.selectedlink.y < buffer.fromY + buffer.height: - termGoto(buffer.selectedlink.x - buffer.fromX, buffer.selectedlink.y - buffer.fromY - 1) - let str = buffer.textBetween(buffer.selectedlink.fmtchar, buffer.selectedlink.fmtend) - #var i = str.findChar(Rune('\n')) - #while i != -1: - # print("".ansiStyle(styleUnderscore)) - # i = str.findChar(Rune('\n'), i + 1) - print(str.ansiStyle(styleUnderscore).ansiReset()) - if not nostatus: buffer.statusMsgForBuffer() else: diff --git a/enums.nim b/enums.nim index e11059d0..e95ca6ac 100644 --- a/enums.nim +++ b/enums.nim @@ -5,7 +5,8 @@ type DisplayType* = enum - DISPLAY_INLINE, DISPLAY_BLOCK, DISPLAY_SINGLE, DISPLAY_LIST_ITEM, DISPLAY_NONE + DISPLAY_INLINE, DISPLAY_BLOCK, DISPLAY_LIST_ITEM, DISPLAY_TABLE_COLUMN, + DISPLAY_INLINE_BLOCK, DISPLAY_NONE InputType* = enum @@ -32,35 +33,47 @@ type TAG_CODE, TAG_DATA, TAG_DFN, TAG_EM, TAG_I, TAG_KBD, TAG_MARK, TAG_Q, TAG_RB, TAG_RP, TAG_RT, TAG_RTC, TAG_RUBY, TAG_S, TAG_SAMP, TAG_SMALL, TAG_SPAN, TAG_STRONG, TAG_SUB, TAG_SUP, TAG_TIME, TAG_U, TAG_VAR, TAG_WBR, - TAG_AREA, TAG_AUDIO, TAG_IMG, TAG_MAP, TAG_TRACK, TAG_VIDEO, TAG_EMBED, + TAG_AREA, TAG_AUDIO, TAG_IMG, TAG_MAP, TAG_TRACK, TAG_VIDEO, TAG_IFRAME, TAG_OBJECT, TAG_PARAM, TAG_PICTURE, TAG_PORTAL, TAG_SOURCE, TAG_CANVAS, TAG_NOSCRIPT, TAG_SCRIPT, TAG_DEL, TAG_INS, TAG_CAPTION, TAG_COL, TAG_COLGROUP, TAG_TABLE, TAG_TBODY, TAG_TD, TAG_TFOOT, TAG_TH, TAG_THEAD, TAG_TR, TAG_BUTTON, TAG_DATALIST, TAG_FIELDSET, TAG_FORM, TAG_INPUT, TAG_LABEL, TAG_LEGEND, TAG_METER, TAG_OPTGROUP, TAG_OPTION, TAG_OUTPUT, TAG_PROGRESS, TAG_SELECT, TAG_TEXTAREA, TAG_DETAILS, - TAG_DIALOG, TAG_MENU, TAG_SUMMARY, TAG_BLINK, TAG_CENTER, TAG_COMMAND, - TAG_CONTENT, TAG_DIR, TAG_FONT, TAG_FRAME, TAG_NOFRAMES, TAG_FRAMESET, - TAG_STRIKE, TAG_TT + TAG_DIALOG, TAG_MENU, TAG_SUMMARY, TAG_BLINK, TAG_CENTER, TAG_CONTENT, + TAG_DIR, TAG_FONT, TAG_FRAME, TAG_NOFRAMES, TAG_FRAMESET, TAG_STRIKE, TAG_TT -const InlineTagTypes* = { +const DisplayInlineTags* = { TAG_A, TAG_ABBR, TAG_B, TAG_BDO, TAG_BR, TAG_BUTTON, TAG_CITE, TAG_CODE, TAG_DEL, TAG_DFN, TAG_EM, TAG_FONT, TAG_I, TAG_IMG, TAG_INS, TAG_INPUT, TAG_IFRAME, TAG_KBD, TAG_LABEL, TAG_MAP, TAG_OBJECT, TAG_Q, TAG_SAMP, TAG_SCRIPT, TAG_SELECT, TAG_SMALL, TAG_SPAN, TAG_STRONG, TAG_SUB, TAG_SUP, TAG_TEXTAREA, TAG_TT, TAG_VAR, TAG_FONT, TAG_IFRAME, TAG_U, TAG_S, TAG_STRIKE, - TAG_WBR + TAG_FRAME, TAG_IMG, TAG_INPUT } -const BlockTagTypes* = { +const DisplayNoneTags* = { + TAG_AREA, TAG_BASE, TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR +} + +const DisplayInlineBlockTags* = { + TAG_IMG +} + +const DisplayTableColumnTags* = { + TAG_COL +} + +const DisplayBlockTags* = { TAG_ADDRESS, TAG_BLOCKQUOTE, TAG_CENTER, TAG_DEL, TAG_DIR, TAG_DIV, TAG_DL, TAG_FIELDSET, TAG_FORM, TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6, TAG_HR, TAG_INS, TAG_MENU, TAG_NOFRAMES, TAG_NOSCRIPT, TAG_OL, TAG_P, TAG_PRE, - TAG_TABLE, TAG_UL, TAG_CENTER, TAG_DIR, TAG_MENU, TAG_NOFRAMES + TAG_TABLE, TAG_UL, TAG_CENTER, TAG_DIR, TAG_MENU, TAG_NOFRAMES, TAG_BODY, + #single + TAG_HR } const SingleTagTypes* = { - TAG_AREA, TAG_BASE, TAG_BR, TAG_COL, TAG_EMBED, TAG_FRAME, TAG_HR, TAG_IMG, - TAG_INPUT, TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR, - TAG_COMMAND + TAG_AREA, TAG_BASE, TAG_BR, TAG_COL, TAG_FRAME, TAG_HR, TAG_IMG, TAG_INPUT, + TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR, TAG_LI } diff --git a/htmlelement.nim b/htmlelement.nim index 3ead2f15..7c0d9169 100644 --- a/htmlelement.nim +++ b/htmlelement.nim @@ -3,7 +3,6 @@ import terminal import uri import unicode -import fusion/htmlparser import fusion/htmlparser/xmltree import twtstr @@ -28,10 +27,8 @@ type fmttext*: seq[string] x*: int y*: int - fmtchar*: int - rawchar*: int - fmtend*: int - rawend*: int + ex*: int + ey*: int width*: int height*: int openblock*: bool @@ -45,8 +42,8 @@ type HtmlElement* = ref HtmlElementObj HtmlElementObj = object of HtmlNodeObj id*: string + class*: string tagType*: TagType - name*: string centered*: bool display*: DisplayType innerText*: string @@ -77,6 +74,7 @@ type HtmlSelectElement* = ref HtmlSelectElementObj HtmlSelectElementObj = object of HtmlElementObj + name*: string value*: string HtmlOptionElement* = ref HtmlOptionElementObj @@ -103,7 +101,7 @@ macro genEnumCase(s: string): untyped = branch.add(ret) casestmt.add(branch) -func tagType*(s: string): TagType = +func tagType(s: string): TagType = genEnumCase(s) func nodeAttr*(node: HtmlNode): HtmlElement = @@ -192,6 +190,9 @@ func ancestor*(htmlNode: HtmlNode, tagType: TagType): HtmlElement = while result != nil and result.tagType != tagType: result = result.parentElement +func displayWhitespace*(htmlElem: HtmlElement): bool = + return htmlElem.display == DISPLAY_INLINE or htmlElem.display == DISPLAY_INLINE_BLOCK + proc getRawText*(htmlNode: HtmlNode): string = if htmlNode.isElemNode(): case HtmlElement(htmlNode).tagType @@ -202,9 +203,9 @@ proc getRawText*(htmlNode: HtmlNode): string = result = htmlNode.rawtext.remove("\n") if unicode.strip(result).runeLen() > 0: if htmlNode.nodeAttr().display != DISPLAY_INLINE: - if htmlNode.previousSibling == nil or htmlNode.previousSibling.nodeAttr().display != DISPLAY_INLINE: + if htmlNode.previousSibling == nil or htmlNode.previousSibling.nodeAttr().displayWhitespace(): result = unicode.strip(result, true, false) - if htmlNode.nextSibling == nil or htmlNode.nextSibling.nodeAttr().display != DISPLAY_INLINE: + if htmlNode.nextSibling == nil or htmlNode.nextSibling.nodeAttr().displayWhitespace(): result = unicode.strip(result, false, true) else: result = "" @@ -259,12 +260,12 @@ proc getHtmlElement*(xmlElement: XmlNode, parentNode: HtmlNode): HtmlElement = result.id = xmlElement.attr("id") - if tagType in InlineTagTypes: + if tagType in DisplayInlineTags: result.display = DISPLAY_INLINE - elif tagType in BlockTagTypes: + elif tagType in DisplayBlockTags: result.display = DISPLAY_BLOCK - elif tagType in SingleTagTypes: - result.display = DISPLAY_SINGLE + elif tagType in DisplayInlineBlockTags: + result.display = DISPLAY_INLINE_BLOCK elif tagType == TAG_LI: result.display = DISPLAY_LIST_ITEM else: diff --git a/main.nim b/main.nim index db4b0f6d..b6ee8cb2 100644 --- a/main.nim +++ b/main.nim @@ -1,6 +1,7 @@ import httpClient import uri import os +import streams import fusion/htmlparser import fusion/htmlparser/xmltree @@ -10,13 +11,19 @@ import termattrs import buffer import twtio import config +import twtstr +import parser +let clientInstance = newHttpClient() proc loadRemotePage*(url: string): string = - return newHttpClient().getContent(url) + return clientInstance.getContent(url) proc loadLocalPage*(url: string): string = return readFile(url) +proc getRemotePage*(url: string): Stream = + return clientInstance.get(url).bodyStream + proc loadPageUri(uri: Uri, currentcontent: XmlNode): XmlNode = var moduri = uri moduri.anchor = "" @@ -25,6 +32,7 @@ proc loadPageUri(uri: Uri, currentcontent: XmlNode): XmlNode = elif uri.scheme == "" or uri.scheme == "file": return parseHtml(loadLocalPage($moduri)) else: + #return nparseHtml(getRemotePage($moduri)) return parseHtml(loadRemotePage($moduri)) var buffers: seq[Buffer] @@ -55,4 +63,9 @@ proc main*() = lastUri = newUri #waitFor loadPage("https://lite.duckduckgo.com/lite/?q=hello%20world") -main() +#eprint mk_wcswidth_cjk("abc•de") +var buf = newBuffer(getTermAttributes()) +buf.document = nparseHtml(getRemotePage("http://lite.duckduckgo.com")) +buf.nrenderHtml() +discard displayPage(getTermAttributes(), buf) +#main() diff --git a/parser.nim b/parser.nim index 591a43e6..d69accf3 100644 --- a/parser.nim +++ b/parser.nim @@ -1,17 +1,179 @@ import parsexml import htmlelement import streams +import macros -func parseNextNode(str: string) = - return +import twtio +import enums +import strutils + +#> no I won't manually write all this down +#> maybe todo to accept stuff other than tagtype (idk how useful that'd be) +#still todo, it'd be very useful +macro genEnumCase(s: string): untyped = + let casestmt = nnkCaseStmt.newTree() + casestmt.add(ident("s")) + for i in low(TagType) .. high(TagType): + let ret = nnkReturnStmt.newTree() + ret.add(newLit(TagType(i))) + let branch = nnkOfBranch.newTree() + let enumname = $TagType(i) + let tagname = enumname.substr("TAG_".len, enumname.len - 1).tolower() + branch.add(newLit(tagname)) + branch.add(ret) + casestmt.add(branch) + let ret = nnkReturnStmt.newTree() + ret.add(newLit(TAG_UNKNOWN)) + let branch = nnkElse.newTree() + branch.add(ret) + casestmt.add(branch) + +func tagType(s: string): TagType = + genEnumCase(s) + +func newHtmlElement(tagType: TagType): HtmlElement = + case tagType + of TAG_INPUT: result = new(HtmlInputElement) + of TAG_A: result = new(HtmlAnchorElement) + of TAG_SELECT: result = new(HtmlSelectElement) + of TAG_OPTION: result = new(HtmlOptionElement) + else: result = new(HtmlElement) + result.tagType = tagType + result.nodeType = NODE_ELEMENT + +func toInputType*(str: string): InputType = + case str + of "button": INPUT_BUTTON + of "checkbox": INPUT_CHECKBOX + of "color": INPUT_COLOR + of "date": INPUT_DATE + of "datetime_local": INPUT_DATETIME_LOCAL + of "email": INPUT_EMAIL + of "file": INPUT_FILE + of "hidden": INPUT_HIDDEN + of "image": INPUT_IMAGE + of "month": INPUT_MONTH + of "number": INPUT_NUMBER + of "password": INPUT_PASSWORD + of "radio": INPUT_RADIO + of "range": INPUT_RANGE + of "reset": INPUT_RESET + of "search": INPUT_SEARCH + of "submit": INPUT_SUBMIT + of "tel": INPUT_TEL + of "text": INPUT_TEXT + of "time": INPUT_TIME + of "url": INPUT_URL + of "week": INPUT_WEEK + else: INPUT_UNKNOWN + +func toInputSize*(str: string): int = + if str.len == 0: + return 20 + for c in str: + if not c.isDigit: + return 20 + return str.parseInt() + +proc applyAttribute(htmlElement: HtmlElement, key: string, value: string) = + case key + of "id": htmlElement.id = value + of "class": htmlElement.class = value + of "name": + case htmlElement.tagType + of TAG_SELECT: HtmlSelectElement(htmlElement).name = value + else: discard + of "value": + case htmlElement.tagType + of TAG_INPUT: HtmlInputElement(htmlElement).value = value + of TAG_SELECT: HtmlSelectElement(htmlElement).value = value + of TAG_OPTION: HtmlOptionElement(htmlElement).value = value + else: discard + of "href": + case htmlElement.tagType + of TAG_A: HtmlAnchorElement(htmlElement).href = value + else: discard + of "type": + case htmlElement.tagType + of TAG_INPUT: HtmlInputElement(htmlElement).itype = value.toInputType() + else: discard + of "size": + case htmlElement.tagType + of TAG_INPUT: HtmlInputElement(htmlElement).size = value.toInputSize() + else: discard + else: return var s = "" -proc parseHtml*(inputStream: Stream) = +proc nparseHtml*(inputStream: Stream): Document = var x: XmlParser x.open(inputStream, "") - while true: - x.next() - case x.kind - of xmlElementStart: discard - of xmlEof: break - else: discard + var parents: seq[HtmlNode] + let document = newDocument() + parents.add(document) + var closed = true + while parents.len > 0 and x.kind != xmlEof: + var currParent = parents[^1] + while true: + var parsedNode: HtmlNode + x.next() + case x.kind + of xmlComment: discard #TODO + of xmlElementStart: + if not closed and currParent.isElemNode() and HtmlElement(currParent).tagType in SingleTagTypes: + parents.setLen(parents.len - 1) + currParent = parents[^1] + closed = true + eprint "<" & x.rawData & ">" + parsedNode = newHtmlElement(tagType(x.rawData)) + currParent.childNodes.add(parsedNode) + if currParent.isElemNode(): + parsedNode.parentElement = HtmlElement(currParent) + parsedNode.parentNode = currParent + parents.add(parsedNode) + closed = false + break + of xmlElementEnd: + eprint "</" & x.rawData & ">" + parents.setLen(parents.len - 1) + closed = true + of xmlElementOpen: + if not closed and currParent.isElemNode() and HtmlElement(currParent).tagType in SingleTagTypes: + parents.setLen(parents.len - 1) + currParent = parents[^1] + closed = true + parsedNode = newHtmlElement(tagType(x.rawData)) + s = "<" & x.rawData + x.next() + while x.kind != xmlElementClose and x.kind != xmlEof: + if x.kind == xmlAttribute: + HtmlElement(parsedNode).applyAttribute(x.rawData.tolower(), x.rawData2) + s &= " " + s &= x.rawData + s &= "=\"" + s &= x.rawData2 + s &= "\"" + x.next() + s &= ">" + eprint s + + currParent.childNodes.add(parsedNode) + if currParent.isElemNode(): + parsedNode.parentElement = HtmlElement(currParent) + parsedNode.parentNode = currParent + parents.add(parsedNode) + closed = false + break + of xmlCharData: + let textNode = new(HtmlNode) + textNode.nodeType = NODE_TEXT + textNode.rawtext = x.rawData + currParent.childNodes.add(textNode) + textNode.parentNode = currParent + if currParent.isElemNode(): + textNode.parentElement = HtmlElement(currParent) + eprint x.rawData, currParent.nodeType + of xmlEntity: + eprint "entity", x.rawData + of xmlEof: break + else: discard + return document diff --git a/termattrs.nim b/termattrs.nim index 21fd3b04..d49800ae 100644 --- a/termattrs.nim +++ b/termattrs.nim @@ -6,7 +6,6 @@ type termHeight*: int proc getTermAttributes*(): TermAttributes = - var t = TermAttributes() - t.termWidth = terminalWidth() - t.termHeight = terminalHeight() - return t + let attrs = TermAttributes(termWidth: terminalWidth(), + termHeight: terminalHeight()) + return attrs diff --git a/twtio.nim b/twtio.nim index 16dfb254..0a19bfbf 100644 --- a/twtio.nim +++ b/twtio.nim @@ -102,7 +102,7 @@ proc readLine*(prompt: string, current: var string): bool = var rune: Rune new.fastRuneAt(cursor, rune, false) printesc($rune) - cursor += 1 + inc cursor of ACTION_LINED_PREV_WORD: while cursor > 0: print('\b') @@ -116,7 +116,7 @@ proc readLine*(prompt: string, current: var string): bool = var rune: Rune new.fastRuneAt(cursor, rune, false) printesc($rune) - cursor += 1 + inc cursor if cursor < rl: new.fastRuneAt(cursor, rune, false) if rune == Rune(' '): @@ -124,7 +124,7 @@ proc readLine*(prompt: string, current: var string): bool = of ACTION_LINED_KILL_WORD: var chars = 0 while cursor > chars: - chars += 1 + inc chars var rune: Rune new.fastRuneAt(cursor - chars, rune, false) if rune == Rune(' '): @@ -157,6 +157,6 @@ proc readLine*(prompt: string, current: var string): bool = rl = new.runeLen() printesc(new.runeSubstr(cursor)) print('\b'.repeat(rl - cursor - 1)) - cursor += 1 + inc cursor else: feedNext = true diff --git a/twtstr.nim b/twtstr.nim index 9c03436e..1f6b49d4 100644 --- a/twtstr.nim +++ b/twtstr.nim @@ -70,7 +70,7 @@ func findChar*(str: string, c: char, start: int = 0): int = while i < str.len: if str[i] == c: return i - i += 1 + inc i return -1 func findChar*(str: string, c: Rune, start: int = 0): int = @@ -306,10 +306,10 @@ func mk_wcwidth_cjk*(ucs: Rune): int = func mk_wcswidth_cjk*(s: string): int = - result = 0 - for r in s.runes: - result += mk_wcwidth_cjk(r) - return result + #result = 0 + #for r in s.runes: + # result += mk_wcwidth_cjk(r) + #return result result = 0 var i = 0 while i < len(s): |