Add WriteSettings.UseCRLF for Windows systems.
The document Indent and IndentTabs functions are now capable of
outputting CRLF-based newlines.
diff --git a/etree.go b/etree.go
index 138fc02..791a25a 100644
--- a/etree.go
+++ b/etree.go
@@ -65,6 +65,11 @@
// attribute value characters &, < and ". If false, XML character
// references are also produced for > and '. Default: false.
CanonicalAttrVal bool
+
+ // When outputting indented XML, use a carriage return and linefeed
+ // ("\r\n") as a new-line delimiter instead of just a linefeed ("\n").
+ // This is useful on Windows-based systems.
+ UseCRLF bool
}
// newWriteSettings creates a default WriteSettings record.
@@ -73,6 +78,7 @@
CanonicalEndTags: false,
CanonicalText: false,
CanonicalAttrVal: false,
+ UseCRLF: false,
}
}
@@ -273,11 +279,11 @@
// spaces if you want no indentation at all.
func (d *Document) Indent(spaces int) {
var indent indentFunc
- switch {
- case spaces < 0:
- indent = func(depth int) string { return "" }
+ switch d.WriteSettings.UseCRLF {
+ case true:
+ indent = func(depth int) string { return indentCRLF(depth*spaces, indentSpaces) }
default:
- indent = func(depth int) string { return crIndent(depth*spaces, crsp) }
+ indent = func(depth int) string { return indentLF(depth*spaces, indentSpaces) }
}
d.Element.indent(0, indent)
}
@@ -286,7 +292,13 @@
// entities containing carriage returns and tabs for indentation. One tab is
// used per indentation level.
func (d *Document) IndentTabs() {
- indent := func(depth int) string { return crIndent(depth, crtab) }
+ var indent indentFunc
+ switch d.WriteSettings.UseCRLF {
+ case true:
+ indent = func(depth int) string { return indentCRLF(depth, indentTabs) }
+ default:
+ indent = func(depth int) string { return indentLF(depth, indentTabs) }
+ }
d.Element.indent(0, indent)
}
diff --git a/etree_test.go b/etree_test.go
index bff6cf4..30eb60e 100644
--- a/etree_test.go
+++ b/etree_test.go
@@ -653,3 +653,60 @@
t.Error("etree: invalid text")
}
}
+
+func TestIndentSettings(t *testing.T) {
+ doc := NewDocument()
+ root := doc.CreateElement("root")
+ root.CreateElement("child1")
+ root.CreateElement("child2")
+
+ doc.WriteSettings.UseCRLF = false
+ doc.Indent(20)
+
+ s, err := doc.WriteToString()
+ if err != nil {
+ t.Error("etree: failed to serialize document")
+ }
+
+ checkEq(t, s, "<root>\n <child1/>\n <child2/>\n</root>\n")
+
+ doc.WriteSettings.UseCRLF = true
+ doc.Indent(4)
+
+ s, err = doc.WriteToString()
+ if err != nil {
+ t.Error("etree: failed to serialize document")
+ }
+
+ checkEq(t, s, "<root>\r\n <child1/>\r\n <child2/>\r\n</root>\r\n")
+
+ doc.WriteSettings.UseCRLF = true
+ doc.IndentTabs()
+
+ s, err = doc.WriteToString()
+ if err != nil {
+ t.Error("etree: failed to serialize document")
+ }
+
+ checkEq(t, s, "<root>\r\n\t<child1/>\r\n\t<child2/>\r\n</root>\r\n")
+
+ doc.WriteSettings.UseCRLF = false
+ doc.Indent(4)
+
+ s, err = doc.WriteToString()
+ if err != nil {
+ t.Error("etree: failed to serialize document")
+ }
+
+ checkEq(t, s, "<root>\n <child1/>\n <child2/>\n</root>\n")
+
+ doc.WriteSettings.UseCRLF = false
+ doc.IndentTabs()
+
+ s, err = doc.WriteToString()
+ if err != nil {
+ t.Error("etree: failed to serialize document")
+ }
+
+ checkEq(t, s, "<root>\n\t<child1/>\n\t<child2/>\n</root>\n")
+}
diff --git a/helpers.go b/helpers.go
index b2b2f7e..70e7e2d 100644
--- a/helpers.go
+++ b/helpers.go
@@ -149,22 +149,35 @@
return str[:colon], str[colon+1:]
}
-// Strings used by crIndent
+// Strings used by indentCRLF and indentLF
const (
- crsp = "\n "
- crtab = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
+ indentSpaces = "\r\n "
+ indentTabs = "\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
)
-// crIndent returns a carriage return followed by n copies of the
-// first non-CR character in the source string.
-func crIndent(n int, source string) string {
+// indentCRLF returns a CRLF newline followed by n copies of the first
+// non-CRLF character in the source string.
+func indentCRLF(n int, source string) string {
switch {
case n < 0:
- return source[:1]
- case n < len(source):
- return source[:n+1]
+ return source[:2]
+ case n < len(source)-1:
+ return source[:n+2]
default:
- return source + strings.Repeat(source[1:2], n-len(source)+1)
+ return source + strings.Repeat(source[2:3], n-len(source))
+ }
+}
+
+// indentLF returns a LF newline followed by n copies of the first non-LF
+// character in the source string.
+func indentLF(n int, source string) string {
+ switch {
+ case n < 0:
+ return source[1:2]
+ case n < len(source)-1:
+ return source[1 : n+2]
+ default:
+ return source[1:] + strings.Repeat(source[2:3], n-len(source))
}
}