private bool CompareAttributes(
                AttributeSyntax oldAttribute,
                AttributeSyntax newAttribute,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldAttribute != null && newAttribute != null);

                bool same = true;

                if (!CompareNames(oldAttribute.Name, newAttribute.Name))
                {
                    EnqueueChangeEvent(newAttribute, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    same = false;
                }

                // If arguments have changed enqueue a element changed (arguments changed) node
                if (!CompareAttributeArguments(oldAttribute.ArgumentList, newAttribute.ArgumentList))
                {
                    EnqueueChangeEvent(newAttribute, newNodeParent, CodeModelEventType.ArgChange, eventQueue);
                    same = false;
                }

                return same;
            }
 private bool CompareAttributeLists(
     AttributeListSyntax oldAttributeList,
     AttributeListSyntax newAttributeList,
     SyntaxNode newNodeParent,
     CodeModelEventQueue eventQueue)
 {
     return CompareChildren(
         CompareAttributes,
         oldAttributeList.Attributes.AsReadOnlyList(),
         newAttributeList.Attributes.AsReadOnlyList(),
         newNodeParent,
         CodeModelEventType.Unknown,
         eventQueue);
 }
            private void CompareCompilationUnits(
                CompilationUnitSyntax oldCompilationUnit,
                CompilationUnitSyntax newCompilationUnit,
                CodeModelEventQueue eventQueue)
            {
                // Note: In the C# legacy code model, events are generated for the top-level
                // namespace that is at the root of every parse tree. In the Roslyn C# code model
                // implementation, we won't bother.

                CompareChildren(
                    CompareNamespacesOrTypes,
                    GetValidMembers(oldCompilationUnit),
                    GetValidMembers(newCompilationUnit),
                    (SyntaxNode)null,
                    CodeModelEventType.Unknown,
                    eventQueue);
            }
Exemple #4
0
            protected DeclarationChange CompareRenamedDeclarations <TNode, TParent>(
                NodeComparison <TNode, TParent> compare,
                IReadOnlyList <TNode> oldChildren,
                IReadOnlyList <TNode> newChildren,
                SyntaxNode oldNode,
                SyntaxNode newNode,
                TParent newNodeParent,
                CodeModelEventQueue eventQueue)
                where TNode : SyntaxNode
                where TParent : SyntaxNode
            {
                var oldCount = oldChildren.Count;
                var newCount = newChildren.Count;

                if (oldCount == newCount)
                {
                    // We now check the children of the old and new types against each other. If any of them have changed,
                    // it means that the old type has essentially been removed and a new one added.
                    for (var i = 0; i < oldCount; i++)
                    {
                        if (!compare(oldChildren[i], newChildren[i], newNodeParent, null))
                        {
                            EnqueueRemoveEvent(oldNode, newNodeParent, eventQueue);
                            EnqueueAddEvent(newNode, newNodeParent, eventQueue);

                            // Report that the whole declaration has changed
                            return(DeclarationChange.WholeDeclaration);
                        }
                    }

                    // The children are all the same, so only the name has changed.
                    return(DeclarationChange.NameOnly);
                }
                else
                {
                    // Since the number of members is different, essentially the old type has been removed, and a new one added.
                    EnqueueRemoveEvent(oldNode, newNodeParent, eventQueue);
                    EnqueueAddEvent(newNode, newNodeParent, eventQueue);

                    // Report that the whole declaration has changed
                    return(DeclarationChange.WholeDeclaration);
                }
            }
Exemple #5
0
            protected bool CompareChildren <TNode, TParent>(
                NodeComparison <TNode, TParent> compare,
                IReadOnlyList <TNode> oldChildren,
                IReadOnlyList <TNode> newChildren,
                TParent newNodeParent,
                CodeModelEventType eventType,
                CodeModelEventQueue eventQueue)
                where TNode : SyntaxNode
                where TParent : SyntaxNode
            {
                var oldCount = oldChildren.Count;
                var newCount = newChildren.Count;

                if (oldCount == newCount)
                {
                    return(FindDifferentChild(compare, oldChildren, newChildren, newNodeParent, eventType, eventQueue));
                }
                else if (Math.Abs(oldCount - newCount) > MaxChildDelta)
                {
                    // We got two discrepancies, enqueue element changed node for containing node
                    EnqueueChangeEvent(newNodeParent, null, eventType, eventQueue);
                }
                else
                {
                    if (oldCount > newCount)
                    {
                        FindRemovedChild(compare, oldChildren, newChildren, newNodeParent, eventType, oldCount - newCount, eventQueue);
                    }
                    else
                    {
                        FindAddedChild(compare, oldChildren, newChildren, newNodeParent, eventType, newCount - oldCount, eventQueue);
                    }
                }

                return(false);
            }
            private bool CompareTypeDeclarations(
                MemberDeclarationSyntax oldMember,
                MemberDeclarationSyntax newMember,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldMember != null && newMember != null);
                Debug.Assert(oldMember is BaseTypeDeclarationSyntax || oldMember is DelegateDeclarationSyntax);
                Debug.Assert(newMember is BaseTypeDeclarationSyntax || newMember is DelegateDeclarationSyntax);

                // If the kind doesn't match, it has to be a remove/add.
                if (oldMember.Kind() != newMember.Kind())
                {
                    EnqueueRemoveEvent(oldMember, newNodeParent, eventQueue);
                    EnqueueAddEvent(newMember, newNodeParent, eventQueue);

                    return false;
                }

                if (oldMember is BaseTypeDeclarationSyntax)
                {
                    var oldType = (BaseTypeDeclarationSyntax)oldMember;
                    var newType = (BaseTypeDeclarationSyntax)newMember;

                    var oldMembers = GetValidMembers(oldType);
                    var newMembers = GetValidMembers(newType);

                    bool same = true;

                    // If the type name is different, it might mean that the whole type has been removed and a new one added.
                    // In that case, we shouldn't do any other checks and instead return immediately.
                    if (!StringComparer.Ordinal.Equals(oldType.Identifier.ToString(), newType.Identifier.ToString()))
                    {
                        var change = CompareRenamedDeclarations(
                            CompareMemberDeclarations,
                            oldMembers,
                            newMembers,
                            oldType,
                            newType,
                            newNodeParent,
                            eventQueue);

                        if (change == DeclarationChange.WholeDeclaration)
                        {
                            return false;
                        }

                        same = false;
                        EnqueueChangeEvent(newType, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    }

                    if (!CompareModifiers(oldType, newType))
                    {
                        same = false;
                        EnqueueChangeEvent(newType, newNodeParent, CodeModelEventType.Unknown, eventQueue);
                    }

                    if (!CompareBaseLists(oldType, newType))
                    {
                        same = false;
                        EnqueueChangeEvent(newType, newNodeParent, CodeModelEventType.BaseChange, eventQueue);
                    }

                    same &= CompareChildren(
                        CompareAttributeLists,
                        oldType.AttributeLists.AsReadOnlyList(),
                        newType.AttributeLists.AsReadOnlyList(),
                        newType,
                        CodeModelEventType.Unknown,
                        eventQueue);

                    same &= CompareChildren(
                        CompareMemberDeclarations,
                        oldMembers,
                        newMembers,
                        newType,
                        CodeModelEventType.Unknown,
                        eventQueue);

                    return same;
                }
                else if (oldMember is DelegateDeclarationSyntax)
                {
                    var oldDelegate = (DelegateDeclarationSyntax)oldMember;
                    var newDelegate = (DelegateDeclarationSyntax)newMember;

                    bool same = true;

                    // If the delegate name is different, it might mean that the whole delegate has been removed and a new one added.
                    // In that case, we shouldn't do any other checks and instead return immediately.
                    if (!StringComparer.Ordinal.Equals(oldDelegate.Identifier.ToString(), newDelegate.Identifier.ToString()))
                    {
                        var change = CompareRenamedDeclarations(
                            CompareParameters,
                            oldDelegate.ParameterList.Parameters.AsReadOnlyList(),
                            newDelegate.ParameterList.Parameters.AsReadOnlyList(),
                            oldDelegate,
                            newDelegate,
                            newNodeParent,
                            eventQueue);

                        if (change == DeclarationChange.WholeDeclaration)
                        {
                            return false;
                        }

                        same = false;
                        EnqueueChangeEvent(newDelegate, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    }

                    if (!CompareModifiers(oldDelegate, newDelegate))
                    {
                        same = false;
                        EnqueueChangeEvent(newDelegate, newNodeParent, CodeModelEventType.Unknown, eventQueue);
                    }

                    if (!CompareTypes(oldDelegate.ReturnType, newDelegate.ReturnType))
                    {
                        same = false;
                        EnqueueChangeEvent(newDelegate, newNodeParent, CodeModelEventType.TypeRefChange, eventQueue);
                    }

                    same &= CompareChildren(
                        CompareAttributeLists,
                        oldDelegate.AttributeLists.AsReadOnlyList(),
                        newDelegate.AttributeLists.AsReadOnlyList(),
                        newDelegate,
                        CodeModelEventType.Unknown,
                        eventQueue);

                    same &= CompareChildren(
                        CompareParameters,
                        oldDelegate.ParameterList.Parameters.AsReadOnlyList(),
                        newDelegate.ParameterList.Parameters.AsReadOnlyList(),
                        newDelegate,
                        CodeModelEventType.SigChange,
                        eventQueue);

                    return same;
                }

                return false;
            }
            private bool CompareNamespaceDeclarations(
                NamespaceDeclarationSyntax oldNamespace,
                NamespaceDeclarationSyntax newNamespace,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldNamespace != null && newNamespace != null);

                // Check if the namespace nodes are identical w.r.t Name
                if (!CompareNames(oldNamespace.Name, newNamespace.Name))
                {
                    var change = CompareRenamedDeclarations(
                        CompareNamespacesOrTypes,
                        GetValidMembers(oldNamespace),
                        GetValidMembers(newNamespace),
                        oldNamespace,
                        newNamespace,
                        newNodeParent,
                        eventQueue);

                    if (change == DeclarationChange.NameOnly)
                    {
                        EnqueueChangeEvent(newNamespace, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    }

                    return false;
                }

                return CompareChildren(
                    CompareNamespacesOrTypes,
                    GetValidMembers(oldNamespace),
                    GetValidMembers(newNamespace),
                    newNamespace,
                    CodeModelEventType.Unknown,
                    eventQueue);
            }
            private bool CompareMethodDeclarations(
                BaseMethodDeclarationSyntax oldMethod,
                BaseMethodDeclarationSyntax newMethod,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldMethod != null && newMethod != null);

                if (!StringComparer.Ordinal.Equals(CodeModelService.GetName(oldMethod), CodeModelService.GetName(newMethod)))
                {
                    var change = CompareRenamedDeclarations(
                        CompareParameters,
                        oldMethod.ParameterList.Parameters.AsReadOnlyList(),
                        newMethod.ParameterList.Parameters.AsReadOnlyList(),
                        oldMethod,
                        newMethod,
                        newNodeParent,
                        eventQueue);

                    if (change == DeclarationChange.NameOnly)
                    {
                        EnqueueChangeEvent(newMethod, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    }

                    return false;
                }
                else
                {
                    bool same = true;

                    if (!CompareModifiers(oldMethod, newMethod))
                    {
                        same = false;
                        EnqueueChangeEvent(newMethod, newNodeParent, CodeModelEventType.Unknown, eventQueue);
                    }

                    if (!CompareTypes(GetReturnType(oldMethod), GetReturnType(newMethod)))
                    {
                        same = false;
                        EnqueueChangeEvent(newMethod, newNodeParent, CodeModelEventType.TypeRefChange, eventQueue);
                    }

                    same &= CompareChildren(
                        CompareAttributeLists,
                        oldMethod.AttributeLists.AsReadOnlyList(),
                        newMethod.AttributeLists.AsReadOnlyList(),
                        newMethod,
                        CodeModelEventType.Unknown,
                        eventQueue);

                    same &= CompareChildren(
                        CompareParameters,
                        oldMethod.ParameterList.Parameters.AsReadOnlyList(),
                        newMethod.ParameterList.Parameters.AsReadOnlyList(),
                        newMethod,
                        CodeModelEventType.SigChange,
                        eventQueue);

                    return same;
                }
            }
Exemple #9
0
 protected abstract void CollectCore(SyntaxNode oldRoot, SyntaxNode newRoot, CodeModelEventQueue eventQueue);
 private static void ChangeEventQueueForAttributes(AttributeSyntax attribute, SyntaxNode parent, CodeModelEventType eventType, CodeModelEventQueue eventQueue)
 {
     if (parent is BaseFieldDeclarationSyntax)
     {
         foreach (var variableDeclarator in ((BaseFieldDeclarationSyntax)parent).Declaration.Variables)
         {
             eventQueue.EnqueueChangeEvent(attribute, variableDeclarator, eventType);
         }
     }
     else
     {
         eventQueue.EnqueueChangeEvent(attribute, parent, eventType);
     }
 }
            private bool CompareEnumMemberDeclarations(
                EnumMemberDeclarationSyntax oldEnumMember,
                EnumMemberDeclarationSyntax newEnumMember,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldEnumMember != null && newEnumMember != null);

                bool same = true;

                if (!StringComparer.Ordinal.Equals(CodeModelService.GetName(oldEnumMember), CodeModelService.GetName(newEnumMember)))
                {
                    EnqueueChangeEvent(newEnumMember, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    same = false;
                }

                same &= CompareChildren(
                    CompareAttributeLists,
                    oldEnumMember.AttributeLists.AsReadOnlyList(),
                    newEnumMember.AttributeLists.AsReadOnlyList(),
                    newEnumMember,
                    CodeModelEventType.Unknown,
                    eventQueue);

                return same;
            }
            private bool CompareMemberDeclarations(
                MemberDeclarationSyntax oldMember,
                MemberDeclarationSyntax newMember,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldMember != null && newMember != null);

                // If the kind doesn't match, it has to be a remove/add.
                if (oldMember.Kind() != newMember.Kind())
                {
                    EnqueueRemoveEvent(oldMember, newNodeParent, eventQueue);
                    EnqueueAddEvent(newMember, newNodeParent, eventQueue);

                    return false;
                }

                if (oldMember is BaseTypeDeclarationSyntax ||
                    oldMember is DelegateDeclarationSyntax)
                {
                    return CompareTypeDeclarations(oldMember, newMember, newNodeParent, eventQueue);
                }
                else if (oldMember is BaseMethodDeclarationSyntax)
                {
                    return CompareMethodDeclarations((BaseMethodDeclarationSyntax)oldMember, (BaseMethodDeclarationSyntax)newMember, newNodeParent, eventQueue);
                }
                else if (oldMember is BaseFieldDeclarationSyntax)
                {
                    return CompareFieldDeclarations((BaseFieldDeclarationSyntax)oldMember, (BaseFieldDeclarationSyntax)newMember, newNodeParent, eventQueue);
                }
                else if (oldMember is BasePropertyDeclarationSyntax)
                {
                    return ComparePropertyDeclarations((BasePropertyDeclarationSyntax)oldMember, (BasePropertyDeclarationSyntax)newMember, newNodeParent, eventQueue);
                }
                else if (oldMember is EnumMemberDeclarationSyntax)
                {
                    return CompareEnumMemberDeclarations((EnumMemberDeclarationSyntax)oldMember, (EnumMemberDeclarationSyntax)newMember, newNodeParent, eventQueue);
                }

                throw new NotImplementedException();
            }
            private bool CompareParameters(ParameterSyntax oldParameter, ParameterSyntax newParameter, SyntaxNode newNodeParent, CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldParameter != null && newParameter != null);

                bool same = true;

                if (!StringComparer.Ordinal.Equals(CodeModelService.GetName(oldParameter), CodeModelService.GetName(newParameter)))
                {
                    EnqueueChangeEvent(newParameter, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    same = false;
                }

                // If modifiers or the type have changed enqueue a element changed (unknown change) node
                if (!CompareModifiers(oldParameter, newParameter) ||
                    !CompareTypes(oldParameter.Type, newParameter.Type))
                {
                    EnqueueChangeEvent(newParameter, newNodeParent, CodeModelEventType.Unknown, eventQueue);
                    same = false;
                }

                return same;
            }
Exemple #14
0
 protected abstract void EnqueueChangeEvent(SyntaxNode node, SyntaxNode parent, CodeModelEventType eventType, CodeModelEventQueue eventQueue);
Exemple #15
0
 protected abstract void EnqueueRemoveEvent(SyntaxNode node, SyntaxNode parent, CodeModelEventQueue eventQueue);
            private bool ComparePropertyDeclarations(
                BasePropertyDeclarationSyntax oldProperty,
                BasePropertyDeclarationSyntax newProperty,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldProperty != null && newProperty != null);

                bool same = true;

                if (!StringComparer.Ordinal.Equals(CodeModelService.GetName(oldProperty), CodeModelService.GetName(newProperty)))
                {
                    EnqueueChangeEvent(newProperty, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    same = false;
                }

                // If modifiers have changed enqueue a element changed (unknown change) node
                if (!CompareModifiers(oldProperty, newProperty))
                {
                    EnqueueChangeEvent(newProperty, newNodeParent, CodeModelEventType.Unknown, eventQueue);
                    same = false;
                }

                // If return type had changed enqueue a element changed (typeref changed) node
                if (!CompareTypes(oldProperty.Type, newProperty.Type))
                {
                    EnqueueChangeEvent(newProperty, newNodeParent, CodeModelEventType.TypeRefChange, eventQueue);
                    same = false;
                }

                same &= CompareChildren(
                    CompareAttributeLists,
                    oldProperty.AttributeLists.AsReadOnlyList(),
                    newProperty.AttributeLists.AsReadOnlyList(),
                    newProperty,
                    CodeModelEventType.Unknown,
                    eventQueue);

                if (oldProperty is IndexerDeclarationSyntax)
                {
                    var oldIndexer = (IndexerDeclarationSyntax)oldProperty;
                    var newIndexer = (IndexerDeclarationSyntax)newProperty;
                    same &= CompareChildren(
                        CompareParameters,
                        oldIndexer.ParameterList.Parameters.AsReadOnlyList(),
                        newIndexer.ParameterList.Parameters.AsReadOnlyList(),
                        newIndexer,
                        CodeModelEventType.SigChange,
                        eventQueue);
                }

                return same;
            }
            private bool CompareNamespacesOrTypes(
                MemberDeclarationSyntax oldNamespaceOrType,
                MemberDeclarationSyntax newNamespaceOrType,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                // If the kind doesn't match, it has to be a remove/add.
                if (oldNamespaceOrType.Kind() != newNamespaceOrType.Kind())
                {
                    EnqueueRemoveEvent(oldNamespaceOrType, newNodeParent, eventQueue);
                    EnqueueAddEvent(newNamespaceOrType, newNodeParent, eventQueue);

                    return false;
                }

                if (oldNamespaceOrType is BaseTypeDeclarationSyntax ||
                    oldNamespaceOrType is DelegateDeclarationSyntax)
                {
                    return CompareTypeDeclarations(oldNamespaceOrType, newNamespaceOrType, newNodeParent, eventQueue);
                }
                else if (oldNamespaceOrType is NamespaceDeclarationSyntax)
                {
                    return CompareNamespaceDeclarations((NamespaceDeclarationSyntax)oldNamespaceOrType, (NamespaceDeclarationSyntax)newNamespaceOrType, newNodeParent, eventQueue);
                }

                return false;
            }
            private bool CompareVariableDeclarators(
                VariableDeclaratorSyntax oldVariableDeclarator,
                VariableDeclaratorSyntax newVariableDeclarator,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldVariableDeclarator != null && newVariableDeclarator != null);

                if (!StringComparer.Ordinal.Equals(CodeModelService.GetName(oldVariableDeclarator), CodeModelService.GetName(newVariableDeclarator)))
                {
                    EnqueueChangeEvent(newVariableDeclarator, newNodeParent, CodeModelEventType.Rename, eventQueue);
                    return false;
                }

                return true;
            }
 protected override void CollectCore(SyntaxNode oldRoot, SyntaxNode newRoot, CodeModelEventQueue eventQueue)
 {
     CompareCompilationUnits((CompilationUnitSyntax)oldRoot, (CompilationUnitSyntax)newRoot, eventQueue);
 }
            private bool CompareFieldDeclarations(
                BaseFieldDeclarationSyntax oldField,
                BaseFieldDeclarationSyntax newField,
                SyntaxNode newNodeParent,
                CodeModelEventQueue eventQueue)
            {
                Debug.Assert(oldField != null && newField != null);

                bool same = true;
                same &= CompareChildren(
                    CompareVariableDeclarators,
                    oldField.Declaration.Variables.AsReadOnlyList(),
                    newField.Declaration.Variables.AsReadOnlyList(),
                    newNodeParent,
                    CodeModelEventType.Unknown,
                    eventQueue);

                // If modifiers have changed enqueue a element changed (unknown change) node
                if ((oldField.Kind() != newField.Kind()) ||
                    !CompareModifiers(oldField, newField))
                {
                    EnqueueChangeEvent(newField, newNodeParent, CodeModelEventType.Unknown, eventQueue);
                    same = false;
                }

                // If type had changed enqueue a element changed (typeref changed) node
                if (!CompareTypes(oldField.Declaration.Type, newField.Declaration.Type))
                {
                    EnqueueChangeEvent(newField, newNodeParent, CodeModelEventType.TypeRefChange, eventQueue);
                    same = false;
                }

                same &= CompareChildren(
                    CompareAttributeLists,
                    oldField.AttributeLists.AsReadOnlyList(),
                    newField.AttributeLists.AsReadOnlyList(),
                    newField,
                    CodeModelEventType.Unknown, eventQueue);

                return same;
            }
            protected override void EnqueueRemoveEvent(SyntaxNode node, SyntaxNode parent, CodeModelEventQueue eventQueue)
            {
                if (eventQueue == null)
                {
                    return;
                }

                if (node is IncompleteMemberSyntax)
                {
                    return;
                }

                if (node is BaseFieldDeclarationSyntax)
                {
                    foreach (var variableDeclarator in ((BaseFieldDeclarationSyntax)node).Declaration.Variables)
                    {
                        eventQueue.EnqueueRemoveEvent(variableDeclarator, parent);
                    }
                }
                else if (node is AttributeListSyntax)
                {
                    foreach (var attribute in ((AttributeListSyntax)node).Attributes)
                    {
                        AddEventToEventQueueForAttributes(attribute, parent, eventQueue.EnqueueRemoveEvent);
                    }
                }
                else if (node is AttributeSyntax)
                {
                    AddEventToEventQueueForAttributes((AttributeSyntax)node, parent, eventQueue.EnqueueRemoveEvent);
                }
                else
                {
                    eventQueue.EnqueueRemoveEvent(node, parent);
                }
            }
Exemple #22
0
            private void FindRemovedChild <TNode, TParent>(
                NodeComparison <TNode, TParent> compare,
                IReadOnlyList <TNode> oldChildren,
                IReadOnlyList <TNode> newChildren,
                TParent newNodeParent,
                int delta,
                CodeModelEventQueue eventQueue
                )
                where TNode : SyntaxNode
                where TParent : SyntaxNode
            {
                Debug.Assert(oldChildren.Count - delta == newChildren.Count);

                // The strategy is to assume that all of the removed children are contiguous.
                // If that turns out not to be the case, an unknown change event is raised
                // for the containing node.

                var firstRemoved = -1;

                // Look for the first different child. If there is one, track that index as
                // the first added node.
                int oldIndex,
                    newIndex;

                for (
                    oldIndex = 0, newIndex = 0;
                    oldIndex < oldChildren.Count;
                    oldIndex++, newIndex++
                    )
                {
                    if (
                        newIndex >= newChildren.Count ||
                        !compare(
                            oldChildren[oldIndex],
                            newChildren[newIndex],
                            newNodeParent,
                            null
                            )
                        )
                    {
                        firstRemoved = oldIndex;
                        oldIndex    += delta;
                        break;
                    }
                }

                // Look for a second different child. If there is one, we'll throw away any events from
                // the first different child and enqueue an unknown event on the containing node.
                for (; oldIndex < oldChildren.Count; oldIndex++, newIndex++)
                {
                    if (!compare(oldChildren[oldIndex], newChildren[newIndex], newNodeParent, null))
                    {
                        EnqueueChangeEvent(
                            newNodeParent,
                            null,
                            CodeModelEventType.Unknown,
                            eventQueue
                            );
                        return;
                    }
                }

                if (firstRemoved >= 0)
                {
                    for (var i = 0; i < delta; i++)
                    {
                        EnqueueRemoveEvent(
                            oldChildren[firstRemoved + i],
                            newNodeParent,
                            eventQueue
                            );
                    }
                }
            }