about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/html/dom.nim37
-rw-r--r--src/io/buffer.nim88
-rw-r--r--src/io/cell.nim10
-rw-r--r--src/layout/box.nim7
-rw-r--r--src/layout/engine.nim24
-rw-r--r--src/render/renderdocument.nim2
6 files changed, 86 insertions, 82 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 5537491c..c3506a72 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -129,6 +129,30 @@ iterator textNodes*(node: Node): Text {.inline.} =
     if node.nodeType == TEXT_NODE:
       yield Text(node)
 
+# Returns the node's ancestors
+iterator ancestors*(node: Node): Element {.inline.} =
+  var element = node.parentElement
+  while element != nil:
+    yield element
+    element = element.parentElement
+
+# Returns the node itself and its ancestors
+iterator branch*(node: Node): Node {.inline.} =
+  var node = node
+  while node != nil:
+    yield node
+    node = node.parentElement
+
+# a == b or b in a's ancestors
+func contains*(a, b: Node): bool =
+  for node in a.branch:
+    if node == b: return true
+  return false
+
+func branch*(node: Node): seq[Node] =
+  for node in node.branch:
+    result.add(node)
+
 func firstChild(node: Node): Node =
   if node.childNodes.len == 0:
     return nil
@@ -223,13 +247,10 @@ func toInputType*(str: string): InputType =
   of "week": INPUT_WEEK
   else: INPUT_UNKNOWN
 
-func ancestor(node: Node, tagTypes: set[TagType]): Element =
-  var elem = node.parentElement
-  while elem != nil:
-    if elem.tagType in tagTypes:
-      return elem
-
-    elem = elem.parentElement
+func findAncestor*(node: Node, tagTypes: set[TagType]): Element =
+  for element in node.ancestors:
+    if element.tagType in tagTypes:
+      return element
   return nil
 
 func attr*(element: Element, s: string): string =
@@ -247,7 +268,7 @@ proc applyOrdinal*(elem: HTMLLIElement) =
   if val.issome:
     elem.ordinalvalue = val.get
   else:
-    let owner = elem.ancestor({TAG_OL, TAG_UL, TAG_MENU})
+    let owner = elem.findAncestor({TAG_OL, TAG_UL, TAG_MENU})
     if owner == nil:
       elem.ordinalvalue = 1
     else:
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index a082cfe3..f46b5be0 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -45,7 +45,7 @@ type
     streamclosed*: bool
     source*: string
     rootbox*: CSSBox
-    prevnodes*: seq[Node]
+    prevnode*: Node
     sourcepair*: Buffer
     prev*: Buffer
     next* {.cursor.}: Buffer
@@ -197,16 +197,15 @@ func currentDisplayCell(buffer: Buffer): FixedCell =
   let row = (buffer.cursory - buffer.fromy) * buffer.width
   return buffer.display[row + buffer.currentCellOrigin()]
 
-func getLink(nodes: seq[Node]): Element =
-  for node in nodes:
-    if node.nodeType == ELEMENT_NODE:
-      let elem = Element(node)
-      if elem.tagType == TAG_A:
-        return elem
-  return nil
+func getLink(node: Node): Element =
+  if node == nil:
+    return nil
+  if node.nodeType == ELEMENT_NODE and Element(node).tagType == TAG_A:
+    return Element(node)
+  return node.findAncestor({TAG_A})
 
 func getCursorLink(buffer: Buffer): Element =
-  return buffer.currentDisplayCell().nodes.getLink()
+  return buffer.currentDisplayCell().node.getLink()
 
 func currentLine(buffer: Buffer): string =
   return buffer.lines[buffer.cursory].str
@@ -311,7 +310,7 @@ proc refreshDisplay(buffer: Buffer) =
       buffer.display[dls + k].runes.add(r)
       if cf.pos != -1:
         buffer.display[dls + k].formatting = cf.formatting
-        buffer.display[dls + k].nodes = cf.nodes
+        buffer.display[dls + k].node = cf.node
       let tk = k + r.width()
       while k < tk and k < buffer.width - 1:
         inc k
@@ -476,12 +475,12 @@ proc cursorNextLink*(buffer: Buffer) =
   var i = line.findFormatN(buffer.currentCursorBytes()) - 1
   var link: Element = nil
   if i >= 0:
-    link = line.formats[i].nodes.getLink()
+    link = line.formats[i].node.getLink()
   inc i
 
   while i < line.formats.len:
     let format = line.formats[i]
-    let fl = format.nodes.getLink()
+    let fl = format.node.getLink()
     if fl != nil and fl != link:
       buffer.setCursorXB(format.pos)
       return
@@ -492,7 +491,7 @@ proc cursorNextLink*(buffer: Buffer) =
     i = 0
     while i < line.formats.len:
       let format = line.formats[i]
-      let fl = format.nodes.getLink()
+      let fl = format.node.getLink()
       if fl != nil and fl != link:
         buffer.setCursorXBY(format.pos, y)
         return
@@ -503,12 +502,12 @@ proc cursorPrevLink*(buffer: Buffer) =
   var i = line.findFormatN(buffer.currentCursorBytes()) - 1
   var link: Element = nil
   if i >= 0:
-    link = line.formats[i].nodes.getLink()
+    link = line.formats[i].node.getLink()
   dec i
 
   while i >= 0:
     let format = line.formats[i]
-    let fl = format.nodes.getLink()
+    let fl = format.node.getLink()
     if fl != nil and fl != link:
       buffer.setCursorXB(format.pos)
       return
@@ -519,7 +518,7 @@ proc cursorPrevLink*(buffer: Buffer) =
     i = line.formats.len - 1
     while i >= 0:
       let format = line.formats[i]
-      let fl = format.nodes.getLink()
+      let fl = format.node.getLink()
       if fl != nil and fl != link:
         #go to beginning of link
         var ly = y #last y
@@ -529,7 +528,7 @@ proc cursorPrevLink*(buffer: Buffer) =
           i = line.formats.len - 1
           while i >= 0:
             let format = line.formats[i]
-            let nl = format.nodes.getLink()
+            let nl = format.node.getLink()
             if nl == fl:
               ly = iy
               lx = format.pos
@@ -652,7 +651,7 @@ proc gotoAnchor*(buffer: Buffer) =
     var i = 0
     while i < line.formats.len:
       let format = line.formats[i]
-      if anchor in format.nodes:
+      if anchor in format.node:
         buffer.setCursorY(y)
         buffer.centerLine()
         buffer.setCursorXB(format.pos)
@@ -680,38 +679,35 @@ proc updateCursor(buffer: Buffer) =
     buffer.cursory = 0
 
 proc updateHover(buffer: Buffer) =
-  let nodes = buffer.currentDisplayCell().nodes
-  if nodes != buffer.prevnodes:
-    for node in nodes:
-      var elem: Element
-      if node of Element:
-        elem = Element(node)
-      else:
-        elem = node.parentElement
-        assert elem != nil
-
-      if not elem.hover and not (node in buffer.prevnodes):
-        elem.hover = true
-        buffer.reshape = true
-        elem.refreshStyle()
-    let link = nodes.getLink()
+  let thisnode = buffer.currentDisplayCell().node
+  let prevnode = buffer.prevnode
+
+  if thisnode != prevnode:
+    for node in thisnode.branch:
+      if node.nodeType == ELEMENT_NODE:
+        let elem = Element(node)
+        if not elem.hover and node notin prevnode:
+          elem.hover = true
+          buffer.reshape = true
+          elem.refreshStyle()
+
+    let link = thisnode.getLink()
     if link != nil:
       if link.tagType == TAG_A:
-        buffer.hovertext = parseUrl(HTMLAnchorElement(link).href, buffer.location.some).serialize()
+        let anchor = HTMLAnchorElement(link)
+        buffer.hovertext = parseUrl(anchor.href, buffer.location.some).serialize()
     else:
       buffer.hovertext = ""
-    for node in buffer.prevnodes:
-      var elem: Element
-      if node of Element:
-        elem = Element(node)
-      else:
-        elem = node.parentElement
-        assert elem != nil
-      if elem.hover and not (node in nodes):
-        elem.hover = false
-        buffer.reshape = true
-        elem.refreshStyle()
-  buffer.prevnodes = nodes
+
+    for node in prevnode.branch:
+      if node.nodeType == ELEMENT_NODE:
+        let elem = Element(node)
+        if elem.hover and node notin thisnode:
+          elem.hover = false
+          buffer.reshape = true
+          elem.refreshStyle()
+
+  buffer.prevnode = thisnode
 
 proc loadResources(buffer: Buffer, document: Document) =
   for elem in document.head.children:
diff --git a/src/io/cell.nim b/src/io/cell.nim
index fbe35268..b783d180 100644
--- a/src/io/cell.nim
+++ b/src/io/cell.nim
@@ -24,7 +24,7 @@ type
 
   Cell* = object of RootObj
     formatting*: Formatting
-    nodes*: seq[Node]
+    node*: Node
 
   FormattingCell* = object of Cell
     pos*: int
@@ -64,7 +64,7 @@ template `overline=`*(f: var Formatting, b: bool) = flag_template f, b, FLAG_OVE
 func `==`*(a: FixedCell, b: FixedCell): bool =
   return a.formatting == b.formatting and
     a.runes == b.runes and
-    a.nodes == b.nodes
+    a.node == b.node
 
 func newFixedGrid*(w: int, h: int = 1): FixedGrid =
   return newSeq[FixedCell](w * h)
@@ -128,7 +128,7 @@ proc setLen*(line: var FlexibleLine, len: int) =
 
 proc add*(a: var FlexibleLine, b: FlexibleLine) =
   let l = a.str.len
-  a.formats.add(b.formats.map((x) => FormattingCell(formatting: x.formatting, nodes: x.nodes, pos: l + x.pos)))
+  a.formats.add(b.formats.map((x) => FormattingCell(formatting: x.formatting, node: x.node, pos: l + x.pos)))
   a.str &= b.str
 
 proc addLine*(grid: var FlexibleGrid) =
@@ -140,8 +140,8 @@ proc addFormat*(line: var FlexibleLine, pos: int, format: Formatting) =
 proc addFormat*(grid: var FlexibleGrid, y, pos: int, format: Formatting) =
   grid[y].formats.add(FormattingCell(formatting: format, pos: grid[y].str.len))
 
-proc addFormat*(grid: var FlexibleGrid, y, pos: int, format: Formatting, nodes: seq[Node]) =
-  grid[y].formats.add(FormattingCell(formatting: format, nodes: nodes, pos: pos))
+proc addFormat*(grid: var FlexibleGrid, y, pos: int, format: Formatting, node: Node) =
+  grid[y].formats.add(FormattingCell(formatting: format, node: node, pos: pos))
 
 proc addCell*(grid: var FlexibleGrid, y: int, r: Rune) =
   grid[y].str &= $r
diff --git a/src/layout/box.nim b/src/layout/box.nim
index 07dce0ed..d0cf99c4 100644
--- a/src/layout/box.nim
+++ b/src/layout/box.nim
@@ -7,7 +7,7 @@ import io/term
 type
   Viewport* = ref object
     term*: TermAttributes
-    nodes*: seq[Node]
+    node*: Node
     root*: BlockBox
     map*: seq[CSSBox]
 
@@ -18,7 +18,6 @@ type
     specified*: CSSSpecifiedValues
     node*: Node
     element*: Element
-    nodes*: seq[Node]
 
   InlineAtom* = ref object of RootObj
     relx*: int
@@ -31,7 +30,7 @@ type
     fontweight*: int
     textdecoration*: CSSTextDecoration
     color*: CSSColor
-    nodes*: seq[Node]
+    node*: Node
 
   InlineRow* = ref object
     atoms*: seq[InlineAtom]
@@ -50,7 +49,7 @@ type
     whitespace*: bool
     maxwidth*: int
     viewport*: Viewport
-    nodes*: seq[Node]
+    node*: Node
 
   BlockContext* = ref object of InlineAtom
     inline*: InlineContext
diff --git a/src/layout/engine.nim b/src/layout/engine.nim
index 10cc1374..441f6233 100644
--- a/src/layout/engine.nim
+++ b/src/layout/engine.nim
@@ -26,7 +26,7 @@ func px(l: CSSLength, state: Viewport, p = 0): int {.inline.} =
 type InlineState = object
   ictx: InlineContext
   skip: bool
-  nodes: seq[Node]
+  node: Node
   word: InlineWord
   maxwidth: int
   specified: CSSSpecifiedValues
@@ -58,7 +58,7 @@ proc newWord(state: var InlineState) =
   word.fontstyle = specified{"font-style"}
   word.fontweight = specified{"font-weight"}
   word.textdecoration = specified{"text-decoration"}
-  word.nodes = state.nodes
+  word.node = state.node
   state.word = word
 
 proc finishRow(ictx: InlineContext) =
@@ -128,12 +128,12 @@ proc processWhitespace(state: var InlineState, c: char) =
     else:
       state.ictx.whitespace = true
 
-proc renderText*(ictx: InlineContext, str: string, maxwidth: int, specified: CSSSpecifiedValues, nodes: seq[Node]) =
+proc renderText*(ictx: InlineContext, str: string, maxwidth: int, specified: CSSSpecifiedValues, node: Node) =
   var state: InlineState
   state.specified = specified
   state.ictx = ictx
   state.maxwidth = maxwidth
-  state.nodes = nodes
+  state.node = node
   state.newWord()
 
   #if str.strip().len > 0:
@@ -271,16 +271,13 @@ proc alignInlineBlock(bctx: BlockContext, box: InlineBlockBox, parentcss: CSSSpe
   box.ictx.whitespace = false
 
 proc alignInline(bctx: BlockContext, box: InlineBox) =
-  if box.node != nil:
-    bctx.viewport.nodes.add(box.node)
-
   let box = InlineBox(box)
   assert box.ictx != nil
   if box.newline:
     box.ictx.flushLine()
   for text in box.text:
     assert box.children.len == 0
-    box.ictx.renderText(text, bctx.compwidth, box.specified, box.nodes)
+    box.ictx.renderText(text, bctx.compwidth, box.specified, box.node)
 
   for child in box.children:
     case child.t
@@ -294,8 +291,6 @@ proc alignInline(bctx: BlockContext, box: InlineBox) =
       bctx.alignInlineBlock(child, box.specified)
     else:
       assert false, "child.t is " & $child.t
-  if box.node != nil:
-    discard bctx.viewport.nodes.pop()
 
 proc alignInlines(bctx: BlockContext, inlines: seq[CSSBox]) =
   let ictx = bctx.newInlineContext()
@@ -341,14 +336,11 @@ proc alignBlocks(bctx: BlockContext, blocks: seq[CSSBox], blockgroup: var seq[CS
       alignBlock(child)
     of DISPLAY_INLINE:
       if child.inlinelayout:
-        child.nodes = bctx.viewport.nodes
         blockgroup.add(child)
       else:
         if child.node != nil:
-          bctx.viewport.nodes.add(child.node)
+          bctx.viewport.node = child.node
         bctx.alignBlocks(child.children, blockgroup, child.node)
-        if child.node != nil:
-          discard bctx.viewport.nodes.pop()
         #eprint "put"
     of DISPLAY_INLINE_BLOCK:
       blockgroup.add(child)
@@ -357,8 +349,6 @@ proc alignBlocks(bctx: BlockContext, blocks: seq[CSSBox], blockgroup: var seq[CS
 proc alignBlock(box: BlockBox) =
   if box.bctx.done:
     return
-  if box.node != nil:
-    box.bctx.viewport.nodes.add(box.node)
   if box.inlinelayout:
     # Box only contains inline boxes.
     box.bctx.alignInlines(box.children)
@@ -368,8 +358,6 @@ proc alignBlock(box: BlockBox) =
     let bctx = box.bctx
     flush_group()
     box.bctx.arrangeBlocks()
-  if box.node != nil:
-    discard box.bctx.viewport.nodes.pop()
   box.bctx.done = true
 
 proc getBox(specified: CSSSpecifiedValues): CSSBox =
diff --git a/src/render/renderdocument.nim b/src/render/renderdocument.nim
index 2ff46488..6765853b 100644
--- a/src/render/renderdocument.nim
+++ b/src/render/renderdocument.nim
@@ -48,7 +48,7 @@ proc setRowWord(lines: var FlexibleGrid, word: InlineWord, x, y: int, term: Term
   let oformats = lines[y].formats.subformats(i)
   lines[y].setLen(i)
 
-  lines.addFormat(y, i, word.formatFromWord(), word.nodes)
+  lines.addFormat(y, i, word.formatFromWord(), word.node)
 
   var nx = cx
   if nx < x: