////////////////////////////////////////////////////////////////////////////
// Porgram: xml-beautify-regexp.go
// Purpose: Go XML Beautify from XML string using pure regexp
// Authors: Antonio Sun (c) 2016, All rights reserved
// Credits: diotalevi http://www.perlmonks.org/?node_id=261292
////////////////////////////////////////////////////////////////////////////
package xmlfmt
import (
"regexp"
"strings"
)
var (
reg = regexp.MustCompile(`<([/!]?)([^>]+?)(/?)>`)
// NL is the newline string used in XML output, define for DOS-convenient.
NL = "\r\n"
)
// FormatXML will (purly) reformat the XML string in a readable way, without any rewriting/altering the structure
func FormatXML(xmls, prefix, indent string) string {
src := regexp.MustCompile(`>\s+<`).ReplaceAllString(xmls, "><")
rf := replaceTag(prefix, indent)
return (prefix + reg.ReplaceAllStringFunc(src, rf))
}
// replaceTag returns a closure function to do 's/(?<=>)\s+(?=<)//g; s(<(/?)([^>]+?)(/?)>)($indent+=$3?0:$1?-1:1;"<$1$2$3>"."\n".(" "x$indent))ge' as in Perl
// and deal with comments as well
func replaceTag(prefix, indent string) func(string) string {
indentLevel := 0
return func(m string) string {
parts := reg.FindStringSubmatch(m)
// $3: A <foo/> tag. No alteration to indentation.
// $1: A closing </foo> tag. Drop one indentation level
// else: An opening <foo> tag. Increase one indentation level
if len(parts[3]) == 0 {
//print("] " + parts[1] + "-" + parts[3] + ".\n")
if parts[1] == `/` {
indentLevel--
} else if parts[1] != `!` {
indentLevel++
}
} //else {
//print("] " + parts[1] + parts[2] + parts[3])
//}
return "<" + parts[1] + parts[2] + parts[3] + ">" +
NL + prefix + strings.Repeat(indent, indentLevel)
}
}