/// <summary>
		/// Update the workspace so that the document with the Id of <paramref name="newDocument"/>
		/// has the text of newDocument.  If the document is open, then this method will determine a
		/// minimal set of changes to apply to the document.
		/// </summary>
		internal static void ApplyDocumentChanges (this Workspace workspace, Document newDocument, CancellationToken cancellationToken)
		{
			var oldSolution = workspace.CurrentSolution;
			var oldDocument = oldSolution.GetDocument (newDocument.Id);
			var changes = newDocument.GetTextChangesAsync (oldDocument, cancellationToken).WaitAndGetResult (cancellationToken);
			var newSolution = oldSolution.UpdateDocument (newDocument.Id, changes, cancellationToken);
			workspace.TryApplyChanges (newSolution);
		}
Esempio n. 2
0
        public async Task <ImmutableArray <TextChange> > GetTextChangesAsync(
            Document oldDocument,
            Document newDocument,
            TextDifferenceTypes preferredDifferenceType,
            CancellationToken cancellationToken
            )
        {
            var changes = await newDocument
                          .GetTextChangesAsync(oldDocument, cancellationToken)
                          .ConfigureAwait(false);

            return(changes.ToImmutableArray());
        }
            private static async Task<List<TextChange>> AddDocumentMergeChangesAsync(
                Document oldDocument,
                Document newDocument,
                List<TextChange> cumulativeChanges,
                List<UnmergedDocumentChanges> unmergedChanges,
                LinkedFileGroupSessionInfo groupSessionInfo,
                CancellationToken cancellationToken)
            {
                var unmergedDocumentChanges = new List<TextChange>();
                var successfullyMergedChanges = new List<TextChange>();

                int cumulativeChangeIndex = 0;
                foreach (var change in await newDocument.GetTextChangesAsync(oldDocument).ConfigureAwait(false))
                {
                    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(),
                        await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false),
                        oldDocument.Project.Name));
                }

                return successfullyMergedChanges;
            }
        /// <summary>
        /// Try to merge the changes between <paramref name="newDocument"/> and <paramref name="oldDocument"/> into <paramref name="cumulativeChanges"/>.
        /// If there is any conflicting change in <paramref name="newDocument"/> with existing <paramref name="cumulativeChanges"/>, then the original <paramref name="cumulativeChanges"/> are returned.
        /// Otherwise, the newly merged changes are returned.
        /// </summary>
        /// <param name="oldDocument">Base document on which FixAll was invoked.</param>
        /// <param name="newDocument">New document with a code fix that is being merged.</param>
        /// <param name="cumulativeChanges">Existing merged changes from other batch fixes into which newDocument changes are being merged.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private static async Task<List<TextChange>> TryAddDocumentMergeChangesAsync(
            Document oldDocument,
            Document newDocument,
            List<TextChange> cumulativeChanges,
            CancellationToken cancellationToken)
        {
            var successfullyMergedChanges = new List<TextChange>();

            int cumulativeChangeIndex = 0;
            foreach (var change in await newDocument.GetTextChangesAsync(oldDocument, cancellationToken).ConfigureAwait(false))
            {
                cancellationToken.ThrowIfCancellationRequested();
                while (cumulativeChangeIndex < cumulativeChanges.Count && cumulativeChanges[cumulativeChangeIndex].Span.End < change.Span.Start)
                {
                    cancellationToken.ThrowIfCancellationRequested();

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

                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);
                    }
                    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.
                            // Bail out merge efforts and return the original 'cumulativeChanges'.
                            return cumulativeChanges;
                        }
                        else
                        {
                            // The current change in consideration is identical to an existing change
                            successfullyMergedChanges.Add(change);
                            cumulativeChangeIndex++;
                        }
                    }
                }
                else
                {
                    // The current change in consideration does not intersect with any existing change
                    successfullyMergedChanges.Add(change);
                }
            }

            while (cumulativeChangeIndex < cumulativeChanges.Count)
            {
                cancellationToken.ThrowIfCancellationRequested();

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

            return successfullyMergedChanges;
        }
 public async Task<IEnumerable<TextChange>> GetTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken)
 {
     return await newDocument.GetTextChangesAsync(oldDocument, cancellationToken).ConfigureAwait(false);
 }
Esempio n. 6
0
 public async Task <IEnumerable <TextChange> > GetTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken)
 {
     return(await newDocument.GetTextChangesAsync(oldDocument, cancellationToken).ConfigureAwait(false));
 }
Esempio n. 7
0
            private static async Task <List <TextChange> > AddDocumentMergeChangesAsync(
                Document oldDocument,
                Document newDocument,
                List <TextChange> cumulativeChanges,
                List <UnmergedDocumentChanges> unmergedChanges,
                LinkedFileGroupSessionInfo groupSessionInfo,
                CancellationToken cancellationToken)
            {
                var unmergedDocumentChanges   = new List <TextChange>();
                var successfullyMergedChanges = new List <TextChange>();

                int cumulativeChangeIndex = 0;

                foreach (var change in await newDocument.GetTextChangesAsync(oldDocument).ConfigureAwait(false))
                {
                    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(),
                                            await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false),
                                            oldDocument.Project.Name));
                }

                return(successfullyMergedChanges);
            }
Esempio n. 8
0
        private static async Task <O.ChangedLineMap[]> CreateChangedLineMaps(R.Document oldDocument, R.Document newDocument)
        {
            var changes = await newDocument.GetTextChangesAsync(oldDocument).ConfigureAwait(false);

            var oldText = await oldDocument.GetTextAsync().ConfigureAwait(false);

            var lineChanges = new List <LineChange>();

            foreach (var change in changes)
            {
                var startLine           = oldText.Lines.IndexOf(change.Span.Start);
                var oldLineCount        = oldText.ToString(change.Span).Count(c => c == '\n') + 1;
                var endLine             = startLine + oldLineCount;
                var additionalLineCount = change.NewText.Count(c => c == '\n') + 1 - oldLineCount;

                // 近いものがあるならマージしていく
                var isMerged = false;
                for (var i = 0; i < lineChanges.Count; i++)
                {
                    var lc = lineChanges[i];

                    // change
                    // lc
                    // みたいな配置になっている場合
                    var b1 = startLine <= lc.StartLine && startLine + oldLineCount >= lc.StartLine;

                    // lc
                    // change
                    var b2 = lc.StartLine <= startLine && lc.StartLine + lc.OldLineCount >= startLine;

                    isMerged = b1 || b2;
                    if (isMerged)
                    {
                        var newStartLine = Math.Min(startLine, lc.StartLine);
                        lineChanges[i] = new LineChange(
                            newStartLine,
                            Math.Max(endLine, lc.StartLine + lc.OldLineCount) - newStartLine,
                            lc.AdditionalLineCount + additionalLineCount
                            );

                        break;
                    }
                }

                if (!isMerged)
                {
                    lineChanges.Add(new LineChange(startLine, oldLineCount, additionalLineCount));
                }
            }

            // StartLine 順に並べて、変更後の行数を反映しながら結果を詰めていく
            lineChanges.Sort((x, y) => x.StartLine.CompareTo(y.StartLine));

            var changedLineMaps = new O.ChangedLineMap[lineChanges.Count];
            var additional      = 0;

            for (var i = 0; i < changedLineMaps.Length; i++)
            {
                var lc = lineChanges[i];
                changedLineMaps[i] = new O.ChangedLineMap(
                    new O.LineRange(lc.StartLine, lc.OldLineCount),
                    new O.LineRange(lc.StartLine + additional, lc.OldLineCount + lc.AdditionalLineCount)
                    );

                additional += lc.AdditionalLineCount;
            }

            return(changedLineMaps);
        }