diff options
author | Kaushal Modi <kaushal.modi@gmail.com> | 2020-02-26 05:41:44 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-26 11:41:44 +0100 |
commit | f091b5a0ee9b5585a08e9130d3ec682a9526ef39 (patch) | |
tree | c80bc6280d8cb7ade82604c7d3ba3b871fe749b8 | |
parent | 0c312ad898f793d9b095197d26074e6fd7f3328d (diff) | |
download | Nim-f091b5a0ee9b5585a08e9130d3ec682a9526ef39.tar.gz |
xmltree: Make indentation consistent with any num of children nodes (#13482)
Ref: https://forum.nim-lang.org/t/5972
-rw-r--r-- | changelog.md | 2 | ||||
-rw-r--r-- | 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 == "<firstTag><childTag /></firstTag>" + assert $a == """<firstTag> + <childTag /> +</firstTag>""" 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 == "<firstTag><childTag /></firstTag>" + assert $a == """<firstTag> + <childTag /> +</firstTag>""" 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 == "<firstTag><childTag /></firstTag>" + assert $a == """<firstTag> + <childTag /> +</firstTag>""" a.tag = "newTag" - assert $a == "<newTag><childTag /></newTag>" + assert $a == """<newTag> + <childTag /> +</newTag>""" 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 == "<myTag>\n<second />\n<first />\n</myTag>" + assert $f == """<myTag> + <second /> + <first /> +</myTag>""" 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 == "<myTag><first /></myTag>" + assert $f == """<myTag> + <first /> +</myTag>""" 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 comment --><firstTag />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: ``a<b>b</b>`` is - # different from ``a <b>b</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("</") - result.add(n.fTag) - 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("</") + result.add(n.fTag) + result.add(">") of xnText: result.addEscaped(n.fText) of xnComment: @@ -759,5 +781,84 @@ macro `<>`*(x: untyped): untyped = when isMainModule: - assert """<a href="http://nim-lang.org">Nim rules.</a>""" == - $(<>a(href = "http://nim-lang.org", newText("Nim rules."))) + var + x: XmlNode + + x = <>a(href = "http://nim-lang.org", newText("Nim rules.")) + assert $x == """<a href="http://nim-lang.org">Nim rules.</a>""" + + x = <>outer(<>inner()) + assert $x == """<outer> + <inner /> +</outer>""" + + x = <>outer(<>middle(<>inner1(), <>inner2(), <>inner3(), <>inner4())) + assert $x == """<outer> + <middle> + <inner1 /> + <inner2 /> + <inner3 /> + <inner4 /> + </middle> +</outer>""" + + x = <>l0(<>l1(<>l2(<>l3(<>l4())))) + assert $x == """<l0> + <l1> + <l2> + <l3> + <l4 /> + </l3> + </l2> + </l1> +</l0>""" + + x = <>l0(<>l1p1(), <>l1p2(), <>l1p3()) + assert $x == """<l0> + <l1p1 /> + <l1p2 /> + <l1p3 /> +</l0>""" + + x = <>l0(<>l1(<>l2p1(), <>l2p2())) + assert $x == """<l0> + <l1> + <l2p1 /> + <l2p2 /> + </l1> +</l0>""" + + 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 == """<l0> + <l1> + <l2_1 /> + <l2_2> + <l3_1 /> + <l3_2 /> + <l3_3> + <l4_1 /> + <l4_2 /> + <l4_3 /> + </l3_3> + </l2_2> + <l2_3 /> + <l2_4 /> + </l1> +</l0>""" + + let + innermost = newElement("innermost") + middle = newXmlTree("middle", [innermost]) + innermost.add newText("innermost text") + x = newXmlTree("outer", [middle]) + assert $x == """<outer> + <middle> + <innermost>innermost text</innermost> + </middle> +</outer>""" + + x = newElement("myTag") + x.add newText("my text") + x.add newElement("sonTag") + x.add newEntity("my entity") + assert $x == "<myTag>my text<sonTag />&my entity;</myTag>" |