Permalink
Browse files

Add option section capability

 - See the updated README for description.
  • Loading branch information...
schmalls committed Aug 18, 2016
1 parent 3d6fe8f commit 2624ae99318c627bd7a26c46759a24b53ac631ed
@@ -13,7 +13,6 @@
// limitations under the License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
@@ -48,10 +47,10 @@ public enum ArrangeOptions
All = -1,
CombineRootElements = 1 << 0,
KeepCommentWithNext = 1 << 1,
KeepImportWithNext = 1 << 2,
SortRootElements = 1 << 3,
SplitItemGroups = 1 << 4,
SortRootElements = 1 << 2,
SplitItemGroups = 1 << 3,
NoRoot = All & ~CombineRootElements & ~SortRootElements,
NoSortRootElements = ~SortRootElements,
}
/// <summary>
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
@@ -95,21 +96,30 @@ private void ArrangeElementByNameThenAttributes(XElement element)
}
public void Arrange(XDocument input)
{
{
_attributeKeyComparer = CreateAttributeKeyComparer(_sortAttributes);
_nodeNameComparer = new NodeNameComparer(_stickyElementNames);
_attributeKeyComparer = CreateAttributeKeyComparer(_sortAttributes);
input.Root.ReplaceNodes(
UnfoldSections(
FoldSections(input.Root.Nodes())
.OrderBy(x => x.OptionSection ? 2 : 1)
)
);
}
CombineRootElementsAndSort(input, _options);
if (_options.HasFlag(CsProjArrange.ArrangeOptions.SplitItemGroups))
{
SplitItemGroups(input, _stickyElementNames);
private void ArrangeSection(XNodeSection section, CsProjArrange.ArrangeOptions options)
{
CombineRootElementsAndSort(section, options);
if (options.HasFlag(CsProjArrange.ArrangeOptions.SplitItemGroups)) {
SplitItemGroups(section, _stickyElementNames);
}
if (_options.HasFlag(CsProjArrange.ArrangeOptions.SortRootElements))
{
SortRootElements(input);
if (options.HasFlag(CsProjArrange.ArrangeOptions.SortRootElements)) {
SortRootElements(section);
}
}
@@ -118,41 +128,88 @@ private AttributeKeyComparer CreateAttributeKeyComparer(IEnumerable<string> sort
return new AttributeKeyComparer(sortAttributes);
}
private void SortRootElements(XDocument input)
private void SortRootElements(XNodeSection section)
{
// Sort the elements in root.
input.Root.ReplaceNodes(
UnfoldComments(
FoldComments(input.Root.Nodes())
.OrderBy(x => x.Element, _nodeNameComparer)
.ThenBy(x => x.Element.NodeType == XmlNodeType.Element ? x.Element.Attributes() : null, _attributeKeyComparer))
);
if (_options.HasFlag(CsProjArrange.ArrangeOptions.KeepCommentWithNext)) {
section.Nodes =
UnfoldComments(
FoldComments(section.Nodes)
.OrderBy(x => x.Element, _nodeNameComparer)
.ThenBy(x => x.Element.NodeType == XmlNodeType.Element ? x.Element.Attributes() : null, _attributeKeyComparer)
)
.ToList();
} else {
section.Nodes =
section.Nodes
.OrderBy(x => x, _nodeNameComparer)
.ThenBy(x => x.NodeType == XmlNodeType.Element ? ((XElement)x).Attributes() : null,
_attributeKeyComparer)
.ToList();
}
}
private class XElementWithComments
private class XElementsWithComments
{
public XElement Element {
public IList<XComment> Comments
{
get;
set;
}
public IList<XComment> Comments
public XElement Element
{
get;
set;
}
public XElementWithComments()
public XElementsWithComments()
{
Comments = new List<XComment>();
}
}
private IEnumerable<XElementWithComments> FoldComments(IEnumerable<XNode> nodes)
private class XNodeSection
{
var elements = new List<XElementWithComments>();
public IList<XNode> Nodes
{
get;
set;
}
public XComment OpenComment
{
get;
set;
}
public XComment CloseComment
{
get;
set;
}
public CsProjArrange.ArrangeOptions? Options
{
get;
set;
}
XElementWithComments current = new XElementWithComments();
public bool OptionSection
{
get;
set;
}
public XNodeSection()
{
Nodes = new List<XNode>();
}
}
private IEnumerable<XElementsWithComments> FoldComments(IEnumerable<XNode> nodes)
{
XElementsWithComments current = new XElementsWithComments();
foreach (var node in nodes)
{
switch (node.NodeType)
@@ -162,8 +219,8 @@ private IEnumerable<XElementWithComments> FoldComments(IEnumerable<XNode> nodes)
break;
case XmlNodeType.Element:
current.Element = node as XElement;
elements.Add(current);
current = new XElementWithComments();
yield return current;
current = new XElementsWithComments();
break;
}
}
@@ -172,31 +229,92 @@ private IEnumerable<XElementWithComments> FoldComments(IEnumerable<XNode> nodes)
if (current.Comments.Any())
{
current.Element = new XElement(NameOfFakeNodeForLastComment);
elements.Add(current);
yield return current;
}
}
return elements;
private string _optionsOpenCommentRegexString = string.Format(@"^(\s*Options:\s*)({0}(,({0}))*)\s*$", string.Join("|", ((CsProjArrange.ArrangeOptions[])Enum.GetValues(typeof(CsProjArrange.ArrangeOptions))).Select(x => x.ToString())));
private string _optionsCloseCommentRegexString = @"^(\s*/Options\s*)$";
private IEnumerable<XNodeSection> FoldSections(IEnumerable<XNode> nodes)
{
XNodeSection none = new XNodeSection();
XNodeSection current = none;
bool section = false;
foreach (var node in nodes) {
switch (node.NodeType) {
case XmlNodeType.Comment:
var comment = (node as XComment).Value;
if (section) {
if (Regex.IsMatch(comment, _optionsCloseCommentRegexString)) {
current.CloseComment = node as XComment;
yield return current;
current = none;
section = false;
} else {
current.Nodes.Add(node);
}
} else {
var match = Regex.Match(comment, _optionsOpenCommentRegexString);
if (match.Success) {
if (current != none) {
// Missing closing comment for previous section.
yield return current;
}
current = new XNodeSection();
current.OpenComment = node as XComment;
current.Options = (CsProjArrange.ArrangeOptions)Enum.Parse(typeof(CsProjArrange.ArrangeOptions), match.Groups[2].Value);
current.OptionSection = true;
section = true;
} else {
current.Nodes.Add(node);
}
}
break;
default:
current.Nodes.Add(node);
break;
}
}
if (current != none) {
yield return current;
}
yield return none;
}
private IEnumerable<XNode> UnfoldComments(IEnumerable<XElementWithComments> elements)
private IEnumerable<XNode> UnfoldComments(IEnumerable<XElementsWithComments> elements)
{
var nodes = new List<XNode>();
foreach (var element in elements)
{
foreach (var element in elements) {
nodes.AddRange(element.Comments);
if (element.Element.Name != NameOfFakeNodeForLastComment)
{
if (element.Element.Name != NameOfFakeNodeForLastComment) {
nodes.Add(element.Element);
}
}
return nodes;
}
private void SplitItemGroups(XDocument input, IList<string> stickyElementNames)
private IEnumerable<XNode> UnfoldSections(IEnumerable<XNodeSection> sections)
{
var nodes = new List<XNode>();
foreach (var section in sections)
{
if (section.OpenComment != null) {
nodes.Add(section.OpenComment);
}
ArrangeSection(section, section.Options ?? _options);
nodes.AddRange(section.Nodes);
if (section.CloseComment != null) {
nodes.Add(section.CloseComment);
}
}
return nodes;
}
private void SplitItemGroups(XNodeSection section, IList<string> stickyElementNames)
{
var ns = input.Root.Name.Namespace;
foreach (var group in input.Root.Elements(ns + "ItemGroup"))
foreach (var group in section.Nodes.Where(x => x is XElement).Cast<XElement>().Where(x => x.Name.LocalName == "ItemGroup").ToList())
{
var uniqueTypes =
@group.Elements()
@@ -217,17 +335,20 @@ private void SplitItemGroups(XDocument input, IList<string> stickyElementNames)
foreach (var type in restTypes)
{
var newElement = new XElement(@group.Name, @group.Attributes(), @group.Elements(type));
@group.AddAfterSelf(newElement);
// Insert node after.
section.Nodes.Insert(section.Nodes.IndexOf(@group) + 1, newElement);
}
@group.ReplaceNodes(@group.Elements(firstType));
}
}
}
private void CombineRootElementsAndSort(XDocument input, CsProjArrange.ArrangeOptions options)
private void CombineRootElementsAndSort(XNodeSection section, CsProjArrange.ArrangeOptions options)
{
var combineGroups =
input.Root.Elements()
section.Nodes
.Where(x => x is XElement)
.Cast<XElement>()
.GroupBy(
x =>
new CombineGroups
@@ -244,7 +365,7 @@ private void CombineRootElementsAndSort(XDocument input, CsProjArrange.ArrangeOp
{
if (options.HasFlag(CsProjArrange.ArrangeOptions.CombineRootElements))
{
CombineIdenticalRootElements(elementGroup);
CombineIdenticalRootElements(section, elementGroup);
}
ArrangeAllElementsInGroup(elementGroup);
@@ -259,7 +380,7 @@ private void ArrangeAllElementsInGroup(IGrouping<CombineGroups, XElement> elemen
}
}
private void CombineIdenticalRootElements(IGrouping<CombineGroups, XElement> elementGroup)
private void CombineIdenticalRootElements(XNodeSection section, IGrouping<CombineGroups, XElement> elementGroup)
{
XElement first = elementGroup.First();
// Combine multiple elements if they have the same name and attributes.
@@ -269,7 +390,7 @@ private void CombineIdenticalRootElements(IGrouping<CombineGroups, XElement> ele
first.Add(restGroup.SelectMany(x => x.Elements()));
foreach (var rest in restGroup)
{
rest.Remove();
section.Nodes.Remove(rest);
}
}
}
@@ -13,13 +13,6 @@ public class NodeNameComparer : IComparer<XNode>
public NodeNameComparer(IList<string> stickyElementNames = null, CsProjArrange.ArrangeOptions options = CsProjArrange.ArrangeOptions.None)
{
StickyElementNames = stickyElementNames ?? new string[] { };
Options = options;
}
public CsProjArrange.ArrangeOptions Options
{
get;
set;
}
public IList<string> StickyElementNames
@@ -60,34 +53,11 @@ private static int Compare(int stickyElement1, int stickyElement2)
private string GetName(XNode node)
{
string name = null;
if (node.NodeType == XmlNodeType.Comment) {
name = GetNextClosestElementName(node);
}
if (node.NodeType == XmlNodeType.Element) {
name = ((XElement)node).Name.LocalName;
if (Options.HasFlag(CsProjArrange.ArrangeOptions.KeepImportWithNext)) {
if (name == "Import") {
// HACK: Need to figure out how to handle import. Just sticking to next element for now.
name = GetNextClosestElementName(node.NextNode);
}
}
}
return name;
}
private string GetNextClosestElementName(XNode node)
{
XElement result;
XNode current = node;
while (((result = current as XElement) == null) && ((current = current.NextNode) != null)) {
// Everything is already done in the condition.
}
if (result == null) {
return null;
}
return result.Name.LocalName;
}
}
}
Oops, something went wrong.

0 comments on commit 2624ae9

Please sign in to comment.