/// <summary> /// Draw margins for each diff line. /// </summary> /// <param name="diffLines">Differences between the current document and the version in TFS.</param> private void DrawMargins(DiffLinesCollection diffLines) { if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(() => DrawMargins(diffLines)); return; } Children.Clear(); foreach (DiffLineEntry diffLine in diffLines) { double top, bottom; try { MapLineToPixels(diffLine.Line, out top, out bottom); } catch (ArgumentException) { // The supplied line is on an incorrect snapshot (old version). return; } var rect = new Rectangle { Height = bottom - top, Width = Width - MarginElementOffset, Focusable = false, IsHitTestVisible = false }; SetLeft(rect, MarginElementOffset); SetTop(rect, top); switch (diffLine.ChangeType) { case DiffChangeType.Insert: rect.Fill = _marginCore.MarginSettings.AddedLineMarginBrush; break; case DiffChangeType.Change: rect.Fill = _marginCore.MarginSettings.ModifiedLineMarginBrush; break; case DiffChangeType.Delete: rect.Fill = _marginCore.MarginSettings.RemovedLineMarginBrush; break; default: throw new ArgumentOutOfRangeException(); } Children.Add(rect); } }
/// <summary> /// Adds a line of text from an <see cref="ITextSnapshot"/> to <see cref="DiffLinesCollection"/>. /// </summary> /// <param name="collection">Collection of differences between the current document and the version in TFS.</param> /// <param name="textSnapshot">The text snapshot.</param> /// <param name="lineNumber">The line number.</param> /// <param name="diffChangeInfo">Represents information about a specific difference between two sequences.</param> private static void AddLineToDiffLinesCollection(DiffLinesCollection collection, ITextSnapshot textSnapshot, int lineNumber, IDiffChange diffChangeInfo) { ITextSnapshotLine line; try { line = textSnapshot.GetLineFromLineNumber(lineNumber); } catch (ArgumentOutOfRangeException ex) { string msg = string.Format("Line number {0} is out of range [0..{1}].", lineNumber, textSnapshot.LineCount); throw new ArgumentOutOfRangeException(msg, ex); } collection.Add(line, diffChangeInfo); }
/// <summary> /// Update difference between lines asynchronously, if needed, and redraw the margin. /// </summary> /// <param name="useCache">Use cached differences.</param> /// <param name="reason">The reason of redrawing.</param> private void Redraw(bool useCache, MarginDrawReason reason) { try { if (_isDisposed || _textView.IsClosed) { return; } if (!_isActivated) { _cachedChangedLines.Clear(); RaiseMarginRedraw(reason); return; } if (useCache) { RaiseMarginRedraw(reason); return; } var task = new Task(() => { lock (_drawLockObject) { try { _cachedChangedLines = GetChangedLineNumbers(); } catch (Exception ex) { RaiseExceptionThrown(ex); return; } Redraw(true, reason); } }); task.Start(); } catch (Exception ex) { RaiseExceptionThrown(ex); } }
/// <summary> /// Get differences between the document's local file and his source control version. /// </summary> /// <returns> /// Collection that contains the result of comparing the document's local file with his source control version. /// <para/>Each element is a pair of key and value: the key is a line of text, the value is a type of difference. /// </returns> private DiffLinesCollection GetChangedLineNumbers() { Debug.Assert(_textDoc != null, "_textDoc is null."); Debug.Assert(_versionControl != null, "_versionControl is null."); var textSnapshot = _textView.TextSnapshot; Stream sourceStream = new MemoryStream(); Encoding sourceStreamEncoding = _textDoc.Encoding; byte[] textBytes = sourceStreamEncoding.GetBytes(textSnapshot.GetText()); sourceStream.Write(textBytes, 0, textBytes.Length); DiffSummary diffSummary; lock (_versionControlItemStreamLockObject) { diffSummary = GetDifference( _versionControlItemStream, Encoding.GetEncoding(_versionControlItem.Encoding), sourceStream, sourceStreamEncoding); } var dict = new DiffLinesCollection(); for (int i = 0; i < diffSummary.Changes.Length; i++) { var diff = new DiffChange(diffSummary.Changes[i]); if (diff.ChangeType == DiffChangeType.Change) { if (diff.OriginalLength >= diff.ModifiedLength) { int linesModified = diff.ModifiedLength; if (linesModified == 0) { diff.ChangeType = DiffChangeType.Delete; int linesDeleted = diff.OriginalLength - diff.ModifiedLength; Debug.Assert(linesDeleted > 0, "linesDeleted must be greater than zero."); } } else { int linesModified = diff.OriginalLength; if (linesModified == 0) { diff.ChangeType = DiffChangeType.Insert; int linesAdded = diff.ModifiedLength - diff.OriginalLength; Debug.Assert(linesAdded > 0, "linesAdded must be greater than zero."); } } } if (diff.ChangeType != DiffChangeType.Delete) { for (int k = diff.ModifiedStart; k <= diff.ModifiedEnd; k++) { AddLineToDiffLinesCollection(dict, textSnapshot, k, diff); } } else { AddLineToDiffLinesCollection(dict, textSnapshot, diff.ModifiedStart, diff); } } return(dict); }
/// <summary> /// Draw margins for each diff line. /// </summary> /// <param name="diffLines">Differences between the current document and the version in TFS.</param> private void DrawMargins(DiffLinesCollection diffLines) { if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(() => DrawMargins(diffLines)); return; } Children.Clear(); foreach (ITextViewLine viewLine in _textView.TextViewLines) { DiffChangeType diffType; ITextSnapshotLine line; try { if (ContainsChanges(viewLine, diffLines[DiffChangeType.Change], out line)) { diffType = DiffChangeType.Change; } else if (ContainsChanges(viewLine, diffLines[DiffChangeType.Insert], out line)) { diffType = DiffChangeType.Insert; ITextSnapshotLine tmp; if (ContainsChanges(viewLine, diffLines[DiffChangeType.Delete], out tmp)) { diffType = DiffChangeType.Change; line = tmp; } } else if (ContainsChanges(viewLine, diffLines[DiffChangeType.Delete], out line)) { diffType = DiffChangeType.Delete; } else { continue; } } catch (ArgumentException) { // The supplied line is on an incorrect snapshot (old version). return; } IDiffChange diffChangeInfo = diffLines[line]; var rect = new Rectangle { Height = viewLine.Height, Width = MarginElementWidth, Cursor = Cursors.Hand, ContextMenu = _contextMenu, Tag = new { DiffChangeInfo = diffChangeInfo, Line = line } }; SetLeft(rect, MarginElementLeft); SetTop(rect, viewLine.Top - _textView.ViewportTop); switch (diffType) { case DiffChangeType.Insert: rect.Fill = _marginCore.MarginSettings.AddedLineMarginBrush; break; case DiffChangeType.Change: rect.Fill = _marginCore.MarginSettings.ModifiedLineMarginBrush; break; case DiffChangeType.Delete: rect.Fill = _marginCore.MarginSettings.RemovedLineMarginBrush; break; default: throw new ArgumentOutOfRangeException(); } if (diffType != DiffChangeType.Insert) { rect.MouseEnter += OnMarginElementMouseEnter; } rect.MouseLeftButtonDown += OnMarginElementMouseLeftButtonDown; rect.MouseLeftButtonUp += OnMarginElementMouseLeftButtonUp; Children.Add(rect); } }
/// <summary> /// Initialize a new instance of the <see cref="MarginRedrawEventArgs"/> class. /// </summary> /// <param name="diffLines">Differences between the current document and the version in TFS.</param> /// <param name="reason">The reason of redrawing the margin.</param> public MarginRedrawEventArgs(DiffLinesCollection diffLines, MarginDrawReason reason) { _diffLines = diffLines; _reason = reason; }