Browse Source

improved diff tooltip

pull/15/head
Siegfried Pammer 15 years ago
parent
commit
4b7f38fe9b
  1. 5
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
  2. 138
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs
  3. 65
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs
  4. 11
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DiffControl.xaml
  5. 30
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DiffControl.xaml.cs
  6. 53
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/LineChangeInfo.cs
  7. 18
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs
  8. 11
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

5
src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj

@ -95,6 +95,10 @@ @@ -95,6 +95,10 @@
<Compile Include="Src\CodeEditorView.cs" />
<Compile Include="Src\ContextActionsRenderer.cs" />
<Compile Include="Src\DefaultChangeWatcher.cs" />
<Compile Include="Src\DiffControl.xaml.cs">
<DependentUpon>DiffControl.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Src\ExpressionHighlightRenderer.cs" />
<Compile Include="Src\CodeManipulation.cs" />
<Compile Include="Src\CaretHighlightAdorner.cs" />
@ -176,6 +180,7 @@ @@ -176,6 +180,7 @@
<EmbeddedResource Include="Resources\ReverseIncrementalSearchCursor.cur" />
</ItemGroup>
<ItemGroup>
<Page Include="Src\DiffControl.xaml" />
<Page Include="Src\HiddenDefinition\HiddenDefinitionControl.xaml" />
<Page Include="Src\SharpDevelopCompletionWindow.xaml">
<DependentUpon>SharpDevelopCompletionWindow.cs</DependentUpon>

138
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs

@ -3,14 +3,20 @@ @@ -3,14 +3,20 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.AddIn.Options;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Widgets;
namespace ICSharpCode.AvalonEdit.AddIn
{
@ -64,26 +70,26 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -64,26 +70,26 @@ namespace ICSharpCode.AvalonEdit.AddIn
throw new Exception("Invalid value for ChangeType");
}
if (!string.IsNullOrEmpty(info.DeletedLinesAfterThisLine)) {
Point pt1 = new Point(5, line.VisualTop + line.Height - textView.ScrollOffset.Y - 4);
Point pt2 = new Point(10, line.VisualTop + line.Height - textView.ScrollOffset.Y);
Point pt3 = new Point(5, line.VisualTop + line.Height - textView.ScrollOffset.Y + 4);
drawingContext.DrawGeometry(Brushes.Red, null, new PathGeometry(new List<PathFigure>() { CreateNAngle(pt1, pt2, pt3) }));
}
// special case for line 0
if (line.FirstDocumentLine.LineNumber == 1) {
info = changeWatcher.GetChange(0);
if (!string.IsNullOrEmpty(info.DeletedLinesAfterThisLine)) {
Point pt1 = new Point(5, line.VisualTop - textView.ScrollOffset.Y - 4);
Point pt2 = new Point(10, line.VisualTop - textView.ScrollOffset.Y);
Point pt3 = new Point(5, line.VisualTop - textView.ScrollOffset.Y + 4);
drawingContext.DrawGeometry(Brushes.Red, null, new PathGeometry(new List<PathFigure>() { CreateNAngle(pt1, pt2, pt3) }));
}
}
// if (!string.IsNullOrEmpty(info.DeletedLinesAfterThisLine)) {
// Point pt1 = new Point(5, line.VisualTop + line.Height - textView.ScrollOffset.Y - 4);
// Point pt2 = new Point(10, line.VisualTop + line.Height - textView.ScrollOffset.Y);
// Point pt3 = new Point(5, line.VisualTop + line.Height - textView.ScrollOffset.Y + 4);
//
// drawingContext.DrawGeometry(Brushes.Red, null, new PathGeometry(new List<PathFigure>() { CreateNAngle(pt1, pt2, pt3) }));
// }
//
// // special case for line 0
// if (line.FirstDocumentLine.LineNumber == 1) {
// info = changeWatcher.GetChange(0);
//
// if (!string.IsNullOrEmpty(info.DeletedLinesAfterThisLine)) {
// Point pt1 = new Point(5, line.VisualTop - textView.ScrollOffset.Y - 4);
// Point pt2 = new Point(10, line.VisualTop - textView.ScrollOffset.Y);
// Point pt3 = new Point(5, line.VisualTop - textView.ScrollOffset.Y + 4);
//
// drawingContext.DrawGeometry(Brushes.Red, null, new PathGeometry(new List<PathFigure>() { CreateNAngle(pt1, pt2, pt3) }));
// }
// }
}
}
}
@ -135,43 +141,80 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -135,43 +141,80 @@ namespace ICSharpCode.AvalonEdit.AddIn
#region Diffs tooltip
ToolTip tooltip = new ToolTip();
Popup tooltip = new Popup() { StaysOpen = false };
ITextMarker marker;
ITextMarkerService markerService;
protected override void OnMouseMove(MouseEventArgs e)
{
var diffs = changeWatcher.GetDiffsByLine(GetLineFromMousePosition(e));
int line = GetLineFromMousePosition(e);
if (diffs != null && diffs.Count > 0) {
StackPanel stack = new StackPanel() {
Background = Brushes.White
};
TextBlock oldTb = new TextBlock() {
FontFamily = new FontFamily(CodeEditorOptions.Instance.FontFamily),
FontSize = CodeEditorOptions.Instance.FontSize,
Foreground = Brushes.Black,
Background = Brushes.White,
TextDecorations = TextDecorations.Strikethrough,
};
if (line == 0)
return;
int startLine;
string oldText = changeWatcher.GetOldVersionFromLine(line, out startLine);
int offset, length;
TextEditor editor = this.TextView.Services.GetService(typeof(TextEditor)) as TextEditor;
markerService = this.TextView.Services.GetService(typeof(ITextMarkerService)) as ITextMarkerService;
if (changeWatcher.GetNewVersionFromLine(line, out offset, out length)) {
if (marker != null)
markerService.Remove(marker);
marker = markerService.Create(offset, length);
marker.BackgroundColor = Colors.LightGreen;
}
if (oldText != null) {
DiffControl differ = new DiffControl();
differ.editor.SyntaxHighlighting = editor.SyntaxHighlighting;
differ.editor.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
differ.editor.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
differ.editor.Document.Text = oldText;
differ.Background = Brushes.White;
DocumentHighlighter mainHighlighter = TextView.Services.GetService(typeof(IHighlighter)) as DocumentHighlighter;
DocumentHighlighter popupHighlighter = differ.editor.TextArea.GetService(typeof(IHighlighter)) as DocumentHighlighter;
if (diffs[0] != null)
oldTb.Text = diffs[0].Text.Trim();
// popupHighlighter.InitialSpanStack = mainHighlighter.GetSpanStack(
TextBlock newTb = new TextBlock() {
FontFamily = new FontFamily(CodeEditorOptions.Instance.FontFamily),
FontSize = CodeEditorOptions.Instance.FontSize,
Foreground = Brushes.Black,
Background = Brushes.White,
Text = diffs[1].Text.Trim()
if (oldText == string.Empty) {
differ.editor.Visibility = Visibility.Collapsed;
}
differ.undoButton.Click += delegate {
if (marker != null) {
int delimiter = 0;
if (oldText == string.Empty)
delimiter = Document.GetLineByOffset(offset + length).DelimiterLength;
Document.Replace(offset, length + delimiter, oldText);
tooltip.IsOpen = false;
}
};
stack.Children.Add(oldTb);
stack.Children.Add(newTb);
tooltip.Content = stack;
tooltip.Background = Brushes.White;
tooltip.Child = new Border() {
Child = differ,
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(1)
};
if (tooltip.IsOpen)
tooltip.IsOpen = false;
tooltip.IsOpen = true;
tooltip.Closed += delegate {
if (marker != null) markerService.Remove(marker);
};
tooltip.HorizontalOffset = -10;
tooltip.VerticalOffset =
TextView.GetVisualTopByDocumentLine(startLine) - TextView.ScrollOffset.Y;
tooltip.Placement = PlacementMode.Top;
tooltip.PlacementTarget = this.TextView;
}
base.OnMouseMove(e);
@ -179,7 +222,8 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -179,7 +222,8 @@ namespace ICSharpCode.AvalonEdit.AddIn
protected override void OnMouseLeave(MouseEventArgs e)
{
tooltip.IsOpen = false;
if (marker != null && !tooltip.IsOpen)
markerService.Remove(marker);
base.OnMouseLeave(e);
}

65
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs

@ -6,7 +6,7 @@ using System.Collections.Generic; @@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.AvalonEdit.AddIn.MyersDiff;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
@ -91,7 +91,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -91,7 +91,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
new DocumentSequence(document, hashes)
);
changeList.Add(new LineChangeInfo(ChangeType.None, ""));
changeList.Add(LineChangeInfo.Empty);
int lastEndLine = 0;
foreach (Edit edit in diff.GetEdits()) {
@ -100,17 +100,8 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -100,17 +100,8 @@ namespace ICSharpCode.AvalonEdit.AddIn
changeList.InsertRange(changeList.Count, beginLine - lastEndLine, LineChangeInfo.Empty);
if (edit.EditType == ChangeType.Deleted) {
LineChangeInfo change = changeList[beginLine];
for (int i = edit.BeginA; i < edit.EndA; i++) {
var line = baseDocument.GetLine(i + 1);
change.DeletedLinesAfterThisLine += line.Text;
}
changeList[beginLine] = change;
} else {
var change = new LineChangeInfo(edit.EditType, "");
if (edit.EditType != ChangeType.Deleted) {
var change = new LineChangeInfo(edit.EditType, edit.BeginA, edit.BeginB, edit.EndA, edit.EndB);
changeList.InsertRange(changeList.Count, endLine - beginLine, change);
}
@ -165,20 +156,16 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -165,20 +156,16 @@ namespace ICSharpCode.AvalonEdit.AddIn
void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine)
{
int index = insertionPos.LineNumber;
var firstLine = changeList[index];
var newLineInfo = new LineChangeInfo(ChangeType.Unsaved, firstLine.DeletedLinesAfterThisLine);
firstLine.Change = ChangeType.Unsaved;
firstLine.DeletedLinesAfterThisLine = "";
var newLineInfo = new LineChangeInfo(ChangeType.Unsaved, index, index, newLine.LineNumber, newLine.LineNumber);
changeList[index] = newLineInfo;
changeList.Insert(index + 1, newLineInfo);
changeList[index] = firstLine;
}
void ILineTracker.RebuildDocument()
{
changeList.Clear();
changeList.InsertRange(0, document.TotalNumberOfLines + 1, new LineChangeInfo(ChangeType.Unsaved, ""));
changeList.InsertRange(0, document.TotalNumberOfLines + 1, new LineChangeInfo(ChangeType.Unsaved, 1, 1, baseDocument.TotalNumberOfLines, document.TotalNumberOfLines));
}
bool disposed = false;
@ -192,18 +179,40 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -192,18 +179,40 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
}
public IList<IDocumentLine> GetDiffsByLine(int line)
public string GetOldVersionFromLine(int lineNumber, out int newStartLine)
{
var result = new List<IDocumentLine>();
LineChangeInfo info = changeList[lineNumber];
if (info.Change != ChangeType.None && info.Change != ChangeType.Unsaved) {
var startDocumentLine = baseDocument.GetLine(info.OldStartLineNumber + 1);
var endLine = baseDocument.GetLine(info.OldEndLineNumber);
newStartLine = info.NewStartLineNumber + 1;
if (info.Change == ChangeType.Added)
return "";
return baseDocument.GetText(startDocumentLine.Offset, endLine.EndOffset - startDocumentLine.Offset);
}
if (baseDocument.TotalNumberOfLines < line)
result.Add(null);
else
result.Add(baseDocument.GetLine(line));
newStartLine = 0;
return null;
}
public bool GetNewVersionFromLine(int lineNumber, out int offset, out int length)
{
LineChangeInfo info = changeList[lineNumber];
result.Add(document.GetLine(line));
if (info.Change != ChangeType.None) {
var startLine = document.GetLine(info.NewStartLineNumber + 1);
var endLine = document.GetLine(info.NewEndLineNumber);
offset = startLine.Offset;
length = endLine.EndOffset - startLine.Offset;
return true;
}
return result;
offset = length = 0;
return false;
}
}
}

11
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DiffControl.xaml

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
<UserControl x:Class="ICSharpCode.AvalonEdit.AddIn.DiffControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:ae="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical">
<ToolBar>
<Button x:Name="undoButton" Content="Undo" />
</ToolBar>
<ae:TextEditor x:Name="editor" />
</StackPanel>
</UserControl>

30
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DiffControl.xaml.cs

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.Core.Presentation;
namespace ICSharpCode.AvalonEdit.AddIn
{
/// <summary>
/// Interaction logic for DiffControl.xaml
/// </summary>
public partial class DiffControl : UserControl
{
public DiffControl()
{
InitializeComponent();
undoButton.Content = PresentationResourceService.GetImage("Icons.16x16.UndoIcon");
}
}
}

53
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/LineChangeInfo.cs

@ -17,7 +17,8 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -17,7 +17,8 @@ namespace ICSharpCode.AvalonEdit.AddIn
/// </summary>
LineChangeInfo GetChange(int lineNumber);
void Initialize(IDocument document);
IList<IDocumentLine> GetDiffsByLine(int line);
string GetOldVersionFromLine(int lineNumber, out int newStartLine);
bool GetNewVersionFromLine(int lineNumber, out int offset, out int length);
}
public enum ChangeType
@ -31,7 +32,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -31,7 +32,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
public struct LineChangeInfo : IEquatable<LineChangeInfo>
{
public static readonly LineChangeInfo Empty = new LineChangeInfo(ChangeType.None, "");
public static readonly LineChangeInfo Empty = new LineChangeInfo(ChangeType.None, -1, -1, -1, -1);
ChangeType change;
@ -40,17 +41,41 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -40,17 +41,41 @@ namespace ICSharpCode.AvalonEdit.AddIn
set { change = value; }
}
string deletedLinesAfterThisLine;
int oldStartLineNumber;
public string DeletedLinesAfterThisLine {
get { return deletedLinesAfterThisLine; }
set { deletedLinesAfterThisLine = value; }
public int OldStartLineNumber {
get { return oldStartLineNumber; }
set { oldStartLineNumber = value; }
}
public LineChangeInfo(ChangeType change, string deletedLinesAfterThisLine)
int newStartLineNumber;
public int NewStartLineNumber {
get { return newStartLineNumber; }
set { newStartLineNumber = value; }
}
int oldEndLineNumber;
public int OldEndLineNumber {
get { return oldEndLineNumber; }
set { oldEndLineNumber = value; }
}
int newEndLineNumber;
public int NewEndLineNumber {
get { return newEndLineNumber; }
set { newEndLineNumber = value; }
}
public LineChangeInfo(ChangeType change, int oldStartLineNumber, int newStartLineNumber, int oldEndLineNumber, int newEndLineNumber)
{
this.change = change;
this.deletedLinesAfterThisLine = deletedLinesAfterThisLine;
this.oldStartLineNumber = oldStartLineNumber;
this.newStartLineNumber = newStartLineNumber;
this.oldEndLineNumber = oldEndLineNumber;
this.newEndLineNumber = newEndLineNumber;
}
#region Equals and GetHashCode implementation
@ -61,7 +86,11 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -61,7 +86,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
public bool Equals(LineChangeInfo other)
{
return this.change == other.change && this.deletedLinesAfterThisLine == other.deletedLinesAfterThisLine;
return this.change == other.change &&
this.oldEndLineNumber == other.oldEndLineNumber &&
this.newStartLineNumber == other.newStartLineNumber &&
this.oldStartLineNumber == other.oldStartLineNumber &&
this.newEndLineNumber == other.newEndLineNumber;
}
public override int GetHashCode()
@ -69,8 +98,10 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -69,8 +98,10 @@ namespace ICSharpCode.AvalonEdit.AddIn
int hashCode = 0;
unchecked {
hashCode += 1000000007 * change.GetHashCode();
if (deletedLinesAfterThisLine != null)
hashCode += 1000000009 * deletedLinesAfterThisLine.GetHashCode();
hashCode += 1000000009 * oldStartLineNumber.GetHashCode();
hashCode += 1000000021 * newStartLineNumber.GetHashCode();
hashCode += 1000000033 * oldEndLineNumber.GetHashCode();
hashCode += 1000000087 * newEndLineNumber.GetHashCode();
}
return hashCode;
}

18
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs

@ -90,6 +90,22 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -90,6 +90,22 @@ namespace ICSharpCode.AvalonEdit.Highlighting
InvalidateHighlighting();
}
ImmutableStack<HighlightingSpan> initialSpanStack = SpanStack.Empty;
/// <summary>
/// Gets/sets the the initial span stack of the document. Default value is <see cref="SpanStack.Empty" />.
/// </summary>
public ImmutableStack<HighlightingSpan> InitialSpanStack {
get { return initialSpanStack; }
set {
if (value == null)
initialSpanStack = SpanStack.Empty;
else
initialSpanStack = value;
InvalidateHighlighting();
}
}
/// <summary>
/// Invalidates all stored highlighting info.
/// When the document changes, the highlighting is invalidated automatically, this method
@ -99,7 +115,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -99,7 +115,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
{
CheckIsHighlighting();
storedSpanStacks.Clear();
storedSpanStacks.Add(SpanStack.Empty);
storedSpanStacks.Add(initialSpanStack);
storedSpanStacks.InsertRange(1, document.LineCount, null);
isValid.Clear();
isValid.Add(true);

11
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

@ -1409,6 +1409,17 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -1409,6 +1409,17 @@ namespace ICSharpCode.AvalonEdit.Rendering
return null;
}
/// <summary>
/// Gets the visual top position (relative to start of document) from a document line number.
/// </summary>
public double GetVisualTopByDocumentLine(int line)
{
VerifyAccess();
if (heightTree == null)
throw ThrowUtil.NoDocumentAssigned();
return heightTree.GetVisualPosition(heightTree.GetLineByNumber(line));
}
VisualLineElement GetVisualLineElementFromPosition(Point visualPosition)
{
VisualLine vl = GetVisualLineFromVisualTop(visualPosition.Y);

Loading…
Cancel
Save