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("")
- 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 """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;"