From 06d98a374d59b58ffd5f4dd4501091367c8e2c82 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 15 Dec 2020 13:12:16 +0800 Subject: [PATCH] feature: show line numbers in file preview and remove limitation for line counts --- src/Git/Commit.cs | 30 +++++------ src/UI/CommitViewer.xaml | 31 +++++++---- src/UI/CommitViewer.xaml.cs | 101 ++++++++++++++++++++++++++++++++++-- src/UI/DiffViewer.xaml | 2 +- src/UI/DiffViewer.xaml.cs | 2 +- 5 files changed, 136 insertions(+), 30 deletions(-) diff --git a/src/Git/Commit.cs b/src/Git/Commit.cs index 6b0b474a..e432bf4b 100644 --- a/src/Git/Commit.cs +++ b/src/Git/Commit.cs @@ -30,6 +30,14 @@ namespace SourceGit.Git { public string SHA { get; set; } } + /// + /// Line of text in file. + /// + public class Line { + public int No { get; set; } + public string Content { get; set; } + } + /// /// SHA /// @@ -224,10 +232,10 @@ namespace SourceGit.Git { /// /// /// - public string GetTextFileContent(Repository repo, string file, out bool isBinary) { - var data = new List(); - var count = 0; + public List GetTextFileContent(Repository repo, string file, out bool isBinary) { + var data = new List(); var binary = false; + var count = 0; repo.RunCommand($"diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 {SHA} --numstat -- \"{file}\"", line => { if (REG_TESTBINARY.IsMatch(line)) binary = true; @@ -237,29 +245,21 @@ namespace SourceGit.Git { var errs = repo.RunCommand($"show {SHA}:\"{file}\"", line => { if (binary) return; - count++; - if (data.Count >= 1000) return; - if (line.IndexOf('\0') >= 0) { binary = true; data.Clear(); - data.Add("BINARY FILE PREVIEW NOT SUPPORTED!"); return; } - data.Add(line); + count++; + data.Add(new Line() { No = count, Content = line }); }); if (errs != null) App.RaiseError(errs); - } - - if (!binary && count > 1000) { - data.Add("..."); - data.Add($"Total {count} lines. Hide {count-1000} lines."); } - isBinary = binary; - return string.Join("\n", data); + isBinary = binary; + return data; } /// diff --git a/src/UI/CommitViewer.xaml b/src/UI/CommitViewer.xaml index fbaf5103..17a40048 100644 --- a/src/UI/CommitViewer.xaml +++ b/src/UI/CommitViewer.xaml @@ -11,6 +11,26 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" Unloaded="Cleanup"> + + + + + + + + @@ -427,16 +447,7 @@ - - - + diff --git a/src/UI/CommitViewer.xaml.cs b/src/UI/CommitViewer.xaml.cs index 8826685b..7659a303 100644 --- a/src/UI/CommitViewer.xaml.cs +++ b/src/UI/CommitViewer.xaml.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; @@ -423,12 +425,105 @@ namespace SourceGit.UI { Dispatcher.Invoke(() => { fileTree.ItemsSource = fileTreeSource; - filePreview.Text = ""; + previewEditor.Children.Clear(); }); } + private void LayoutPreview(List data) { + var maxLineNumber = $"{data.Count + 1}"; + var formatted = new FormattedText( + maxLineNumber, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + new Typeface(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), + 12.0, + Brushes.Black, + VisualTreeHelper.GetDpi(this).PixelsPerDip); + + var grid = new DataGrid(); + grid.SetValue(Grid.RowProperty, 1); + grid.RowHeight = 16.0; + grid.FrozenColumnCount = 1; + grid.ContextMenuOpening += OnPreviewContextMenuOpening; + grid.RowStyle = FindResource("Style.DataGridRow.NoBringIntoView") as Style; + + var colLineNumber = new DataGridTextColumn(); + colLineNumber.IsReadOnly = true; + colLineNumber.Binding = new Binding("No"); + colLineNumber.ElementStyle = FindResource("Style.DataGridText.LineNumber") as Style; + colLineNumber.Width = new DataGridLength(formatted.Width + 16, DataGridLengthUnitType.Pixel); + grid.Columns.Add(colLineNumber); + + var offset = formatted.Width + 16; + if (data.Count * 16 > previewEditor.ActualHeight) offset += 8; + + var colContent = new DataGridTextColumn(); + colContent.IsReadOnly = true; + colContent.Binding = new Binding("Content"); + colContent.ElementStyle = FindResource("Style.DataGridText.Content") as Style; + colContent.MinWidth = previewEditor.ActualWidth - offset; + colContent.Width = DataGridLength.SizeToCells; + grid.Columns.Add(colContent); + + var splitter = new System.Windows.Shapes.Rectangle(); + splitter.Width = 1; + splitter.Fill = FindResource("Brush.Border2") as Brush; + splitter.HorizontalAlignment = HorizontalAlignment.Left; + splitter.Margin = new Thickness(formatted.Width + 15, 0, 0, 0); + + grid.ItemsSource = data; + previewEditor.Children.Add(grid); + previewEditor.Children.Add(splitter); + } + + private void OnPreviewContextMenuOpening(object sender, ContextMenuEventArgs e) { + var grid = sender as DataGrid; + if (grid == null) return; + + var menu = new ContextMenu(); + var copy = new MenuItem(); + copy.Header = "Copy"; + copy.Click += (o, ev) => { + var items = grid.SelectedItems; + if (items.Count == 0) return; + + var builder = new StringBuilder(); + foreach (var item in items) { + var line = item as Git.Commit.Line; + if (line == null) continue; + + builder.Append(line.Content); + builder.AppendLine(); + } + + Clipboard.SetText(builder.ToString()); + }; + menu.Items.Add(copy); + menu.IsOpen = true; + e.Handled = true; + } + + private void OnPreviewRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) { + e.Handled = true; + } + + private void OnPreviewSizeChanged(object sender, SizeChangedEventArgs e) { + if (previewEditor.Children.Count == 0) return; + + var totalWidth = previewEditor.ActualWidth; + var totalHeight = previewEditor.ActualHeight; + var editor = previewEditor.Children[0] as DataGrid; + var minWidth = totalWidth - editor.NonFrozenColumnsViewportHorizontalOffset; + var desireHeight = editor.Items.Count * editor.RowHeight; + if (desireHeight > totalHeight) minWidth -= 8; + + editor.Columns[1].MinWidth = minWidth; + editor.Columns[1].Width = DataGridLength.SizeToCells; + editor.UpdateLayout(); + } + private async void FileTreeItemSelected(object sender, RoutedPropertyChangedEventArgs e) { - filePreview.Text = ""; + previewEditor.Children.Clear(); maskPreviewNotSupported.Visibility = Visibility.Collapsed; maskRevision.Visibility = Visibility.Collapsed; @@ -450,7 +545,7 @@ namespace SourceGit.UI { if (isBinary) { Dispatcher.Invoke(() => maskPreviewNotSupported.Visibility = Visibility.Visible); } else { - Dispatcher.Invoke(() => filePreview.Text = data); + Dispatcher.Invoke(() => LayoutPreview(data)); } }); } diff --git a/src/UI/DiffViewer.xaml b/src/UI/DiffViewer.xaml index d165ac8c..a566e80c 100644 --- a/src/UI/DiffViewer.xaml +++ b/src/UI/DiffViewer.xaml @@ -7,7 +7,7 @@ mc:Ignorable="d" FontFamily="Consolas"> - diff --git a/src/UI/DiffViewer.xaml.cs b/src/UI/DiffViewer.xaml.cs index 79b83b1e..b5bce41f 100644 --- a/src/UI/DiffViewer.xaml.cs +++ b/src/UI/DiffViewer.xaml.cs @@ -421,7 +421,7 @@ namespace SourceGit.UI { grid.RowHeight = 16.0; grid.FrozenColumnCount = lineNumbers.Length; grid.ContextMenuOpening += OnTextChangeContextMenuOpening; - grid.RowStyle = FindResource("Style.DataGridRow.TextChange") as Style; + grid.RowStyle = FindResource("Style.DataGridRow.NoBringIntoView") as Style; foreach (var number in lineNumbers) { var colLineNumber = new DataGridTextColumn();