| File: LinkedFileDiffMerging\AbstractLinkedFileMergeConflictCommentAdditionService.cs | Web Access |
| Project: src\roslyn\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; internal static class LinkedFileMergeConflictCommentAdditionService { public static ImmutableArray<TextChange> CreateEdits(SourceText originalSourceText, ArrayBuilder<UnmergedDocumentChanges> unmergedChanges) { using var _ = ArrayBuilder<TextChange>.GetInstance(out var commentChanges); foreach (var documentWithChanges in unmergedChanges) { var partitionedChanges = PartitionChangesForDocument(documentWithChanges.UnmergedChanges, originalSourceText); var comments = GetCommentChangesForDocument(partitionedChanges, documentWithChanges.ProjectName, originalSourceText); commentChanges.AddRange(comments); } return commentChanges.ToImmutableAndClear(); } private static List<List<TextChange>> PartitionChangesForDocument(IEnumerable<TextChange> changes, SourceText originalSourceText) { var partitionedChanges = new List<List<TextChange>>(); var currentPartition = new List<TextChange> { changes.First() }; var currentPartitionEndLine = originalSourceText.Lines.GetLineFromPosition(changes.First().Span.End); foreach (var change in changes.Skip(1)) { // If changes are on adjacent lines, consider them part of the same change. var changeStartLine = originalSourceText.Lines.GetLineFromPosition(change.Span.Start); if (changeStartLine.LineNumber >= currentPartitionEndLine.LineNumber + 2) { partitionedChanges.Add(currentPartition); currentPartition = []; } currentPartition.Add(change); currentPartitionEndLine = originalSourceText.Lines.GetLineFromPosition(change.Span.End); } if (currentPartition.Any()) { partitionedChanges.Add(currentPartition); } return partitionedChanges; } private static ImmutableArray<TextChange> GetCommentChangesForDocument(IEnumerable<IEnumerable<TextChange>> partitionedChanges, string projectName, SourceText oldDocumentText) { using var _ = ArrayBuilder<TextChange>.GetInstance(out var commentChanges); foreach (var changePartition in partitionedChanges) { var startPosition = changePartition.First().Span.Start; var endPosition = changePartition.Last().Span.End; var startLineStartPosition = oldDocumentText.Lines.GetLineFromPosition(startPosition).Start; var endLineEndPosition = oldDocumentText.Lines.GetLineFromPosition(endPosition).End; var oldText = oldDocumentText.GetSubText(TextSpan.FromBounds(startLineStartPosition, endLineEndPosition)); var adjustedChanges = changePartition.Select(c => new TextChange(TextSpan.FromBounds(c.Span.Start - startLineStartPosition, c.Span.End - startLineStartPosition), c.NewText!)); var newText = oldText.WithChanges(adjustedChanges); var warningText = GetConflictCommentText( string.Format(WorkspacesResources.TODO_Unmerged_change_from_project_0, projectName), TrimBlankLines(oldText), TrimBlankLines(newText)); if (warningText != null) commentChanges.Add(new TextChange(TextSpan.FromBounds(startLineStartPosition, startLineStartPosition), warningText)); } return commentChanges.ToImmutableAndClear(); } private static string? GetConflictCommentText(string header, string? beforeString, string? afterString) => (beforeString, afterString) switch { (null, null) => null, (not null, not null) => $""" <<<<<<< {header}, {WorkspacesResources.Before_colon} {beforeString} ======= {afterString} >>>>>>> {WorkspacesResources.After} """, (null, not null) => $""" <<<<<<< {header}, {WorkspacesResources.Before_colon} ======= {afterString} >>>>>>> {WorkspacesResources.After} """, (not null, null) => $""" <<<<<<< {header}, {WorkspacesResources.Before_colon} {beforeString} ======= >>>>>>> {WorkspacesResources.After} """, }; private static string? TrimBlankLines(SourceText text) { int startLine, endLine; for (startLine = 0; startLine < text.Lines.Count; startLine++) { if (!text.Lines[startLine].IsEmptyOrWhitespace()) { break; } } for (endLine = text.Lines.Count - 1; endLine > startLine; endLine--) { if (!text.Lines[endLine].IsEmptyOrWhitespace()) { break; } } return startLine <= endLine ? text.GetSubText(TextSpan.FromBounds(text.Lines[startLine].Start, text.Lines[endLine].End)).ToString() : null; } }