mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-24 20:57:19 -08:00
feature: add auto complete box for searching commits by file path
This commit is contained in:
parent
addfb449cc
commit
7f8b8a19a0
4 changed files with 235 additions and 35 deletions
21
src/Commands/QueryCurrentRevisionFiles.cs
Normal file
21
src/Commands/QueryCurrentRevisionFiles.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace SourceGit.Commands
|
||||||
|
{
|
||||||
|
public class QueryCurrentRevisionFiles : Command
|
||||||
|
{
|
||||||
|
public QueryCurrentRevisionFiles(string repo)
|
||||||
|
{
|
||||||
|
WorkingDirectory = repo;
|
||||||
|
Context = repo;
|
||||||
|
Args = "ls-tree -r --name-only HEAD";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] Result()
|
||||||
|
{
|
||||||
|
var rs = ReadToEnd();
|
||||||
|
if (rs.IsSuccess)
|
||||||
|
return rs.StdOut.Split('\n', System.StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
|
@ -170,8 +171,15 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
SearchedCommits = new List<Models.Commit>();
|
SearchedCommits = new List<Models.Commit>();
|
||||||
SearchCommitFilter = string.Empty;
|
SearchCommitFilter = string.Empty;
|
||||||
|
SearchCommitFilterSuggestion.Clear();
|
||||||
|
IsSearchCommitSuggestionOpen = false;
|
||||||
|
_revisionFiles.Clear();
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
|
{
|
||||||
SelectedViewIndex = 0;
|
SelectedViewIndex = 0;
|
||||||
|
UpdateCurrentRevisionFilesForSearchSuggestion();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,15 +193,59 @@ namespace SourceGit.ViewModels
|
||||||
public int SearchCommitFilterType
|
public int SearchCommitFilterType
|
||||||
{
|
{
|
||||||
get => _searchCommitFilterType;
|
get => _searchCommitFilterType;
|
||||||
set => SetProperty(ref _searchCommitFilterType, value);
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchCommitFilterType, value))
|
||||||
|
UpdateCurrentRevisionFilesForSearchSuggestion();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SearchCommitFilter
|
public string SearchCommitFilter
|
||||||
{
|
{
|
||||||
get => _searchCommitFilter;
|
get => _searchCommitFilter;
|
||||||
set => SetProperty(ref _searchCommitFilter, value);
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchCommitFilter, value) &&
|
||||||
|
_searchCommitFilterType == 3 &&
|
||||||
|
!string.IsNullOrEmpty(value) &&
|
||||||
|
value.Length >= 2 &&
|
||||||
|
_revisionFiles.Count > 0)
|
||||||
|
{
|
||||||
|
var suggestion = new List<string>();
|
||||||
|
foreach (var file in _revisionFiles)
|
||||||
|
{
|
||||||
|
if (file.Contains(value, StringComparison.OrdinalIgnoreCase) && file.Length != value.Length)
|
||||||
|
{
|
||||||
|
suggestion.Add(file);
|
||||||
|
if (suggestion.Count > 100)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchCommitFilterSuggestion.Clear();
|
||||||
|
SearchCommitFilterSuggestion.AddRange(suggestion);
|
||||||
|
IsSearchCommitSuggestionOpen = SearchCommitFilterSuggestion.Count > 0;
|
||||||
|
}
|
||||||
|
else if (SearchCommitFilterSuggestion.Count > 0)
|
||||||
|
{
|
||||||
|
SearchCommitFilterSuggestion.Clear();
|
||||||
|
IsSearchCommitSuggestionOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsSearchCommitSuggestionOpen
|
||||||
|
{
|
||||||
|
get => _isSearchCommitSuggestionOpen;
|
||||||
|
set => SetProperty(ref _isSearchCommitSuggestionOpen, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AvaloniaList<string> SearchCommitFilterSuggestion
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
} = new AvaloniaList<string>();
|
||||||
|
|
||||||
public List<Models.Commit> SearchedCommits
|
public List<Models.Commit> SearchedCommits
|
||||||
{
|
{
|
||||||
get => _searchedCommits;
|
get => _searchedCommits;
|
||||||
|
@ -306,6 +358,9 @@ namespace SourceGit.ViewModels
|
||||||
_visibleTags.Clear();
|
_visibleTags.Clear();
|
||||||
_submodules.Clear();
|
_submodules.Clear();
|
||||||
_searchedCommits.Clear();
|
_searchedCommits.Clear();
|
||||||
|
|
||||||
|
_revisionFiles.Clear();
|
||||||
|
SearchCommitFilterSuggestion.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshAll()
|
public void RefreshAll()
|
||||||
|
@ -450,6 +505,8 @@ namespace SourceGit.ViewModels
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IsSearchLoadingVisible = true;
|
IsSearchLoadingVisible = true;
|
||||||
|
IsSearchCommitSuggestionOpen = false;
|
||||||
|
SearchCommitFilterSuggestion.Clear();
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
|
@ -1886,6 +1943,20 @@ namespace SourceGit.ViewModels
|
||||||
return visible;
|
return visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateCurrentRevisionFilesForSearchSuggestion()
|
||||||
|
{
|
||||||
|
_revisionFiles.Clear();
|
||||||
|
|
||||||
|
if (_searchCommitFilterType == 3)
|
||||||
|
{
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
var files = new Commands.QueryCurrentRevisionFiles(_fullpath).Result();
|
||||||
|
Dispatcher.UIThread.Invoke(() => _revisionFiles.AddRange(files));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string _fullpath = string.Empty;
|
private string _fullpath = string.Empty;
|
||||||
private string _gitDir = string.Empty;
|
private string _gitDir = string.Empty;
|
||||||
private Models.RepositorySettings _settings = null;
|
private Models.RepositorySettings _settings = null;
|
||||||
|
@ -1899,9 +1970,11 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
private bool _isSearching = false;
|
private bool _isSearching = false;
|
||||||
private bool _isSearchLoadingVisible = false;
|
private bool _isSearchLoadingVisible = false;
|
||||||
|
private bool _isSearchCommitSuggestionOpen = false;
|
||||||
private int _searchCommitFilterType = 0;
|
private int _searchCommitFilterType = 0;
|
||||||
private string _searchCommitFilter = string.Empty;
|
private string _searchCommitFilter = string.Empty;
|
||||||
private List<Models.Commit> _searchedCommits = new List<Models.Commit>();
|
private List<Models.Commit> _searchedCommits = new List<Models.Commit>();
|
||||||
|
private List<string> _revisionFiles = new List<string>();
|
||||||
|
|
||||||
private bool _isLocalBranchGroupExpanded = true;
|
private bool _isLocalBranchGroupExpanded = true;
|
||||||
private bool _isRemoteGroupExpanded = false;
|
private bool _isRemoteGroupExpanded = false;
|
||||||
|
|
|
@ -453,40 +453,92 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Commit Search Panel -->
|
<!-- Commit Search Panel -->
|
||||||
<Grid Grid.Row="1" RowDefinitions="Auto,32,*" Margin="8,0,4,8" IsVisible="{Binding IsSearching}" PropertyChanged="OnSearchCommitPanelPropertyChanged">
|
<Grid Grid.Row="1" RowDefinitions="24,32,*" Margin="8,0,4,8" IsVisible="{Binding IsSearching}" PropertyChanged="OnSearchCommitPanelPropertyChanged">
|
||||||
<!-- Search Input Box -->
|
<!-- Search Input Box -->
|
||||||
<TextBox Grid.Row="0"
|
<Grid Grid.Row="0">
|
||||||
x:Name="TxtSearchCommitsBox"
|
<TextBox x:Name="TxtSearchCommitsBox"
|
||||||
Height="24"
|
Height="24"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
BorderBrush="{DynamicResource Brush.Border2}"
|
BorderBrush="{DynamicResource Brush.Border2}"
|
||||||
Background="{DynamicResource Brush.Contents}"
|
Background="{DynamicResource Brush.Contents}"
|
||||||
CornerRadius="4"
|
CornerRadius="4"
|
||||||
Watermark="{DynamicResource Text.Repository.Search}"
|
Watermark="{DynamicResource Text.Repository.Search}"
|
||||||
Text="{Binding SearchCommitFilter, Mode=TwoWay}"
|
Text="{Binding SearchCommitFilter, Mode=TwoWay}"
|
||||||
VerticalContentAlignment="Center"
|
VerticalContentAlignment="Center"
|
||||||
KeyDown="OnSearchKeyDown">
|
KeyDown="OnSearchKeyDown">
|
||||||
<TextBox.InnerLeftContent>
|
<TextBox.InnerLeftContent>
|
||||||
<Path Width="14" Height="14"
|
|
||||||
Margin="6,0,0,0"
|
|
||||||
Fill="{DynamicResource Brush.FG2}"
|
|
||||||
Data="{StaticResource Icons.Search}"/>
|
|
||||||
</TextBox.InnerLeftContent>
|
|
||||||
|
|
||||||
<TextBox.InnerRightContent>
|
|
||||||
<Button Classes="icon_button"
|
|
||||||
Width="16"
|
|
||||||
Margin="0,0,6,0"
|
|
||||||
Command="{Binding ClearSearchCommitFilter}"
|
|
||||||
IsVisible="{Binding SearchCommitFilter, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
|
||||||
HorizontalAlignment="Right">
|
|
||||||
<Path Width="14" Height="14"
|
<Path Width="14" Height="14"
|
||||||
Margin="0,1,0,0"
|
Margin="6,0,0,0"
|
||||||
Fill="{DynamicResource Brush.FG1}"
|
Fill="{DynamicResource Brush.FG2}"
|
||||||
Data="{StaticResource Icons.Clear}"/>
|
Data="{StaticResource Icons.Search}"/>
|
||||||
</Button>
|
</TextBox.InnerLeftContent>
|
||||||
</TextBox.InnerRightContent>
|
|
||||||
</TextBox>
|
<TextBox.InnerRightContent>
|
||||||
|
<Button Classes="icon_button"
|
||||||
|
Width="16"
|
||||||
|
Margin="0,0,6,0"
|
||||||
|
Command="{Binding ClearSearchCommitFilter}"
|
||||||
|
IsVisible="{Binding SearchCommitFilter, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Path Width="14" Height="14"
|
||||||
|
Margin="0,1,0,0"
|
||||||
|
Fill="{DynamicResource Brush.FG1}"
|
||||||
|
Data="{StaticResource Icons.Clear}"/>
|
||||||
|
</Button>
|
||||||
|
</TextBox.InnerRightContent>
|
||||||
|
</TextBox>
|
||||||
|
|
||||||
|
<Popup PlacementTarget="#TxtSearchCommitsBox"
|
||||||
|
Placement="BottomEdgeAlignedLeft"
|
||||||
|
HorizontalOffset="-8" VerticalAlignment="-8"
|
||||||
|
IsOpen="{Binding IsSearchCommitSuggestionOpen}">
|
||||||
|
<Border Margin="8" VerticalAlignment="Top" Effect="drop-shadow(0 0 8 #80000000)">
|
||||||
|
<Border Background="{DynamicResource Brush.Popup}" CornerRadius="4" Padding="4" BorderThickness="0.65" BorderBrush="{DynamicResource Brush.Accent}">
|
||||||
|
<ListBox x:Name="SearchSuggestionBox"
|
||||||
|
Background="Transparent"
|
||||||
|
SelectionMode="Single"
|
||||||
|
ItemsSource="{Binding SearchCommitFilterSuggestion}"
|
||||||
|
MaxHeight="400"
|
||||||
|
Focusable="True"
|
||||||
|
KeyDown="OnSearchSuggestionBoxKeyDown">
|
||||||
|
<ListBox.Styles>
|
||||||
|
<Style Selector="ListBoxItem">
|
||||||
|
<Setter Property="Padding" Value="0"/>
|
||||||
|
<Setter Property="MinHeight" Value="26"/>
|
||||||
|
<Setter Property="CornerRadius" Value="4"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ListBox">
|
||||||
|
<Setter Property="FocusAdorner">
|
||||||
|
<FocusAdornerTemplate>
|
||||||
|
<Grid/>
|
||||||
|
</FocusAdornerTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</ListBox.Styles>
|
||||||
|
|
||||||
|
<ListBox.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Orientation="Vertical"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ListBox.ItemsPanel>
|
||||||
|
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="x:String">
|
||||||
|
<StackPanel Background="Transparent" Orientation="Vertical" Margin="8,4" DoubleTapped="OnSearchSuggestionDoubleTapped">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Path Width="12" Height="12" Data="{StaticResource Icons.File}"/>
|
||||||
|
<TextBlock Classes="primary" Margin="6,0,0,0" Text="{Binding Converter={x:Static c:PathConverters.PureFileName}}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Classes="primary" FontSize="12" Margin="18,2,0,0" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Converter={x:Static c:PathConverters.PureDirectoryName}}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,*">
|
<Grid Grid.Row="1" ColumnDefinitions="Auto,*">
|
||||||
<TextBlock Grid.Column="0"
|
<TextBlock Grid.Column="0"
|
||||||
|
|
|
@ -29,11 +29,33 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
private void OnSearchKeyDown(object _, KeyEventArgs e)
|
private void OnSearchKeyDown(object _, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
|
var repo = DataContext as ViewModels.Repository;
|
||||||
if (e.Key == Key.Enter)
|
if (e.Key == Key.Enter)
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.Repository repo && !string.IsNullOrWhiteSpace(repo.SearchCommitFilter))
|
if (!string.IsNullOrWhiteSpace(repo.SearchCommitFilter))
|
||||||
repo.StartSearchCommits();
|
repo.StartSearchCommits();
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Down)
|
||||||
|
{
|
||||||
|
if (repo.IsSearchCommitSuggestionOpen)
|
||||||
|
{
|
||||||
|
SearchSuggestionBox.Focus(NavigationMethod.Tab);
|
||||||
|
SearchSuggestionBox.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Escape)
|
||||||
|
{
|
||||||
|
if (repo.IsSearchCommitSuggestionOpen)
|
||||||
|
{
|
||||||
|
repo.SearchCommitFilterSuggestion.Clear();
|
||||||
|
repo.IsSearchCommitSuggestionOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,5 +294,37 @@ namespace SourceGit.Views
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSearchSuggestionBoxKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
var repo = DataContext as ViewModels.Repository;
|
||||||
|
if (e.Key == Key.Escape)
|
||||||
|
{
|
||||||
|
repo.IsSearchCommitSuggestionOpen = false;
|
||||||
|
repo.SearchCommitFilterSuggestion.Clear();
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Enter && SearchSuggestionBox.SelectedItem is string content)
|
||||||
|
{
|
||||||
|
repo.SearchCommitFilter = content;
|
||||||
|
TxtSearchCommitsBox.CaretIndex = content.Length;
|
||||||
|
repo.StartSearchCommits();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSearchSuggestionDoubleTapped(object sender, TappedEventArgs e)
|
||||||
|
{
|
||||||
|
var repo = DataContext as ViewModels.Repository;
|
||||||
|
var content = (sender as StackPanel)?.DataContext as string;
|
||||||
|
if (!string.IsNullOrEmpty(content))
|
||||||
|
{
|
||||||
|
repo.SearchCommitFilter = content;
|
||||||
|
TxtSearchCommitsBox.CaretIndex = content.Length;
|
||||||
|
repo.StartSearchCommits();
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue