diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.DiffEdit.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.DiffEdit.cs index 68fda66b04f..f2a9b22be94 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.DiffEdit.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.DiffEdit.cs @@ -14,7 +14,7 @@ protected readonly struct DiffEdit public int? NewTextPosition { get; } public int Length { get; } - private DiffEdit(DiffEditKind kind, int position, int? newTextPosition, int length) + public DiffEdit(DiffEditKind kind, int position, int? newTextPosition, int length) { Kind = kind; Position = position; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.IntArray.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.IntArray.cs index bc08daabb99..1f820b06faf 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.IntArray.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.IntArray.cs @@ -28,7 +28,7 @@ public void Deconstruct(out int[] array, out int start, out int length) => (array, start, length) = (Array, Start, Length); } - private const int PageSize = 1024 * 80 / sizeof(int); + private const int PageSize = 1024 * 64 / sizeof(int); private Page _page; private readonly Page[] _pages; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.cs index f5094d95c43..0af958a41c7 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.cs @@ -18,6 +18,9 @@ internal abstract partial class TextDiffer protected abstract int OldSourceLength { get; } protected abstract int NewSourceLength { get; } + private int _oldSourceOffset; + private int _newSourceOffset; + protected abstract bool SourceEqual(int oldSourceIndex, int newSourceIndex); protected List ComputeDiff() @@ -25,31 +28,43 @@ protected List ComputeDiff() var edits = new List(capacity: 4); var builder = new DiffEditBuilder(edits); + var lowA = 0; + var highA = OldSourceLength; + var lowB = 0; + var highB = NewSourceLength; + + // Determine the extent of the changes in both texts, as this will allow us + // to limit the amount of memory needed in the vf/vr arrays. By doing this though, + // we will need to adjust SourceEqual requests to use an appropriate offset. + FindChangeExtent(ref lowA, ref highA, ref lowB, ref highB); + + _oldSourceOffset = lowA; + _newSourceOffset = lowB; + + var oldSourceLength = highA - lowA; + var newSourceLength = highB - lowB; + // Initialize the vectors to use for forward and reverse searches. - var max = NewSourceLength + OldSourceLength; + var max = newSourceLength + oldSourceLength; using var vf = new IntArray((2 * max) + 1); using var vr = new IntArray((2 * max) + 1); - ComputeDiffRecursive(builder, 0, OldSourceLength, 0, NewSourceLength, vf, vr); + ComputeDiffRecursive(builder, 0, oldSourceLength, 0, newSourceLength, vf, vr); + + // Update the resultant edits with the appropriate offsets + for (var i = 0; i < edits.Count; i++) + { + var edit = edits[i]; + + edits[i] = new DiffEdit(edit.Kind, _oldSourceOffset + edit.Position, _newSourceOffset + edit.NewTextPosition, edit.Length); + } return edits; } private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, int lowB, int highB, IntArray vf, IntArray vr) { - while (lowA < highA && lowB < highB && SourceEqual(lowA, lowB)) - { - // Skip equal text at the start. - lowA++; - lowB++; - } - - while (lowA < highA && lowB < highB && SourceEqual(highA - 1, highB - 1)) - { - // Skip equal text at the end. - highA--; - highB--; - } + FindChangeExtent(ref lowA, ref highA, ref lowB, ref highB); if (lowA == highA) { @@ -82,6 +97,23 @@ private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, in } } + private void FindChangeExtent(ref int lowA, ref int highA, ref int lowB, ref int highB) + { + while (lowA < highA && lowB < highB && SourceEqualUsingOffset(lowA, lowB)) + { + // Skip equal text at the start. + lowA++; + lowB++; + } + + while (lowA < highA && lowB < highB && SourceEqualUsingOffset(highA - 1, highB - 1)) + { + // Skip equal text at the end. + highA--; + highB--; + } + } + private (int, int) FindMiddleSnake(int lowA, int highA, int lowB, int highB, IntArray vf, IntArray vr) { var n = highA - lowA; @@ -126,7 +158,7 @@ private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, in var y = x - k; // Traverse diagonal if possible. - while (x < highA && y < highB && SourceEqual(x, y)) + while (x < highA && y < highB && SourceEqualUsingOffset(x, y)) { x++; y++; @@ -169,7 +201,7 @@ private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, in var y = x - k; // Traverse diagonal if possible. - while (x > lowA && y > lowB && SourceEqual(x - 1, y - 1)) + while (x > lowA && y > lowB && SourceEqualUsingOffset(x - 1, y - 1)) { x--; y--; @@ -195,4 +227,7 @@ private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, in throw Assumes.NotReachable(); } + + private bool SourceEqualUsingOffset(int oldSourceIndex, int newSourceIndex) + => SourceEqual(oldSourceIndex + _oldSourceOffset, newSourceIndex + _newSourceOffset); }