diff options
author | bptato <nincsnevem662@gmail.com> | 2021-01-20 23:26:29 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-01-20 23:26:29 +0100 |
commit | 6e1ef74bdca4c629bda2756a6e72279e038195f4 (patch) | |
tree | ea06c329dbf98e55803887c9780703f9201ff987 | |
parent | 07ee263ccc895b543d0b017f1446e9121bc8a1bb (diff) | |
download | chawan-6e1ef74bdca4c629bda2756a6e72279e038195f4.tar.gz |
some things just never work
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | buffer.nim | 108 | ||||
-rw-r--r-- | display.nim | 129 | ||||
-rw-r--r-- | htmlelement.nim | 191 | ||||
-rw-r--r-- | main.nim | 10 | ||||
-rw-r--r-- | twtio.nim | 2 | ||||
-rw-r--r-- | twtstr.nim | 15 |
7 files changed, 237 insertions, 220 deletions
diff --git a/Makefile b/Makefile index 68cb1ff5..b0297753 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ all: build build: nim compile -d:ssl -o:twt main.nim +release: + nim compile -d:release -d:ssl -o:twt main.nim diff --git a/buffer.nim b/buffer.nim index 5f4cf227..cc0fafc9 100644 --- a/buffer.nim +++ b/buffer.nim @@ -28,6 +28,7 @@ type fromY*: int nodes*: seq[HtmlNode] links*: seq[HtmlNode] + clickables*: seq[HtmlNode] elements*: seq[HtmlNode] idelements*: Table[string, HtmlNode] selectedlink*: HtmlNode @@ -84,7 +85,7 @@ func atPercentOf*(buffer: Buffer): int = return (100 * buffer.cursorY) div buffer.lastLine() func visibleText*(buffer: Buffer): string = - return buffer.text.substr(buffer.lines[buffer.fromY], buffer.lines[buffer.lastVisibleLine() - 1]) + return buffer.text.substr(buffer.lines[buffer.fromY], buffer.lines[buffer.lastVisibleLine() - 1] - 2) func lastNode*(buffer: Buffer): HtmlNode = return buffer.nodes[^1] @@ -95,13 +96,17 @@ func onNewLine*(buffer: Buffer): bool = func onSpace*(buffer: Buffer): bool = return buffer.text.len > 0 and buffer.text[^1] == ' ' +func cursorOnNode*(buffer: Buffer, node: HtmlNode): bool = + return buffer.cursorY >= node.y and buffer.cursorY < node.y + node.height and + buffer.cursorX >= node.x and buffer.cursorX < node.x + node.width + func findSelectedElement*(buffer: Buffer): Option[HtmlElement] = if buffer.selectedlink != nil: return some(buffer.selectedlink.text.parent) for node in buffer.nodes: - if node.nodeType == NODE_ELEMENT: - if node.element.formattedElem.len > 0: - 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.element) + if node.isElemNode(): + if node.getFmtLen() > 0: + if buffer.cursorOnNode(node): return some(node.element) return none(HtmlElement) func cursorAt*(buffer: Buffer): int = @@ -113,9 +118,6 @@ func cursorChar*(buffer: Buffer): char = func canScroll*(buffer: Buffer): bool = return buffer.lastLine() > buffer.height -func cursorOnNode*(buffer: Buffer, node: HtmlNode): bool = - return buffer.cursorY >= node.y and buffer.cursorY <= node.y + node.height and buffer.cursorX >= node.x and buffer.cursorX <= node.x + node.width - func getElementById*(buffer: Buffer, id: string): HtmlNode = if buffer.idelements.hasKey(id): return buffer.idelements[id] @@ -123,15 +125,28 @@ func getElementById*(buffer: Buffer, id: string): HtmlNode = proc findSelectedNode*(buffer: Buffer): Option[HtmlNode] = for node in buffer.nodes: - if node.getFormattedLen() > 0 and node.displayed(): + 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: return some(node) return none(HtmlNode) proc addNode*(buffer: Buffer, htmlNode: HtmlNode) = buffer.nodes.add(htmlNode) - if htmlNode.isTextNode() and htmlNode.text.parent.htmlTag == tagA: + + if htmlNode.isTextNode() and htmlNode.text.parent.islink: buffer.links.add(htmlNode) + + if htmlNode.isElemNode(): + case htmlNode.element.htmlTag + of tagInput, tagOption: + if not htmlNode.element.hidden: + buffer.clickables.add(htmlNode) + else: discard + elif htmlNode.isTextNode(): + if htmlNode.text.parent.islink: + let anchor = htmlNode.text.parent.getParent(tagA) + buffer.clickables.add(anchor.node) + if htmlNode.isElemNode(): buffer.elements.add(htmlNode) if htmlNode.element.id != "": @@ -170,6 +185,7 @@ proc clearText*(buffer: Buffer) = proc clearNodes*(buffer: Buffer) = buffer.nodes.setLen(0) buffer.links.setLen(0) + buffer.clickables.setLen(0) buffer.elements.setLen(0) buffer.idelements.clear() @@ -182,6 +198,23 @@ proc clearBuffer*(buffer: Buffer) = buffer.fromY = 0 buffer.hovertext = "" +proc scrollTo*(buffer: Buffer, y: int): bool = + 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) + return true + +proc cursorTo*(buffer: Buffer, x: int, y: int): bool = + buffer.cursorY = min(max(y, 0), buffer.lastLine()) + if buffer.fromY > buffer.cursorY: + buffer.fromY = min(buffer.cursorY, buffer.lastLine() - buffer.height) + elif buffer.fromY + buffer.height < buffer.cursorY: + buffer.fromY = max(buffer.cursorY - buffer.height, 0) + buffer.cursorX = min(max(x, 0), buffer.currentRawLineLength()) + buffer.fromX = min(max(buffer.currentRawLineLength() - buffer.width + 1, 0), 0) #TODO + return true + proc cursorDown*(buffer: Buffer): bool = if buffer.cursorY < buffer.lastLine(): buffer.cursorY += 1 @@ -302,44 +335,23 @@ proc cursorPrevWord*(buffer: Buffer): bool = return false discard buffer.cursorLeft() -iterator revNodes*(buffer: Buffer): HtmlNode {.inline.} = - var i = buffer.nodes.len - 1 +iterator revclickables*(buffer: Buffer): HtmlNode {.inline.} = + var i = buffer.clickables.len - 1 while i >= 0: - yield buffer.nodes[i] + yield buffer.clickables[i] i -= 1 proc cursorNextLink*(buffer: Buffer): bool = - var next = false - for node in buffer.nodes: - if node.nodeType == NODE_ELEMENT: - case node.element.htmlTag - of tagInput, tagA: - if next: - var res = false - while buffer.cursorY < node.y: - res = res or buffer.cursorDown() - buffer.cursorLineBegin() - while buffer.cursorX < node.x: - res = res or buffer.cursorRight() - return res - if buffer.cursorY >= node.y and buffer.cursorY <= node.y + node.height and buffer.cursorX >= node.x and buffer.cursorX <= node.x + node.width: - next = true - else: discard + 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) + return false proc cursorPrevLink*(buffer: Buffer): bool = - var next = false - for node in buffer.revNodes: - if node.nodeType == NODE_ELEMENT and node.element.htmlTag == tagInput: - if next: - var res = false - while buffer.cursorY < node.y: - res = res or buffer.cursorDown() - buffer.cursorLineBegin() - while buffer.cursorX < node.x: - res = res or buffer.cursorRight() - return true - if buffer.cursorY >= node.y and buffer.cursorY <= node.y + node.height and buffer.cursorX >= node.x and buffer.cursorX <= node.x + node.width: - next = true + for node in buffer.revclickables: + 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: @@ -394,12 +406,6 @@ proc cursorBottom*(buffer: Buffer): bool = buffer.cursorY = min(buffer.fromY + buffer.height - 1, buffer.lastLine()) return false -proc scrollTo*(buffer: Buffer, y: int): bool = - if y == buffer.fromY: - return false - buffer.fromY = min(max(buffer.lastLine() - buffer.height + 1, 0), y - buffer.height) - return true - proc scrollDown*(buffer: Buffer): bool = if buffer.fromY + buffer.height <= buffer.lastLine(): buffer.fromY += 1 @@ -423,13 +429,17 @@ proc checkLinkSelection*(buffer: Buffer): bool = if buffer.cursorOnNode(buffer.selectedlink): return false else: + let anchor = buffer.selectedlink.text.parent.getParent(tagA) + anchor.selected = false buffer.selectedlink = nil buffer.hovertext = "" for node in buffer.links: if buffer.cursorOnNode(node): buffer.selectedlink = node - node.text.parent.selected = true - buffer.hovertext = node.text.parent.href + let anchor = node.text.parent.getParent(tagA) + assert(anchor != nil) + anchor.selected = true + buffer.hovertext = anchor.href return true return false diff --git a/display.nim b/display.nim index f4eaa806..898993dd 100644 --- a/display.nim +++ b/display.nim @@ -22,10 +22,10 @@ proc statusMsg*(str: string, at: int) = print(str.addAnsiStyle(styleReverse)) type - RenderState = ref RenderStateObj - RenderStateObj = object + RenderState = object x: int y: int + lastwidth: int atchar: int atrawchar: int centerqueue: int @@ -34,17 +34,13 @@ type blankspaces: int nextspaces: int docenter: bool + indent: int + listval: int func newRenderState(): RenderState = - return RenderState() + return RenderState(y: 1) -func nodeAttr(node: HtmlNode): HtmlElement = - case node.nodeType - of NODE_TEXT: return node.text.parent - of NODE_ELEMENT: return node.element - else: assert(false) - -proc flushLine(buffer: Buffer, state: RenderState) = +proc flushLine(buffer: Buffer, state: var RenderState) = if buffer.onNewLine(): state.blanklines += 1 buffer.write('\n') @@ -57,7 +53,7 @@ proc flushLine(buffer: Buffer, state: RenderState) = buffer.rawlines.add(state.atrawchar) assert(buffer.onNewLine()) -proc addSpaces(buffer: Buffer, state: RenderState, n: int) = +proc addSpaces(buffer: Buffer, state: var RenderState, n: int) = if state.x + n > buffer.width: buffer.flushLine(state) return @@ -67,29 +63,27 @@ proc addSpaces(buffer: Buffer, state: RenderState, n: int) = state.atchar += n state.atrawchar += n -proc addSpace(buffer: Buffer, state: RenderState) = +proc addSpace(buffer: Buffer, state: var RenderState) = buffer.addSpaces(state, 1) -proc assignCoords(node: HtmlNode, state: RenderState) = - node.x = state.x - node.y = state.y - -proc addSpacePadding(buffer: Buffer, state: RenderState) = +proc addSpacePadding(buffer: Buffer, state: var RenderState) = if not buffer.onSpace(): buffer.addSpace(state) -proc writeWrappedText(buffer: Buffer, state: RenderState, fmttext: string, rawtext: string) = +proc writeWrappedText(buffer: Buffer, state: var RenderState, fmttext: string, rawtext: string) = + state.lastwidth = 0 var n = 0 var fmtword = "" var rawword = "" var prevl = false for c in fmttext: fmtword &= c + if n >= rawtext.len or rawtext[n] != c: continue - state.x += 1 rawword &= c + state.x += 1 if state.x > buffer.width: if buffer.rawtext.len > 0 and buffer.rawtext[^1] == ' ': @@ -98,8 +92,11 @@ proc writeWrappedText(buffer: Buffer, state: RenderState, fmttext: string, rawte state.atchar -= 1 state.atrawchar -= 1 state.x -= 1 + state.lastwidth = max(state.lastwidth, state.x) buffer.flushLine(state) prevl = true + else: + state.lastwidth = max(state.lastwidth, state.x) if c == ' ': buffer.writefmt(fmtword) @@ -107,17 +104,19 @@ proc writeWrappedText(buffer: Buffer, state: RenderState, fmttext: string, rawte state.atchar += fmtword.len state.atrawchar += rawword.len if prevl: - state.x += fmtword.len + state.x += rawword.len prevl = false fmtword = "" rawword = "" n += 1 + buffer.writefmt(fmtword) buffer.writeraw(rawword) state.atchar += fmtword.len state.atrawchar += rawword.len + state.lastwidth = max(state.lastwidth, state.x) -proc preAlignNode(buffer: Buffer, node: HtmlNode, state: RenderState) = +proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = let elem = node.nodeAttr() if not buffer.onNewLine() and node.openblock and state.blanklines == 0: buffer.flushLine(state) @@ -125,21 +124,39 @@ proc preAlignNode(buffer: Buffer, node: HtmlNode, state: RenderState) = if node.openblock: while state.blanklines < max(elem.margin, elem.margintop): buffer.flushLine(state) + if elem.display == DISPLAY_LIST_ITEM: + eprint "???" + state.indent += 1 if not buffer.onNewLine() and state.blanklines == 0 and node.displayed(): buffer.addSpaces(state, state.nextspaces) state.nextspaces = 0 - if elem.pad: - buffer.addSpacePadding(state) - 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(): buffer.addSpaces(state, max(buffer.width div 2 - state.centerlen div 2, 0)) state.centerlen = 0 + + if elem.display == DISPLAY_LIST_ITEM and state.indent > 0: + var listchar = "" + eprint "Display", listchar, elem.parent.htmlTag + case elem.parent.htmlTag + of tagUl: + listchar = "*" + of tagOl: + state.listval += 1 + listchar = $state.listval & ")" + else: + return + buffer.addSpaces(state, state.indent) + buffer.write(listchar) + state.x += 1 + state.atchar += 1 + state.atrawchar += 1 + buffer.addSpaces(state, 1) -proc postAlignNode(buffer: Buffer, node: HtmlNode, state: RenderState) = +proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = let elem = node.nodeAttr() if node.getRawLen() > 0: @@ -147,8 +164,6 @@ proc postAlignNode(buffer: Buffer, node: HtmlNode, state: RenderState) = state.blankspaces = 0 if not buffer.onNewLine() and state.blanklines == 0: - if elem.pad: - state.nextspaces = 1 state.nextspaces += max(elem.margin, elem.marginright) if node.closeblock: buffer.flushLine(state) @@ -156,26 +171,26 @@ proc postAlignNode(buffer: Buffer, node: HtmlNode, state: RenderState) = if node.closeblock: while state.blanklines < max(elem.margin, elem.marginbottom): buffer.flushLine(state) + if elem.display == DISPLAY_LIST_ITEM and node.isTextNode(): + state.indent -= 1 if elem.htmlTag == tagBr and not node.openblock: buffer.flushLine(state) -proc renderNode(buffer: Buffer, node: HtmlNode, state: RenderState) = + if elem.display == DISPLAY_LIST_ITEM and node.isElemNode(): + buffer.flushLine(state) + +proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = if not node.visibleNode(): return - if node.isElemNode(): - node.element.formattedElem = node.element.getFormattedElem() let elem = node.nodeAttr() if elem.htmlTag == tagTitle: - if isTextNode(node): - buffer.title = node.text.text + if node.isTextNode(): + buffer.title = node.rawtext return else: discard if elem.hidden: return - node.height = 1 - node.width = node.getRawLen() - if not state.docenter: if elem.centered: if not node.closeblock and elem.htmlTag != tagBr: @@ -195,18 +210,19 @@ proc renderNode(buffer: Buffer, node: HtmlNode, state: RenderState) = buffer.preAlignNode(node, state) - node.assignCoords(state) - if isTextNode(node): - buffer.writeWrappedText(state, node.text.formattedText, node.text.text) - elif isElemNode(node): - buffer.writeWrappedText(state, node.element.formattedElem, node.element.rawElem) + node.x = state.x + node.y = state.y + buffer.writeWrappedText(state, node.fmttext, node.rawtext) + node.width = state.lastwidth - node.x + 1 + node.height = state.y - node.y + 1 buffer.postAlignNode(node, state) iterator revItems*(n: XmlNode): XmlNode {.inline.} = var i = n.len - 1 while i >= 0: - yield n[i] + if n[i].kind != xnComment: + yield n[i] i -= 1 type @@ -215,13 +231,13 @@ type xml*: XmlNode html*: HtmlNode -proc setLastHtmlLine(buffer: Buffer, state: RenderState) = +proc setLastHtmlLine(buffer: Buffer, state: var RenderState) = if buffer.text.len != buffer.lines[^1]: state.atchar = buffer.text.len + 1 state.atrawchar = buffer.rawtext.len + 1 buffer.flushLine(state) -proc renderHtml(buffer: Buffer) = +proc renderHtml*(buffer: Buffer) = var stack: seq[XmlHtmlNode] let first = XmlHtmlNode(xml: buffer.htmlSource, html: getHtmlNode(buffer.htmlSource, none(HtmlElement))) @@ -230,21 +246,22 @@ proc renderHtml(buffer: Buffer) = var state = newRenderState() while stack.len > 0: let currElem = stack.pop() - if currElem.html.nodeType != NODE_COMMENT: - buffer.renderNode(currElem.html, state) - if currElem.html.isElemNode(): - if currElem.html.element.id != "": - eprint currElem.html.element.id - buffer.addNode(currElem.html) + buffer.renderNode(currElem.html, state) + buffer.addNode(currElem.html) var last = false + var prev: HtmlNode = nil for item in currElem.xml.revItems: let child = XmlHtmlNode(xml: item, html: getHtmlNode(item, some(currElem.html.element))) stack.add(child) + child.html.prev = prev + prev = child.html if not last and child.html.visibleNode(): last = true if currElem.html.element.display == DISPLAY_BLOCK: stack[^1].html.closeblock = true + else: + child.html.next = stack[^1].html if last: if currElem.html.element.display == DISPLAY_BLOCK: stack[^1].html.openblock = true @@ -257,9 +274,11 @@ proc drawHtml(buffer: Buffer) = buffer.setLastHtmlLine(state) proc statusMsgForBuffer(buffer: Buffer) = - let msg = $buffer.cursorY & "/" & $buffer.lastLine() & " (" & + var msg = $buffer.cursorY & "/" & $buffer.lastLine() & " (" & $buffer.atPercentOf() & "%) " & - "<" & buffer.title & buffer.hovertext + "<" & buffer.title & ">" + if buffer.hovertext.len > 0: + msg &= " " & buffer.hovertext statusMsg(msg.maxString(buffer.width), buffer.height) proc cursorBufferPos(buffer: Buffer) = @@ -326,10 +345,11 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool = if status: reshape = true redraw = true - of tagA: - buffer.setLocation(parseUri(buffer.selectedlink.text.parent.href)) - return true else: discard + if selectedElem.get().islink: + let anchor = buffer.selectedlink.text.parent.getParent(tagA).href + buffer.setLocation(parseUri(anchor)) + return true of ACTION_CHANGE_LOCATION: var url = $buffer.location clearStatusMsg(buffer.height) @@ -358,7 +378,6 @@ proc displayPage*(attrs: TermAttributes, buffer: Buffer): bool = termGoto(0, 0) #buffer.printwrite = true discard buffer.gotoAnchor() - buffer.renderHtml() buffer.displayBuffer() buffer.statusMsgForBuffer() return inputLoop(attrs, buffer) diff --git a/htmlelement.nim b/htmlelement.nim index 4cf2e02e..35430f1e 100644 --- a/htmlelement.nim +++ b/htmlelement.nim @@ -15,7 +15,7 @@ type NODE_ELEMENT, NODE_TEXT, NODE_COMMENT DisplayType* = enum - DISPLAY_INLINE, DISPLAY_BLOCK, DISPLAY_SINGLE, DISPLAY_NONE + DISPLAY_INLINE, DISPLAY_BLOCK, DISPLAY_SINGLE, DISPLAY_LIST_ITEM, DISPLAY_NONE InputType* = enum INPUT_BUTTON, INPUT_CHECKBOX, INPUT_COLOR, INPUT_DATE, INPUT_DATETIME_LOCAL, @@ -33,10 +33,9 @@ type HtmlText* = ref HtmlTextObj HtmlTextObj = object parent*: HtmlElement - text*: string - formattedText*: string HtmlElement* = ref HtmlElementObj HtmlElementObj = object + node*: HtmlNode id*: string name*: string value*: string @@ -44,19 +43,17 @@ type hidden*: bool display*: DisplayType innerText*: string - formattedElem*: string - rawElem*: string textNodes*: int margintop*: int marginbottom*: int marginleft*: int marginright*: int margin*: int - pad*: bool bold*: bool italic*: bool underscore*: bool - parentElement*: HtmlElement + islink*: bool + parent*: HtmlElement case htmlTag*: HtmlTag of tagInput: itype*: InputType @@ -66,8 +63,6 @@ type selected*: bool else: discard - -type HtmlNode* = ref HtmlNodeObj HtmlNodeObj = object case nodeType*: NodeType @@ -77,12 +72,25 @@ type text*: HtmlText of NODE_COMMENT: comment*: string + rawtext*: string + fmttext*: string x*: int y*: int width*: int height*: int openblock*: bool closeblock*: bool + next*: HtmlNode + prev*: HtmlNode + +func nodeAttr*(node: HtmlNode): HtmlElement = + case node.nodeType + of NODE_TEXT: return node.text.parent + of NODE_ELEMENT: return node.element + else: assert(false) + +func displayed*(node: HtmlNode): bool = + return node.rawtext.len > 0 and node.nodeAttr().display != DISPLAY_NONE func isTextNode*(node: HtmlNode): bool = return node.nodeType == NODE_TEXT @@ -90,33 +98,11 @@ func isTextNode*(node: HtmlNode): bool = func isElemNode*(node: HtmlNode): bool = return node.nodeType == NODE_ELEMENT -func getFormattedLen*(htmlText: HtmlText): int = - return htmlText.formattedText.strip().len - -func getFormattedLen*(htmlElem: HtmlElement): int = - return htmlElem.formattedElem.len - -func getFormattedLen*(htmlNode: HtmlNode): int = - case htmlNode.nodeType - of NODE_TEXT: return htmlNode.text.getFormattedLen() - of NODE_ELEMENT: return htmlNode.element.getFormattedLen() - else: - assert(false) - return 0 - -func getRawLen*(htmlText: HtmlText): int = - return htmlText.text.len - -func getRawLen*(htmlElem: HtmlElement): int = - return htmlElem.rawElem.len +func getFmtLen*(htmlNode: HtmlNode): int = + return htmlNode.fmttext.len func getRawLen*(htmlNode: HtmlNode): int = - case htmlNode.nodeType - of NODE_TEXT: return htmlNode.text.getRawLen() - of NODE_ELEMENT: return htmlNode.element.getRawLen() - else: - assert(false) - return 0 + return htmlNode.rawtext.len func visibleNode*(node: HtmlNode): bool = case node.nodeType @@ -124,18 +110,6 @@ func visibleNode*(node: HtmlNode): bool = of NODE_ELEMENT: return true else: return false -func displayed*(elem: HtmlElement): bool = - return elem.display != DISPLAY_NONE and (elem.getFormattedLen() > 0 or elem.htmlTag == tagBr) and not elem.hidden - -func displayed*(node: HtmlNode): bool = - if node.isTextNode(): - return node.getRawLen() > 0 - elif node.isElemNode(): - return node.element.displayed() - -func empty*(elem: HtmlElement): bool = - return elem.textNodes == 0 or not elem.displayed() - func toInputType*(str: string): InputType = case str of "button": INPUT_BUTTON @@ -170,21 +144,23 @@ func toInputSize*(str: string): int = func getInputElement(xmlElement: XmlNode, htmlElement: HtmlElement): HtmlElement = assert(htmlElement.htmlTag == tagInput) htmlElement.itype = xmlElement.attr("type").toInputType() + if htmlElement.itype == INPUT_HIDDEN: + htmlElement.hidden = true htmlElement.size = xmlElement.attr("size").toInputSize() htmlElement.value = xmlElement.attr("value") - htmlElement.pad = true return htmlElement func getAnchorElement(xmlElement: XmlNode, htmlElement: HtmlElement): HtmlElement = assert(htmlElement.htmlTag == tagA) htmlElement.href = xmlElement.attr("href") + htmlElement.islink = true return htmlElement func getSelectElement(xmlElement: XmlNode, htmlElement: HtmlElement): HtmlElement = assert(htmlElement.htmlTag == tagSelect) for item in xmlElement.items: if item.kind == xnElement: - if item.tag == "option" and item.attr("value") != "": + if item.tag == "option": htmlElement.value = item.attr("value") break htmlElement.name = xmlElement.attr("name") @@ -193,7 +169,7 @@ func getSelectElement(xmlElement: XmlNode, htmlElement: HtmlElement): HtmlElemen func getOptionElement(xmlElement: XmlNode, htmlElement: HtmlElement): HtmlElement = assert(htmlElement.htmlTag == tagOption) htmlElement.value = xmlElement.attr("value") - if htmlElement.parentElement.value != htmlElement.value: + if htmlElement.parent.value != htmlElement.value: htmlElement.hidden = true return htmlElement @@ -201,53 +177,69 @@ func getFormattedInput(htmlElement: HtmlElement): string = case htmlElement.itype of INPUT_TEXT, INPUT_SEARCH: let valueFit = fitValueToSize(htmlElement.value, htmlElement.size) - return "[" & valueFit.addAnsiStyle(styleUnderscore).addAnsiFgColor(fgRed) & "]" - of INPUT_SUBMIT: return ("[" & htmlElement.value & "]").addAnsiFgColor(fgRed) + return valueFit.addAnsiStyle(styleUnderscore).buttonStr() + of INPUT_SUBMIT: + return htmlElement.value.buttonStr() else: discard func getRawInput(htmlElement: HtmlElement): string = case htmlElement.itype of INPUT_TEXT, INPUT_SEARCH: return "[" & htmlElement.value.fitValueToSize(htmlElement.size) & "]" - of INPUT_SUBMIT: return "[" & htmlElement.value & "]" + of INPUT_SUBMIT: + return "[" & htmlElement.value & "]" else: discard -func getRawElem*(htmlElement: HtmlElement): string = - case htmlElement.htmlTag - of tagInput: return htmlElement.getRawInput() - of tagOption: return "[]" - else: return "" - -func getFormattedElem*(htmlElement: HtmlElement): string = - case htmlElement.htmlTag - of tagInput: return htmlElement.getFormattedInput() - else: return "" - -func getRawText*(htmlText: HtmlText): string = - if htmlText.parent.htmlTag != tagPre: - result = htmlText.text.replace(re"\n").strip() +func getParent*(htmlElement: HtmlElement, htmlTag: HtmlTag): HtmlElement = + result = htmlElement + while result != nil and result.htmlTag != htmlTag: + result = result.parent + +func getRawText*(htmlNode: HtmlNode): string = + if htmlNode.isElemNode(): + case htmlNode.element.htmlTag + of tagInput: return htmlNode.element.getRawInput() + else: return "" + elif htmlNode.isTextNode(): + if htmlNode.text.parent.htmlTag != tagPre: + result = htmlNode.rawtext.replace(re"\n") + if result.strip().len > 0: + if htmlNode.nodeAttr().display != DISPLAY_INLINE: + if htmlNode.prev == nil or htmlNode.prev.nodeAttr().display != DISPLAY_INLINE: + result = result.strip(true, false) + if htmlNode.next == nil or htmlNode.next.nodeAttr().display != DISPLAY_INLINE: + result = result.strip(false, true) + else: + result = "" + else: + result = htmlNode.rawtext.strip() + if htmlNode.text.parent.htmlTag == tagOption: + result = "[" & result & "]" else: - result = htmlText.text - - if htmlText.parent.htmlTag == tagOption: - result = "[" & result & "]" + assert(false) -func getFormattedText*(htmlText: HtmlText): string = - result = htmlText.text - case htmlText.parent.htmlTag - of tagA: - result = result.addAnsiFgColor(fgBlue) - if htmlText.parent.selected: +func getFmtText*(htmlNode: HtmlNode): string = + if htmlNode.isElemNode(): + case htmlNode.element.htmlTag + of tagInput: return htmlNode.element.getFormattedInput() + else: return "" + elif htmlNode.isTextNode(): + result = htmlNode.rawtext + if htmlNode.text.parent.islink: + result = result.addAnsiFgColor(fgBlue) + let parent = htmlNode.text.parent.getParent(tagA) + if parent != nil and parent.selected: + result = result.addAnsiStyle(styleUnderscore) + + if htmlNode.text.parent.htmlTag == tagOption: + result = result.addAnsiFgColor(fgRed) + + if htmlNode.text.parent.bold: + result = result.addAnsiStyle(styleBright) + if htmlNode.text.parent.italic: + result = result.addAnsiStyle(styleItalic) + if htmlNode.text.parent.underscore: result = result.addAnsiStyle(styleUnderscore) - of tagOption: result = result.addAnsiFgColor(fgRed) - else: discard - - if htmlText.parent.bold: - result = result.addAnsiStyle(styleBright) - if htmlText.parent.italic: - result = result.addAnsiStyle(styleItalic) - if htmlText.parent.underscore: - result = result.addAnsiStyle(styleUnderscore) proc newElemFromParent(elem: HtmlElement, parentOpt: Option[HtmlElement]): HtmlElement = if parentOpt.isSome: @@ -263,8 +255,8 @@ proc newElemFromParent(elem: HtmlElement, parentOpt: Option[HtmlElement]): HtmlE #elem.marginbottom = parent.marginbottom #elem.marginleft = parent.marginleft #elem.marginright = parent.marginright - elem.parentElement = parent - elem.pad = false + elem.parent = parent + elem.islink = parent.islink return elem @@ -278,9 +270,10 @@ proc getHtmlElement*(xmlElement: XmlNode, inherit: Option[HtmlElement]): HtmlEle htmlElement.display = DISPLAY_INLINE elif htmlElement.htmlTag in BlockTags: htmlElement.display = DISPLAY_BLOCK - htmlElement.pad = true elif htmlElement.htmlTag in SingleTags: htmlElement.display = DISPLAY_SINGLE + elif htmlElement.htmlTag == tagLi: + htmlElement.display = DISPLAY_LIST_ITEM else: htmlElement.display = DISPLAY_NONE @@ -316,25 +309,25 @@ proc getHtmlElement*(xmlElement: XmlNode, inherit: Option[HtmlElement]): HtmlEle if child.kind == xnText and child.text.strip().len > 0: htmlElement.textNodes += 1 - htmlElement.rawElem = htmlElement.getRawElem() - htmlElement.formattedElem = htmlElement.getFormattedElem() return htmlElement -proc getHtmlText*(text: string, parent: HtmlElement): HtmlText = - var textNode = HtmlText(parent: parent, text: text) - textNode.text = textNode.getRawText() - textNode.formattedText = textNode.getFormattedText() - return textNode +proc getHtmlText*(parent: HtmlElement): HtmlText = + return HtmlText(parent: parent) proc getHtmlNode*(xmlElement: XmlNode, parent: Option[HtmlElement]): HtmlNode = case kind(xmlElement) of xnElement: - return HtmlNode(nodeType: NODE_ELEMENT, element: getHtmlElement(xmlElement, parent)) + result = HtmlNode(nodeType: NODE_ELEMENT, element: getHtmlElement(xmlElement, parent)) + result.element.node = result of xnText: assert(parent.isSome) - return HtmlNode(nodeType: NODE_TEXT, text: getHtmlText(xmlElement.text, parent.get())) + result = HtmlNode(nodeType: NODE_TEXT, text: getHtmlText(parent.get())) + result.rawtext = xmlElement.text of xnComment: - return HtmlNode(nodeType: NODE_COMMENT, comment: xmlElement.text) + result = HtmlNode(nodeType: NODE_COMMENT, comment: xmlElement.text) of xnCData: - return HtmlNode(nodeType: NODE_TEXT, text: getHtmlText(xmlElement.text, parent.get())) + result = HtmlNode(nodeType: NODE_TEXT, text: getHtmlText(parent.get())) + result.rawtext = xmlElement.text else: assert(false) + result.rawtext = result.getRawText() + result.fmttext = result.getFmtText() diff --git a/main.nim b/main.nim index bbfffd58..b0212511 100644 --- a/main.nim +++ b/main.nim @@ -19,7 +19,6 @@ proc loadLocalPage*(url: string): string = proc loadPageUri(uri: Uri, currentcontent: XmlNode): XmlNode = var moduri = uri - var page: XmlNode moduri.anchor = "" if uri.scheme == "" and uri.path == "" and uri.anchor != "" and currentcontent != nil: return currentcontent @@ -38,11 +37,12 @@ proc main*() = eprint "Failed to read keymap, falling back to default" parseKeymap(keymapStr) let attrs = getTermAttributes() - var buffer = newBuffer(attrs) - var url = parseUri(paramStr(1)) + let buffer = newBuffer(attrs) + let uri = parseUri(paramStr(1)) buffers.add(buffer) buffer.setLocation(uri) buffer.htmlSource = loadPageUri(uri, buffer.htmlSource) + buffer.renderHtml() var lastUri = uri while displayPage(attrs, buffer): statusMsg("Loading...", buffer.height) @@ -50,9 +50,9 @@ proc main*() = lastUri.anchor = "" newUri.anchor = "" if $lastUri != $newUri: - buffer.htmlSource = loadPageUri(buffer.location, buffer.htmlSource) buffer.clearBuffer() - else + buffer.htmlSource = loadPageUri(buffer.location, buffer.htmlSource) + buffer.renderHtml() lastUri = newUri #waitFor loadPage("https://lite.duckduckgo.com/lite/?q=hello%20world") diff --git a/twtio.nim b/twtio.nim index cb59686c..4d2f1083 100644 --- a/twtio.nim +++ b/twtio.nim @@ -9,7 +9,7 @@ template print*(s: varargs[string, `$`]) = for x in s: stdout.write(x) -template eprint*(s: varargs[string, `$`]) = +template eprint*(s: varargs[string, `$`]) = {.cast(noSideEffect).}: var a = false for x in s: if not a: diff --git a/twtstr.nim b/twtstr.nim index 5a41f3e3..983d58d8 100644 --- a/twtstr.nim +++ b/twtstr.nim @@ -1,21 +1,11 @@ import terminal import strutils -func stripNewline*(str: string): string = - if str.len == 0: - result = str - return - - case str[^1] - of '\n': - result = str.substr(0, str.len - 2) - else: discard - func addAnsiStyle*(str: string, style: Style): string = return ansiStyleCode(style) & str & "\e[0m" func addAnsiFgColor*(str: string, color: ForegroundColor): string = - return ansiForegroundColorCode(color) & str & "\e[0m" + return ansiForegroundColorCode(color) & str & ansiResetCode func maxString*(str: string, max: int): string = if max < str.len: @@ -26,3 +16,6 @@ func fitValueToSize*(str: string, size: int): string = if str.len < size: return str & ' '.repeat(size - str.len) return str.maxString(size) + +func buttonStr*(str: string): string = + return "[".addAnsiFgColor(fgRed) & str.addAnsiFgColor(fgRed) & "]".addAnsiFgColor(fgRed) |