private IEnumerable <TextChange> MergeChangesWithMergeFailComments(
            IEnumerable <TextChange> mergedChanges,
            IEnumerable <TextChange> commentChanges,
            IList <TextSpan> mergeConflictResolutionSpans,
            LinkedFileGroupSessionInfo groupSessionInfo)
        {
            var mergedChangesList  = NormalizeChanges(mergedChanges).ToList();
            var commentChangesList = NormalizeChanges(commentChanges).ToList();

            var combinedChanges = new List <TextChange>();
            var insertedMergeConflictCommentsAtAdjustedLocation = 0;

            var commentChangeIndex   = 0;
            var currentPositionDelta = 0;

            foreach (var mergedChange in mergedChangesList)
            {
                while (commentChangeIndex < commentChangesList.Count && commentChangesList[commentChangeIndex].Span.End <= mergedChange.Span.Start)
                {
                    // Add a comment change that does not conflict with any merge change
                    combinedChanges.Add(commentChangesList[commentChangeIndex]);
                    mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length));
                    currentPositionDelta += (commentChangesList[commentChangeIndex].NewText.Length - commentChangesList[commentChangeIndex].Span.Length);
                    commentChangeIndex++;
                }

                if (commentChangeIndex >= commentChangesList.Count || mergedChange.Span.End <= commentChangesList[commentChangeIndex].Span.Start)
                {
                    // Add a merge change that does not conflict with any comment change
                    combinedChanges.Add(mergedChange);
                    currentPositionDelta += (mergedChange.NewText.Length - mergedChange.Span.Length);
                    continue;
                }

                // The current comment insertion location conflicts with a merge diff location. Add the comment before the diff.
                var conflictingCommentInsertionLocation = new TextSpan(mergedChange.Span.Start, 0);
                while (commentChangeIndex < commentChangesList.Count && commentChangesList[commentChangeIndex].Span.Start < mergedChange.Span.End)
                {
                    combinedChanges.Add(new TextChange(conflictingCommentInsertionLocation, commentChangesList[commentChangeIndex].NewText));
                    mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length));
                    currentPositionDelta += commentChangesList[commentChangeIndex].NewText.Length;

                    commentChangeIndex++;
                    insertedMergeConflictCommentsAtAdjustedLocation++;
                }

                combinedChanges.Add(mergedChange);
                currentPositionDelta += (mergedChange.NewText.Length - mergedChange.Span.Length);
            }

            while (commentChangeIndex < commentChangesList.Count)
            {
                // Add a comment change that does not conflict with any merge change
                combinedChanges.Add(commentChangesList[commentChangeIndex]);
                mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length));

                currentPositionDelta += (commentChangesList[commentChangeIndex].NewText.Length - commentChangesList[commentChangeIndex].Span.Length);
                commentChangeIndex++;
            }

            groupSessionInfo.InsertedMergeConflictComments = commentChanges.Count();
            groupSessionInfo.InsertedMergeConflictCommentsAtAdjustedLocation = insertedMergeConflictCommentsAtAdjustedLocation;

            return(NormalizeChanges(combinedChanges));
        }
 public void LogLinkedFileResult(LinkedFileGroupSessionInfo info)
 {
     LinkedFileGroups.Add(info);
 }
        private static async Task <ImmutableArray <TextChange> > AddDocumentMergeChangesAsync(
            Document oldDocument,
            Document newDocument,
            List <TextChange> cumulativeChanges,
            List <UnmergedDocumentChanges> unmergedChanges,
            LinkedFileGroupSessionInfo groupSessionInfo,
            IDocumentTextDifferencingService textDiffService,
            CancellationToken cancellationToken)
        {
            var unmergedDocumentChanges   = new List <TextChange>();
            var successfullyMergedChanges = ArrayBuilder <TextChange> .GetInstance();

            int cumulativeChangeIndex = 0;

            var textchanges = await textDiffService.GetTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false);

            foreach (var change in textchanges)
            {
                while (cumulativeChangeIndex < cumulativeChanges.Count && cumulativeChanges[cumulativeChangeIndex].Span.End < change.Span.Start)
                {
                    // Existing change that does not overlap with the current change in consideration
                    successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]);
                    cumulativeChangeIndex++;

                    groupSessionInfo.IsolatedDiffs++;
                }

                if (cumulativeChangeIndex < cumulativeChanges.Count)
                {
                    var cumulativeChange = cumulativeChanges[cumulativeChangeIndex];
                    if (!cumulativeChange.Span.IntersectsWith(change.Span))
                    {
                        // The current change in consideration does not intersect with any existing change
                        successfullyMergedChanges.Add(change);

                        groupSessionInfo.IsolatedDiffs++;
                    }
                    else
                    {
                        if (change.Span != cumulativeChange.Span || change.NewText != cumulativeChange.NewText)
                        {
                            // The current change in consideration overlaps an existing change but
                            // the changes are not identical.
                            unmergedDocumentChanges.Add(change);

                            groupSessionInfo.OverlappingDistinctDiffs++;
                            if (change.Span == cumulativeChange.Span)
                            {
                                groupSessionInfo.OverlappingDistinctDiffsWithSameSpan++;
                                if (change.NewText.Contains(cumulativeChange.NewText) || cumulativeChange.NewText.Contains(change.NewText))
                                {
                                    groupSessionInfo.OverlappingDistinctDiffsWithSameSpanAndSubstringRelation++;
                                }
                            }
                        }
                        else
                        {
                            // The current change in consideration is identical to an existing change
                            successfullyMergedChanges.Add(change);
                            cumulativeChangeIndex++;

                            groupSessionInfo.IdenticalDiffs++;
                        }
                    }
                }
                else
                {
                    // The current change in consideration does not intersect with any existing change
                    successfullyMergedChanges.Add(change);

                    groupSessionInfo.IsolatedDiffs++;
                }
            }

            while (cumulativeChangeIndex < cumulativeChanges.Count)
            {
                // Existing change that does not overlap with the current change in consideration
                successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]);
                cumulativeChangeIndex++;
                groupSessionInfo.IsolatedDiffs++;
            }

            if (unmergedDocumentChanges.Any())
            {
                unmergedChanges.Add(new UnmergedDocumentChanges(
                                        unmergedDocumentChanges.AsEnumerable(),
                                        oldDocument.Project.Name,
                                        oldDocument.Id));
            }

            return(successfullyMergedChanges.ToImmutableAndFree());
        }