/// <summary> /// Constructs a Text Change object. /// </summary> /// <param name="oldPosition"> /// The character position in the TextBuffer at which the text change happened. /// </param> /// <param name="oldText"> /// The text in the buffer that was replaced. /// </param> /// <param name="newText"> /// The text that replaces the old text. /// </param> /// <param name="boundaryConditions"> /// Information about neighboring line break characters. /// </param> public TextChange(int oldPosition, ChangeString oldText, ChangeString newText, LineBreakBoundaryConditions boundaryConditions) { if (oldPosition < 0) { throw new ArgumentOutOfRangeException("oldPosition"); } _oldPosition = oldPosition; _newPosition = oldPosition; _oldText = oldText; _newText = newText; _lineBreakBoundaryConditions = boundaryConditions; }
private static IList <TextChange> GetChangesFromDifferenceCollection(ref int delta, TextChange originalChange, ChangeString oldText, ChangeString newText, IHierarchicalDifferenceCollection diffCollection, int leftOffset = 0, int rightOffset = 0) { List <TextChange> changes = new List <TextChange>(); for (int i = 0; i < diffCollection.Differences.Count; i++) { Difference currentDiff = diffCollection.Differences[i]; Span leftDiffSpan = Translate(diffCollection.LeftDecomposition.GetSpanInOriginal(currentDiff.Left), leftOffset); Span rightDiffSpan = Translate(diffCollection.RightDecomposition.GetSpanInOriginal(currentDiff.Right), rightOffset); // TODO: Since this evaluates differences lazily, we should add something here to *not* compute the next // level of differences if we think it would be too expensive. IHierarchicalDifferenceCollection nextLevelDiffs = diffCollection.GetContainedDifferences(i); if (nextLevelDiffs != null) { changes.AddRange(GetChangesFromDifferenceCollection(ref delta, originalChange, oldText, newText, nextLevelDiffs, leftDiffSpan.Start, rightDiffSpan.Start)); } else { TextChange minimalChange = new TextChange(originalChange.OldPosition + leftDiffSpan.Start, oldText.Substring(leftDiffSpan), newText.Substring(rightDiffSpan), ComputeBoundaryConditions(originalChange, oldText, leftDiffSpan)); minimalChange.NewPosition = originalChange.NewPosition + rightDiffSpan.Start; if (minimalChange.OldLength > 0 && minimalChange.NewLength > 0) { minimalChange.IsOpaque = true; } delta += minimalChange.Delta; changes.Add(minimalChange); } } return(changes); }
private static LineBreakBoundaryConditions ComputeBoundaryConditions(TextChange outerChange, ChangeString oldText, Span leftSpan) { LineBreakBoundaryConditions bc = LineBreakBoundaryConditions.None; if (leftSpan.Start == 0) { bc = (outerChange.LineBreakBoundaryConditions & LineBreakBoundaryConditions.PrecedingReturn); } else if (oldText[leftSpan.Start - 1] == '\r') { bc = LineBreakBoundaryConditions.PrecedingReturn; } if (leftSpan.End == oldText.Length) { bc |= (outerChange.LineBreakBoundaryConditions & LineBreakBoundaryConditions.SucceedingNewline); } else if (oldText[leftSpan.End] == '\n') { bc |= LineBreakBoundaryConditions.SucceedingNewline; } return(bc); }
/// <summary> /// Compute the impact of a change that substitutes <paramref name="newText"/> for <paramref name="oldText"/> in the /// context described by the <paramref name="boundaryConditions"/>. /// </summary> /// <param name="boundaryConditions">Immediate surroundings of the change with respect to compound line breaks.</param> /// <param name="oldText">The replaced text.</param> /// <param name="newText">The newly inserted text.</param> /// <returns></returns> static public int ComputeLineCountDelta(LineBreakBoundaryConditions boundaryConditions, ChangeString oldText, ChangeString newText) { int delta = 0; delta -= oldText.ComputeLineBreakCount(); delta += newText.ComputeLineBreakCount(); if ((boundaryConditions & LineBreakBoundaryConditions.PrecedingReturn) != 0) { if (oldText.Length > 0 && oldText[0] == '\n') { delta++; } if (newText.Length > 0 && newText[0] == '\n') { delta--; } } if ((boundaryConditions & LineBreakBoundaryConditions.SucceedingNewline) != 0) { if (oldText.Length > 0 && oldText[oldText.Length - 1] == '\r') { delta++; } if (newText.Length > 0 && newText[newText.Length - 1] == '\r') { delta--; } } if ((oldText.Length == 0) && ((boundaryConditions & LineBreakBoundaryConditions.PrecedingReturn) != 0) && ((boundaryConditions & LineBreakBoundaryConditions.SucceedingNewline) != 0)) { // return and newline were adjacent before and were separated by the insertion delta++; } if ((newText.Length == 0) && ((boundaryConditions & LineBreakBoundaryConditions.PrecedingReturn) != 0) && ((boundaryConditions & LineBreakBoundaryConditions.SucceedingNewline) != 0)) { // return and newline were separated before and were made adjacent by the deletion delta--; } return(delta); }
/// <summary> /// Constructs a Text Change object. /// </summary> /// <param name="oldPosition"> /// The character position in the TextBuffer at which the text change happened. /// </param> /// <param name="oldText"> /// The text in the buffer that was replaced. /// </param> /// <param name="newText"> /// The text that replaces the old text. /// </param> /// <param name="currentSnapshot"> /// Context in which change occurs. /// </param> public TextChange(int oldPosition, ChangeString oldText, ChangeString newText, ITextSnapshot currentSnapshot) : this(oldPosition, oldText, newText, ComputeLineBreakBoundaryConditions(currentSnapshot, oldPosition, oldText.Length)) { }