mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 05:50:30 +00:00
xmltree: Make indentation consistent with any num of children nodes (#13482)
Ref: https://forum.nim-lang.org/t/5972
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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>"
|
||||
|
||||
Reference in New Issue
Block a user