Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
{
using System.Collections.Immutable;

public interface IRule { }
interface IRule { }

public interface IProjectPropertiesContext { }
interface IProjectPropertiesContext { }

public interface IPropertySheet { }
interface IPropertySheet { }

public class ProjectPropertiesContext : IProjectPropertiesContext
class ProjectPropertiesContext : IProjectPropertiesContext
{
}

Expand All @@ -18,12 +18,12 @@ partial class ProjectTree
[Required]
readonly string caption;
readonly string filePath;
readonly System.Drawing.Image icon;
readonly System.Drawing.Image expandedIcon;
readonly string iconMoniker;
readonly string expandedIconMoniker;
readonly bool visible;
readonly IRule browseObjectProperties;
readonly ImmutableHashSet<string> capabilities;
readonly ImmutableSortedSet<ProjectTree> children;
readonly ImmutableList<ProjectTree> children;
}

[GenerateImmutable(DefineInterface = true, GenerateBuilder = true, DefineWithMethodsPerProperty = true, DefineRootedStruct = true, Delta = true)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,17 @@ public void MovingNodeAroundHierarchy()
var moved = root.RemoveDescendent(aa).AddDescendent(aa, ab);

var history = moved.ChangesSince(root);
Assert.Equal(1, history.Count);
Assert.Equal(2, history.Count);

Assert.Equal(ChangeKind.Replaced, history[0].Kind);
Assert.Same(aa, history[0].Before);
Assert.Same(aa, history[0].After);
Assert.Equal(ProjectTreeChangedProperties.Parent, history[0].Changes);

Assert.Equal(ChangeKind.Replaced, history[1].Kind);
Assert.Same(ab, history[1].Before);
Assert.Same(moved.Children[0], history[1].After);
Assert.Equal(ProjectTreeChangedProperties.PositionUnderParent, history[1].Changes);
}

[Fact]
Expand All @@ -460,12 +466,19 @@ public void MovingNodeAroundHierarchyWithOtherChanges()
var moved = root.RemoveDescendent(aa).AddDescendent(aaModified, ab);

var history = moved.ChangesSince(root);
Assert.Equal(1, history.Count);
Assert.Equal(2, history.Count);

Assert.Equal(ChangeKind.Replaced, history[0].Kind);
Assert.Equal(ProjectTreeChangedProperties.Parent | ProjectTreeChangedProperties.Visible, history[0].Changes);
Assert.Same(aa, history[0].Before);
Assert.Same(aaModified, history[0].After);
Assert.Equal(aa.Identity, history[0].Identity);

Assert.Equal(ChangeKind.Replaced, history[1].Kind);
Assert.Equal(ProjectTreeChangedProperties.PositionUnderParent, history[1].Changes);
Assert.Same(ab, history[1].Before);
Assert.Same(moved.Children[0], history[1].After);
Assert.Equal(ab.Identity, history[1].Identity);
}

[Fact]
Expand All @@ -480,17 +493,24 @@ public void MovingNodeAroundHierarchyWithChildAdds()
var moved = root.RemoveDescendent(aa).AddDescendent(aaModified, ab);

var history = moved.ChangesSince(root);
Assert.Equal(2, history.Count);
Assert.Equal(3, history.Count);

Assert.Equal(ChangeKind.Replaced, history[0].Kind);
Assert.Equal(ProjectTreeChangedProperties.Parent, history[0].Changes);
Assert.Same(aa, history[0].Before);
Assert.Same(aaModified, history[0].After);
Assert.Equal(aa.Identity, history[0].Identity);

Assert.Equal(ChangeKind.Added, history[1].Kind);
Assert.Same(aaModified.Children[0], history[1].After);
Assert.Null(history[1].Before);
Assert.Equal(aaModified.Children[0].Identity, history[1].Identity);
Assert.Equal(ChangeKind.Replaced, history[1].Kind);
Assert.Equal(ProjectTreeChangedProperties.PositionUnderParent, history[1].Changes);
Assert.Same(ab, history[1].Before);
Assert.Same(moved.Children[0], history[1].After);
Assert.Equal(ab.Identity, history[1].Identity);

Assert.Equal(ChangeKind.Added, history[2].Kind);
Assert.Same(aaModified.Children[0], history[2].After);
Assert.Null(history[2].Before);
Assert.Equal(aaModified.Children[0].Identity, history[2].Identity);
}

[Fact]
Expand All @@ -506,7 +526,8 @@ public void MovingNodeAroundHierarchyWithChildRemoves()
var moved = root.RemoveDescendent(aa).AddDescendent(aaModified, ab);

var history = moved.ChangesSince(root);
Assert.Equal(2, history.Count);
Assert.Equal(3, history.Count);

Assert.Equal(ChangeKind.Removed, history[0].Kind);
Assert.Same(aa.Children[0], history[0].Before);
Assert.Null(history[0].After);
Expand All @@ -517,6 +538,12 @@ public void MovingNodeAroundHierarchyWithChildRemoves()
Assert.Same(aa, history[1].Before);
Assert.Same(aaModified, history[1].After);
Assert.Equal(aa.Identity, history[1].Identity);

Assert.Equal(ChangeKind.Replaced, history[2].Kind);
Assert.Equal(ProjectTreeChangedProperties.PositionUnderParent, history[2].Changes);
Assert.Same(ab, history[2].Before);
Assert.Same(moved.Children[0], history[2].After);
Assert.Equal(ab.Identity, history[2].Identity);
}

[Fact]
Expand All @@ -527,7 +554,8 @@ public void RepositioningNodeWithinParentsChildren()
aa = ProjectTree.Create("AA"),
ab = ProjectTree.Create("AB"));
var ac = aa.WithCaption("AC");
var modified = root.ReplaceDescendent(aa, ac);
// TODO: we should generate a method for ordered collections for reordering.
var modified = root.WithChildren(root.Children.Remove(aa).Insert(1, ac));

var history = modified.ChangesSince(root);
Assert.Equal(1, history.Count);
Expand All @@ -536,5 +564,32 @@ public void RepositioningNodeWithinParentsChildren()
Assert.Same(aa, history[0].Before);
Assert.Same(ac, history[0].After);
}

[Fact(Skip = "Not yet passing")]
public void ReorderChildrenInOrderedList()
{
ProjectTree aa, ab, ac, ad, ae;
var original = ProjectTree.Create("A").WithChildren(
aa = ProjectTree.Create("AA"),
ab = ProjectTree.Create("AB"),
ac = ProjectTree.Create("AC"),
ad = ProjectTree.Create("AD"),
ae = ProjectTree.Create("AE"));
var reordered = original.RemoveChild(ac).AddChild(ac); // move AC to bottom.
Assert.Equal(new string[] { "AA", "AB", "AD", "AE", "AC" }, reordered.Children.Select(c => c.Caption));

var changes = reordered.ChangesSince(original);
Assert.NotEmpty(changes);

// Now move it back to its original position.
var restored = reordered.WithChildren(
reordered.Children.Remove(ac).Insert(2, ac));
Assert.Equal(new string[] { "AA", "AB", "AC", "AD", "AE" }, restored.Children.Select(c => c.Caption));

changes = restored.ChangesSince(reordered);
Assert.NotEmpty(changes);

Assert.Empty(restored.ChangesSince(original));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public abstract class ProjectTreeNodeTestBase : IDisposable

private int nodeCounter;

internal ImmutableSortedSet<ProjectTree> Children { get; set; }
internal ImmutableList<ProjectTree> Children { get; set; }

public ProjectTreeNodeTestBase()
{
this.nodeCounter = 0;
this.Children = ImmutableSortedSet.Create(ProjectTreeSort.Default);
this.Children = ImmutableList.Create<ProjectTree>();
}

public void Dispose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static IReadOnlyList<DiffGram> GetDelta(ProjectTree before, ProjectTree a

static partial void CreateDefaultTemplate(ref ProjectTree.Template template)
{
template.Children = ImmutableSortedSet.Create(ProjectTreeSort.Default);
template.Children = ImmutableList.Create<ProjectTree>();
template.Capabilities = ImmutableHashSet.Create<string>(StringComparer.OrdinalIgnoreCase);
template.Visible = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,13 @@ private MethodDeclarationSyntax CreateParamsElementArrayMethod(MetaField field,
var lambdaParameter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("v"));
var argument = passThroughChildSync
? (ExpressionSyntax)Syntax.EnumerableExtension(
SyntaxFactory.IdentifierName(nameof(Enumerable.Select)),
ValuesParameterName,
SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(Syntax.ThisDot(SyncImmediateChildToCurrentVersionMethodName)))))
SyntaxFactory.IdentifierName(nameof(Enumerable.ToList)),
Syntax.EnumerableExtension(
SyntaxFactory.IdentifierName(nameof(Enumerable.Select)),
ValuesParameterName,
SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(Syntax.ThisDot(SyncImmediateChildToCurrentVersionMethodName))))),
SyntaxFactory.ArgumentList())
: ValuesParameterName;

paramsArrayMethod = this.AddMethodBody(
Expand Down
48 changes: 31 additions & 17 deletions src/ImmutableObjectGraph.Generation/CodeGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1289,24 +1289,9 @@ public MetaType RootAncestorOrThisType
}
}

public bool ChildrenAreSorted
{
get
{
// Not very precise, but it does the job for now.
return this.RecursiveParent.RecursiveField.Type.Name == nameof(ImmutableSortedSet<int>);
}
}
public bool ChildrenAreSorted => this.RecursiveParent.RecursiveField.IsCollectionSorted;

public bool ChildrenAreOrdered
{
get
{
// Not very precise, but it does the job for now.
var namedType = this.RecursiveParent.RecursiveField.Type as INamedTypeSymbol;
return namedType != null && namedType.AllInterfaces.Any(iface => iface.Name == nameof(IReadOnlyList<int>));
}
}
public bool ChildrenAreOrdered => this.RecursiveParent.RecursiveField.IsCollectionOrdered;

public IEnumerable<MetaField> GetFieldsBeyond(MetaType ancestor)
{
Expand Down Expand Up @@ -1435,6 +1420,35 @@ public MetaType TypeAsGeneratedImmutable

public bool IsDictionary => IsDictionaryType(this.Symbol.Type);

/// <summary>
/// Gets a value indicating whether the elements in the collection have a defined order.
/// This may be because it is sorted, or because it is documented to retain the order
/// in which the elements were inserted (e.g. a list).
/// </summary>
public bool IsCollectionOrdered
{
get
{
if (this.IsCollection)
{
// Not very precise, but it does the job for now.
var namedType = this.Type as INamedTypeSymbol;
return namedType?.AllInterfaces.Any(iface => iface.Name == nameof(IReadOnlyList<int>)) ?? false;
}

return false;
}
}

public bool IsCollectionSorted
{
get
{
// Not very precise, but it does the job for now.
return this.Type.Name == nameof(ImmutableSortedSet<int>);
}
}

public MetaType DeclaringType
{
get { return new MetaType(this.metaType.Generator, this.Symbol.ContainingType); }
Expand Down