xmltree: Make indentation consistent with any num of children nodes (#13482)

Ref: https://forum.nim-lang.org/t/5972
This commit is contained in:
Kaushal Modi
2020-02-26 05:41:44 -05:00
committed by GitHub
parent 0c312ad898
commit f091b5a0ee
2 changed files with 137 additions and 34 deletions

View File

@@ -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.

View File

@@ -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>"