-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
--> references #37
- Loading branch information
Showing
37 changed files
with
834 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<UserControl x:Class="AlephNote.WPF.Controls.DiffViewer" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:util="clr-namespace:AlephNote.WPF.Util" | ||
mc:Ignorable="d" | ||
d:DesignHeight="450" d:DesignWidth="800"> | ||
<Grid x:Name="DiffRoot"> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition Width="1*"/> | ||
<ColumnDefinition Width="Auto"/> | ||
<ColumnDefinition Width="1*"/> | ||
</Grid.ColumnDefinitions> | ||
|
||
<GridSplitter Grid.Column="1" | ||
HorizontalAlignment="Center" VerticalAlignment="Stretch" | ||
Background="LightGray" ShowsPreview="true" Width="5" /> | ||
|
||
<RichTextBox Grid.Column="0" x:Name="leftDiff" | ||
util:SideBySideDiffModelVisualizer.Left="{Binding Diff}" FontFamily="Consolas" | ||
AcceptsReturn="True" IsReadOnly="True" | ||
VerticalScrollBarVisibility="Visible" ScrollViewer.ScrollChanged="ScrollChanged"/> | ||
|
||
<RichTextBox Grid.Column="2" x:Name="rightDiff" | ||
util:SideBySideDiffModelVisualizer.Right="{Binding Diff}" FontFamily="Consolas" | ||
AcceptsReturn="True" IsReadOnly="True" | ||
VerticalScrollBarVisibility="Visible" ScrollViewer.ScrollChanged="ScrollChanged" /> | ||
|
||
</Grid> | ||
</UserControl> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
using DiffPlex; | ||
using DiffPlex.DiffBuilder; | ||
using DiffPlex.DiffBuilder.Model; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Linq; | ||
using System.Runtime.CompilerServices; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using System.Windows; | ||
using System.Windows.Controls; | ||
using System.Windows.Data; | ||
using System.Windows.Documents; | ||
using System.Windows.Input; | ||
using System.Windows.Media; | ||
using System.Windows.Media.Imaging; | ||
using System.Windows.Navigation; | ||
using System.Windows.Shapes; | ||
|
||
namespace AlephNote.WPF.Controls | ||
{ | ||
/// <summary> | ||
/// Interaction logic for DiffViewer.xaml | ||
/// </summary> | ||
public partial class DiffViewer : UserControl, INotifyPropertyChanged | ||
{ | ||
#region INotifyPropertyChanged | ||
|
||
public event PropertyChangedEventHandler PropertyChanged; | ||
|
||
private void OnPropertyChanged([CallerMemberName] string propertyName = null) | ||
{ | ||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | ||
} | ||
|
||
private void OnExplicitPropertyChanged(string propertyName) | ||
{ | ||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | ||
} | ||
|
||
#endregion | ||
|
||
public static readonly DependencyProperty TextLeftProperty = | ||
DependencyProperty.Register( | ||
"TextLeft", | ||
typeof(string), | ||
typeof(DiffViewer), | ||
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.None, (o,e) => { ((DiffViewer)o).OnTextChanged(e); })); | ||
|
||
public static readonly DependencyProperty TextRightProperty = | ||
DependencyProperty.Register( | ||
"TextRight", | ||
typeof(string), | ||
typeof(DiffViewer), | ||
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.None, (o,e) => { ((DiffViewer)o).OnTextChanged(e); })); | ||
|
||
public string TextLeft | ||
{ | ||
get => (string)GetValue(TextLeftProperty); | ||
set => SetValue(TextLeftProperty, value); | ||
} | ||
|
||
public string TextRight | ||
{ | ||
get => (string)GetValue(TextRightProperty); | ||
set => SetValue(TextRightProperty, value); | ||
} | ||
|
||
private SideBySideDiffModel _diff; | ||
public SideBySideDiffModel Diff | ||
{ | ||
get => _diff; | ||
private set { _diff = value; OnPropertyChanged(); } | ||
} | ||
|
||
public DiffViewer() | ||
{ | ||
InitializeComponent(); | ||
Compare(); | ||
|
||
DiffRoot.DataContext = this; | ||
} | ||
|
||
private void OnTextChanged(DependencyPropertyChangedEventArgs e) | ||
{ | ||
Compare(); | ||
} | ||
|
||
private void Compare() | ||
{ | ||
var diffBuilder = new SideBySideDiffBuilder(new Differ()); | ||
Diff = diffBuilder.BuildDiffModel(TextLeft ?? string.Empty, TextRight ?? string.Empty); | ||
} | ||
|
||
private void ScrollChanged(object sender, ScrollChangedEventArgs e) | ||
{ | ||
leftDiff.ScrollToVerticalOffset(e.VerticalOffset); | ||
leftDiff.ScrollToHorizontalOffset(e.HorizontalOffset); | ||
|
||
rightDiff.ScrollToVerticalOffset(e.VerticalOffset); | ||
rightDiff.ScrollToHorizontalOffset(e.HorizontalOffset); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
Source/AlephNote.App/WPF/Util/SideBySideDiffModelVisualizer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
using DiffPlex.DiffBuilder.Model; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Windows; | ||
using System.Windows.Controls; | ||
using System.Windows.Documents; | ||
using System.Windows.Media; | ||
|
||
namespace AlephNote.WPF.Util | ||
{ | ||
public class SideBySideDiffModelVisualizer | ||
{ | ||
public static SideBySideDiffModel GetLeft(DependencyObject obj) { return (SideBySideDiffModel)obj.GetValue(LeftProperty); } | ||
public static void SetLeft(DependencyObject obj, SideBySideDiffModel value) { obj.SetValue(LeftProperty, value); } | ||
public static readonly DependencyProperty LeftProperty = DependencyProperty.RegisterAttached("Left", typeof(SideBySideDiffModel), typeof(SideBySideDiffModelVisualizer), new PropertyMetadata(null, new PropertyChangedCallback(LeftChanged))); | ||
private static void LeftChanged(DependencyObject dep, DependencyPropertyChangedEventArgs e) | ||
{ | ||
var richTextBox = (RichTextBox)dep; | ||
var diff = (SideBySideDiffModel)e.NewValue; | ||
|
||
var zippedDiffs = Enumerable.Zip(diff.OldText.Lines, diff.NewText.Lines, (oldLine, newLine) => new OldNew<DiffPiece> { Old = oldLine, New = newLine }).ToList(); | ||
ShowDiffs(richTextBox, zippedDiffs, line => line.Old, piece => piece.Old); | ||
} | ||
|
||
public static SideBySideDiffModel GetRight(DependencyObject obj) { return (SideBySideDiffModel)obj.GetValue(RightProperty); } | ||
public static void SetRight(DependencyObject obj, SideBySideDiffModel value) { obj.SetValue(RightProperty, value); } | ||
public static readonly DependencyProperty RightProperty = DependencyProperty.RegisterAttached("Right", typeof(SideBySideDiffModel), typeof(SideBySideDiffModelVisualizer), new PropertyMetadata(null, new PropertyChangedCallback(RightChanged))); | ||
private static void RightChanged(DependencyObject dep, DependencyPropertyChangedEventArgs e) | ||
{ | ||
var richTextBox = (RichTextBox)dep; | ||
var diff = (SideBySideDiffModel)e.NewValue; | ||
|
||
var zippedDiffs = Enumerable.Zip(diff.OldText.Lines, diff.NewText.Lines, (oldLine, newLine) => new OldNew<DiffPiece> { Old = oldLine, New = newLine }).ToList(); | ||
ShowDiffs(richTextBox, zippedDiffs, line => line.New, piece => piece.New); | ||
} | ||
|
||
private static void ShowDiffs(RichTextBox diffBox, List<OldNew<DiffPiece>> lines, Func<OldNew<DiffPiece>, DiffPiece> lineSelector, Func<OldNew<DiffPiece>, DiffPiece> pieceSelector) | ||
{ | ||
diffBox.Document.Blocks.Clear(); | ||
foreach (var line in lines) | ||
{ | ||
var synchroLineLength = Math.Max(line.Old.Text?.Length ?? 0, line.New.Text?.Length ?? 0); | ||
var lineSubPieces = Enumerable.Zip(line.Old.SubPieces, line.New.SubPieces, (oldPiece, newPiece) => new OldNew<DiffPiece> { Old = oldPiece, New = newPiece, Length = Math.Max(oldPiece.Text?.Length ?? 0, newPiece.Text?.Length ?? 0) }); | ||
|
||
var oldNewLine = lineSelector(line); | ||
switch (oldNewLine.Type) | ||
{ | ||
case ChangeType.Unchanged: AppendParagraph(diffBox, oldNewLine.Text ?? string.Empty); break; | ||
case ChangeType.Imaginary: AppendParagraph(diffBox, new string(BreakingSpace, synchroLineLength), Brushes.Gray, Brushes.LightCyan); break; | ||
case ChangeType.Inserted: AppendParagraph(diffBox, oldNewLine.Text ?? string.Empty, Brushes.LightGreen); break; | ||
case ChangeType.Deleted: AppendParagraph(diffBox, oldNewLine.Text ?? string.Empty, Brushes.OrangeRed); break; | ||
case ChangeType.Modified: | ||
var paragraph = AppendParagraph(diffBox, string.Empty); | ||
foreach (var subPiece in lineSubPieces) | ||
{ | ||
var oldNewPiece = pieceSelector(subPiece); | ||
switch (oldNewPiece.Type) | ||
{ | ||
case ChangeType.Unchanged: paragraph.Inlines.Add(NewRun(oldNewPiece.Text ?? string.Empty, Brushes.Yellow)); break; | ||
case ChangeType.Imaginary: paragraph.Inlines.Add(NewRun(oldNewPiece.Text ?? string.Empty)); break; | ||
case ChangeType.Inserted: paragraph.Inlines.Add(NewRun(oldNewPiece.Text ?? string.Empty, Brushes.LightGreen)); break; | ||
case ChangeType.Deleted: paragraph.Inlines.Add(NewRun(oldNewPiece.Text ?? string.Empty, Brushes.OrangeRed)); break; | ||
case ChangeType.Modified: paragraph.Inlines.Add(NewRun(oldNewPiece.Text ?? string.Empty, Brushes.Yellow)); break; | ||
default: throw new ArgumentException(); | ||
} | ||
paragraph.Inlines.Add(NewRun(new string(BreakingSpace, subPiece.Length - (oldNewPiece.Text ?? string.Empty).Length), Brushes.Gray, Brushes.LightCyan)); | ||
} | ||
break; | ||
default: throw new ArgumentException(); | ||
} | ||
} | ||
} | ||
|
||
private class OldNew<T> | ||
{ | ||
public T Old { get; set; } | ||
public T New { get; set; } | ||
public int Length { get; set; } | ||
} | ||
|
||
private static char BreakingSpace = '\u2219'; | ||
|
||
private static Paragraph AppendParagraph(RichTextBox textBox, string text, Brush background = null, Brush foreground = null) | ||
{ | ||
var paragraph = new Paragraph(new Run(text)) | ||
{ | ||
LineHeight = 0.5, | ||
Background = background ?? Brushes.Transparent, | ||
Foreground = foreground ?? Brushes.Black, | ||
BorderBrush = Brushes.DarkGray, | ||
BorderThickness = new Thickness(0, 0, 0, 1), | ||
}; | ||
|
||
textBox.Document.Blocks.Add(paragraph); | ||
return paragraph; | ||
} | ||
|
||
private static Run NewRun(string text, Brush background = null, Brush foreground = null) => new Run(text) | ||
{ | ||
Background = background ?? Brushes.Transparent, | ||
Foreground = foreground ?? Brushes.Black, | ||
}; | ||
} | ||
} |
Oops, something went wrong.