From f091b5a0ee9b5585a08e9130d3ec682a9526ef39 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Wed, 26 Feb 2020 05:41:44 -0500 Subject: xmltree: Make indentation consistent with any num of children nodes (#13482) Ref: https://forum.nim-lang.org/t/5972 --- changelog.md | 2 + lib/pure/xmltree.nim | 169 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 137 insertions(+), 34 deletions(-) diff --git a/changelog.md b/changelog.md index 1943df634..7be7a722b 100644 --- a/changelog.md +++ b/changelog.md @@ -132,3 +132,5 @@ `ioselector_select` now properly handle the `Event.User` select event type. - `joinPath` path normalization when `/` is the first argument works correctly: `assert "/" / "/a" == "/a"`. Fixed the edgecase: `assert "" / "" == ""`. +- `xmltree` now adds indentation consistently to child nodes for any number + of children nodes. diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index b23e00fdb..fbb0ab13a 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -82,7 +82,9 @@ proc newElement*(tag: string): XmlNode = var a = newElement("firstTag") a.add newElement("childTag") assert a.kind == xnElement - assert $a == "" + assert $a == """ + +""" result = newXmlNode(xnElement) result.fTag = tag @@ -207,7 +209,9 @@ proc tag*(n: XmlNode): string {.inline.} = runnableExamples: var a = newElement("firstTag") a.add newElement("childTag") - assert $a == "" + assert $a == """ + +""" assert a.tag == "firstTag" assert n.k == xnElement @@ -225,9 +229,13 @@ proc `tag=`*(n: XmlNode, tag: string) {.inline.} = runnableExamples: var a = newElement("firstTag") a.add newElement("childTag") - assert $a == "" + assert $a == """ + +""" a.tag = "newTag" - assert $a == "" + assert $a == """ + +""" assert n.k == xnElement n.fTag = tag @@ -304,11 +312,13 @@ proc insert*(father, son: XmlNode, index: int) {.inline.} = ## * `add proc <#add,XmlNode,XmlNode>`_ ## * `delete proc <#delete,XmlNode,Natural>`_ runnableExamples: - from strutils import unindent var f = newElement("myTag") f.add newElement("first") f.insert(newElement("second"), 0) - assert ($f).unindent == "\n\n\n" + assert $f == """ + + +""" assert father.k == xnElement and son.k == xnElement if len(father.s) > index: @@ -327,7 +337,9 @@ proc delete*(n: XmlNode, i: Natural) {.noSideEffect.} = f.add newElement("first") f.insert(newElement("second"), 0) f.delete(0) - assert $f == "" + assert $f == """ + +""" assert n.k == xnElement n.s.delete(i) @@ -558,11 +570,8 @@ proc escape*(s: string): string = proc addIndent(result: var string, indent: int, addNewLines: bool) = if addNewLines: result.add("\n") - for i in 1..indent: result.add(' ') - -proc noWhitespace(n: XmlNode): bool = - for i in 0..n.len-1: - if n[i].kind in {xnText, xnEntity}: return true + for i in 1 .. indent: + result.add(' ') proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2, addNewLines = true) = @@ -578,6 +587,10 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2, s.add(b) assert s == "my text" + proc noWhitespace(n: XmlNode): bool = + for i in 0 ..< n.len: + if n[i].kind in {xnText, xnEntity}: return true + proc addEscapedAttr(result: var string, s: string) = # `addEscaped` alternative with less escaped characters. # Only to be used for escaping attribute values enclosed in double quotes! @@ -590,8 +603,18 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2, else: result.add(c) if n == nil: return + case n.k of xnElement: + if indent > 0: + result.addIndent(indent, addNewLines) + + let + addNewLines = if n.noWhitespace(): + false + else: + addNewLines + result.add('<') result.add(n.fTag) if not isNil(n.fAttr): @@ -601,27 +624,26 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2, result.add("=\"") result.addEscapedAttr(val) result.add('"') - if n.len > 0: - result.add('>') - if n.len > 1: - if noWhitespace(n): - # for mixed leaves, we cannot output whitespace for readability, - # because this would be wrong. For example: ``ab`` is - # different from ``a b``. - for i in 0..n.len-1: - result.add(n[i], indent+indWidth, indWidth, addNewLines) - else: - for i in 0..n.len-1: - result.addIndent(indent+indWidth, addNewLines) - result.add(n[i], indent+indWidth, indWidth, addNewLines) - result.addIndent(indent, addNewLines) - else: - result.add(n[0], indent+indWidth, indWidth, addNewLines) - result.add("") - else: + + if n.len == 0: result.add(" />") + return + + let + indentNext = if n.noWhitespace(): + indent + else: + indent+indWidth + result.add('>') + for i in 0 ..< n.len: + result.add(n[i], indentNext, indWidth, addNewLines) + + if not n.noWhitespace(): + result.addIndent(indent, addNewLines) + + result.add("") of xnText: result.addEscaped(n.fText) of xnComment: @@ -759,5 +781,84 @@ macro `<>`*(x: untyped): untyped = when isMainModule: - assert """Nim rules.""" == - $(<>a(href = "http://nim-lang.org", newText("Nim rules."))) + var + x: XmlNode + + x = <>a(href = "http://nim-lang.org", newText("Nim rules.")) + assert $x == """Nim rules.""" + + x = <>outer(<>inner()) + assert $x == """ + +""" + + x = <>outer(<>middle(<>inner1(), <>inner2(), <>inner3(), <>inner4())) + assert $x == """ + + + + + + +""" + + x = <>l0(<>l1(<>l2(<>l3(<>l4())))) + assert $x == """ + + + + + + + +""" + + x = <>l0(<>l1p1(), <>l1p2(), <>l1p3()) + assert $x == """ + + + +""" + + x = <>l0(<>l1(<>l2p1(), <>l2p2())) + assert $x == """ + + + + +""" + + x = <>l0(<>l1(<>l2_1(), <>l2_2(<>l3_1(), <>l3_2(), <>l3_3(<>l4_1(), <>l4_2(), <>l4_3())), <>l2_3(), <>l2_4())) + assert $x == """ + + + + + + + + + + + + + + +""" + + let + innermost = newElement("innermost") + middle = newXmlTree("middle", [innermost]) + innermost.add newText("innermost text") + x = newXmlTree("outer", [middle]) + assert $x == """ + + innermost text + +""" + + x = newElement("myTag") + x.add newText("my text") + x.add newElement("sonTag") + x.add newEntity("my entity") + assert $x == "my text&my entity;" -- cgit 1.4.1-2-gfad0