mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-24 20:57:19 -08:00
feature<CommitViewer>: show line numbers in file preview and remove limitation for line counts
This commit is contained in:
parent
0517142330
commit
06d98a374d
5 changed files with 136 additions and 30 deletions
|
@ -30,6 +30,14 @@ namespace SourceGit.Git {
|
||||||
public string SHA { get; set; }
|
public string SHA { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Line of text in file.
|
||||||
|
/// </summary>
|
||||||
|
public class Line {
|
||||||
|
public int No { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SHA
|
/// SHA
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -224,10 +232,10 @@ namespace SourceGit.Git {
|
||||||
/// <param name="repo"></param>
|
/// <param name="repo"></param>
|
||||||
/// <param name="file"></param>
|
/// <param name="file"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public string GetTextFileContent(Repository repo, string file, out bool isBinary) {
|
public List<Line> GetTextFileContent(Repository repo, string file, out bool isBinary) {
|
||||||
var data = new List<string>();
|
var data = new List<Line>();
|
||||||
var count = 0;
|
|
||||||
var binary = false;
|
var binary = false;
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
repo.RunCommand($"diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 {SHA} --numstat -- \"{file}\"", line => {
|
repo.RunCommand($"diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 {SHA} --numstat -- \"{file}\"", line => {
|
||||||
if (REG_TESTBINARY.IsMatch(line)) binary = true;
|
if (REG_TESTBINARY.IsMatch(line)) binary = true;
|
||||||
|
@ -237,29 +245,21 @@ namespace SourceGit.Git {
|
||||||
var errs = repo.RunCommand($"show {SHA}:\"{file}\"", line => {
|
var errs = repo.RunCommand($"show {SHA}:\"{file}\"", line => {
|
||||||
if (binary) return;
|
if (binary) return;
|
||||||
|
|
||||||
count++;
|
|
||||||
if (data.Count >= 1000) return;
|
|
||||||
|
|
||||||
if (line.IndexOf('\0') >= 0) {
|
if (line.IndexOf('\0') >= 0) {
|
||||||
binary = true;
|
binary = true;
|
||||||
data.Clear();
|
data.Clear();
|
||||||
data.Add("BINARY FILE PREVIEW NOT SUPPORTED!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Add(line);
|
count++;
|
||||||
|
data.Add(new Line() { No = count, Content = line });
|
||||||
});
|
});
|
||||||
|
|
||||||
if (errs != null) App.RaiseError(errs);
|
if (errs != null) App.RaiseError(errs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!binary && count > 1000) {
|
|
||||||
data.Add("...");
|
|
||||||
data.Add($"Total {count} lines. Hide {count-1000} lines.");
|
|
||||||
}
|
|
||||||
|
|
||||||
isBinary = binary;
|
isBinary = binary;
|
||||||
return string.Join("\n", data);
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -11,6 +11,26 @@
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800"
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
Unloaded="Cleanup">
|
Unloaded="Cleanup">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<Style x:Key="Style.DataGridRow.NoBringIntoView" TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||||
|
<EventSetter Event="RequestBringIntoView" Handler="OnPreviewRequestBringIntoView"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="Style.DataGridText.LineNumber" TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Padding" Value="8,0"/>
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="Style.DataGridText.Content" TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Padding" Value="4,0,0,0"/>
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
<TabControl>
|
<TabControl>
|
||||||
<TabItem Header="INFORMATION">
|
<TabItem Header="INFORMATION">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
@ -427,16 +447,7 @@
|
||||||
|
|
||||||
<Border Grid.Column="2" BorderThickness="1" Margin="2,0" BorderBrush="{StaticResource Brush.Border2}">
|
<Border Grid.Column="2" BorderThickness="1" Margin="2,0" BorderBrush="{StaticResource Brush.Border2}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
<Grid x:Name="previewEditor" SizeChanged="OnPreviewSizeChanged" TextElement.FontFamily="Consolas" TextElement.Foreground="{StaticResource Brush.FG}"/>
|
||||||
<TextBlock
|
|
||||||
FontSize="10pt"
|
|
||||||
FontFamily="Consolas"
|
|
||||||
Padding="8"
|
|
||||||
Opacity="0.8"
|
|
||||||
Background="{StaticResource Brush.BG2}"
|
|
||||||
Foreground="{StaticResource Brush.FG}"
|
|
||||||
x:Name="filePreview"/>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<StackPanel x:Name="maskRevision" Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="Collapsed">
|
<StackPanel x:Name="maskRevision" Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="Collapsed">
|
||||||
<Path x:Name="iconPreviewRevision" Width="64" Height="64" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Submodule}" Fill="{StaticResource Brush.FG2}"/>
|
<Path x:Name="iconPreviewRevision" Width="64" Height="64" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Submodule}" Fill="{StaticResource Brush.FG2}"/>
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
@ -423,12 +425,105 @@ namespace SourceGit.UI {
|
||||||
|
|
||||||
Dispatcher.Invoke(() => {
|
Dispatcher.Invoke(() => {
|
||||||
fileTree.ItemsSource = fileTreeSource;
|
fileTree.ItemsSource = fileTreeSource;
|
||||||
filePreview.Text = "";
|
previewEditor.Children.Clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LayoutPreview(List<Git.Commit.Line> 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<object> e) {
|
private async void FileTreeItemSelected(object sender, RoutedPropertyChangedEventArgs<object> e) {
|
||||||
filePreview.Text = "";
|
previewEditor.Children.Clear();
|
||||||
maskPreviewNotSupported.Visibility = Visibility.Collapsed;
|
maskPreviewNotSupported.Visibility = Visibility.Collapsed;
|
||||||
maskRevision.Visibility = Visibility.Collapsed;
|
maskRevision.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
@ -450,7 +545,7 @@ namespace SourceGit.UI {
|
||||||
if (isBinary) {
|
if (isBinary) {
|
||||||
Dispatcher.Invoke(() => maskPreviewNotSupported.Visibility = Visibility.Visible);
|
Dispatcher.Invoke(() => maskPreviewNotSupported.Visibility = Visibility.Visible);
|
||||||
} else {
|
} else {
|
||||||
Dispatcher.Invoke(() => filePreview.Text = data);
|
Dispatcher.Invoke(() => LayoutPreview(data));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
FontFamily="Consolas">
|
FontFamily="Consolas">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<Style x:Key="Style.DataGridRow.TextChange" TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
<Style x:Key="Style.DataGridRow.NoBringIntoView" TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||||
<EventSetter Event="RequestBringIntoView" Handler="OnLineRequestBringIntoView"/>
|
<EventSetter Event="RequestBringIntoView" Handler="OnLineRequestBringIntoView"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
|
|
@ -421,7 +421,7 @@ namespace SourceGit.UI {
|
||||||
grid.RowHeight = 16.0;
|
grid.RowHeight = 16.0;
|
||||||
grid.FrozenColumnCount = lineNumbers.Length;
|
grid.FrozenColumnCount = lineNumbers.Length;
|
||||||
grid.ContextMenuOpening += OnTextChangeContextMenuOpening;
|
grid.ContextMenuOpening += OnTextChangeContextMenuOpening;
|
||||||
grid.RowStyle = FindResource("Style.DataGridRow.TextChange") as Style;
|
grid.RowStyle = FindResource("Style.DataGridRow.NoBringIntoView") as Style;
|
||||||
|
|
||||||
foreach (var number in lineNumbers) {
|
foreach (var number in lineNumbers) {
|
||||||
var colLineNumber = new DataGridTextColumn();
|
var colLineNumber = new DataGridTextColumn();
|
||||||
|
|
Loading…
Reference in a new issue