diff --git a/Rebracer.Tests/UtilitiesTets/XmlMergerTests.cs b/Rebracer.Tests/UtilitiesTets/XmlMergerTests.cs
index 01dda7c..f6391d9 100644
--- a/Rebracer.Tests/UtilitiesTets/XmlMergerTests.cs
+++ b/Rebracer.Tests/UtilitiesTets/XmlMergerTests.cs
@@ -245,6 +245,36 @@ public void ReorderingNewElementsGetNewLines() {
");
}
+ [TestMethod]
+ public void ReplacedChildElementsPreserveIndentation() {
+ // Note two tabs before each element
+ var source = @"
+
+ Hi!
+
+ Bye!
+
+
+
+";
+ var container = XElement.Parse(source, LoadOptions.PreserveWhitespace);
+ var newSource = MergeElements(container,
+ new XElement("a", new XAttribute("b", "c"),
+ new XElement("x1", "Hi!"),
+ new XElement("x2", new XElement("NewContent", "Bye!"))
+ )
+ );
+ container.ToString().Should().Be(@"
+
+ Hi!
+
+ Bye!
+
+
+
+");
+ }
+
[TestMethod]
public void ReorderingElementsPreservesComments() {
// Note two tabs before each element
diff --git a/Rebracer/Utilities/XmlMerger.cs b/Rebracer/Utilities/XmlMerger.cs
index 09e4aaa..ab395d5 100644
--- a/Rebracer/Utilities/XmlMerger.cs
+++ b/Rebracer/Utilities/XmlMerger.cs
@@ -52,6 +52,7 @@ public static bool MergeElements(this XElement container, IEnumerable
if (!changed && !XNode.DeepEquals(o, newItems[newIndex].Value))
changed = true;
o.ReplaceWith(newItems[newIndex].Value);
+ IndentChildren(newItems[newIndex].Value);
newIndex++;
}
@@ -92,15 +93,16 @@ public static bool MergeElements(this XElement container, IEnumerable
///Gets all whitespace and comment nodes before the specified element, until its preceding element.
static IEnumerable GetPrecedingTrivia(this XElement element) {
var lastElem = element.ElementsBeforeSelf().LastOrDefault();
- if (lastElem == null) // If it's the first element, take all preceding nodes
+ if (lastElem == null) // If it's the first element, take all preceding nodes
return element.NodesBeforeSelf();
- else // Otherwise, take all nodes after the prior element.
+ else // Otherwise, take all nodes after the prior element.
return lastElem.NodesAfterSelf().TakeWhile(n => n != element);
}
///A method to insert nodes into a LINQ to XML document.
delegate void NodeInserter(params object[] content);
- private static XNode GetPrecedingWhitespace(this XElement element) {
+ ///Gets the XText node containing the whitespace used to indent this element. If there is no preceding whitespace, the following whitespace will be returned, if any.
+ private static XText GetPrecedingWhitespace(this XElement element) {
// If we started from an empty parent, there is no known whitespace
if (element == null)
return null;
@@ -111,5 +113,48 @@ private static XNode GetPrecedingWhitespace(this XElement element) {
return null;
return new XText(sample);
}
+
+ ///Indents the children of an inserted element, based on the indentation of the parent element.
+ public static void IndentChildren(this XElement parent) {
+ var parentPrefix = parent.GetPrecedingWhitespace();
+ if (parentPrefix == null) // No known indent to start from
+ return;
+
+ // Copy the child list before we start mutating.
+ // Looking at XContainer source code, this isn't
+ // strictly necessary.
+ var childElements = parent.Elements().ToList();
+ if (childElements.Count == 0)
+ return;
+ parent.Add(parentPrefix); // Indent the closing tag.
+
+ var childPrefix = GetChildPrefix(parent.Depth(), parentPrefix.Value);
+ if (string.IsNullOrEmpty(childPrefix))
+ return;
+
+ foreach (var child in childElements) {
+ child.AddBeforeSelf(childPrefix);
+ IndentChildren(child);
+ }
+ }
+
+ public static int Depth(this XElement element) {
+ if (element.Parent == null)
+ return 0;
+ return element.Parent.Depth() + 1;
+ }
+
+ private static string GetChildPrefix(int parentDepth, string parentPrefix) {
+ string newLine = parentPrefix.Substring(0, parentPrefix.TakeWhile(c => c == '\r' || c == '\n').Count());
+ parentPrefix = parentPrefix.Substring(newLine.Length);
+
+ string levelIndent;
+ if (parentDepth == 0)
+ return newLine; // Don't know how to indent
+ else
+ levelIndent = parentPrefix.Substring(0, parentPrefix.Length / parentDepth);
+
+ return newLine + string.Concat(Enumerable.Repeat(levelIndent, parentDepth + 1));
+ }
}
}