Skip to content

Commit 8afaef2

Browse files
committed
feature: show suggestions when searching commit by author or committer
Signed-off-by: leo <longshuang@msn.cn>
1 parent c9ed80c commit 8afaef2

4 files changed

Lines changed: 183 additions & 50 deletions

File tree

‎src/Commands/QueryUsers.cs‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
4+
namespace SourceGit.Commands
5+
{
6+
public class QueryUsers : Command
7+
{
8+
public QueryUsers(string repo)
9+
{
10+
WorkingDirectory = repo;
11+
Context = repo;
12+
RaiseError = false;
13+
Args = "log --format=%aN±%aE%n%cN±%cE";
14+
}
15+
16+
public async Task<List<Models.User>> GetResultAsync()
17+
{
18+
var rs = await ReadToEndAsync().ConfigureAwait(false);
19+
if (!rs.IsSuccess)
20+
return [];
21+
22+
var start = 0;
23+
var end = rs.StdOut.IndexOf('\n', start);
24+
var added = new HashSet<string>();
25+
var users = new List<Models.User>();
26+
while (end > 0)
27+
{
28+
var line = rs.StdOut.Substring(start, end - start);
29+
if (!string.IsNullOrEmpty(line) && !added.Contains(line))
30+
{
31+
var user = Models.User.FindOrAdd(line);
32+
users.Add(user);
33+
added.Add(line);
34+
}
35+
36+
start = end + 1;
37+
if (start >= rs.StdOut.Length - 1)
38+
break;
39+
40+
end = rs.StdOut.IndexOf('\n', start);
41+
}
42+
43+
return users;
44+
}
45+
}
46+
}

‎src/ViewModels/SearchCommitContext.cs‎

Lines changed: 87 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public bool OnlySearchCurrentBranch
4242
}
4343
}
4444

45-
public List<string> Suggestions
45+
public List<object> Suggestions
4646
{
4747
get => _suggestions;
4848
private set => SetProperty(ref _suggestions, value);
@@ -173,6 +173,8 @@ public void EndSearch()
173173
_cancellation.Cancel();
174174

175175
_worktreeFiles = null;
176+
_users = null;
177+
176178
IsQuerying = false;
177179
Suggestions = null;
178180
Results = null;
@@ -181,67 +183,116 @@ public void EndSearch()
181183

182184
private void UpdateSuggestions()
183185
{
184-
if (_method != (int)Models.CommitSearchMethod.ByPath || _requestingWorktreeFiles)
186+
if (_method == (int)Models.CommitSearchMethod.ByAuthor ||
187+
_method == (int)Models.CommitSearchMethod.ByCommitter)
185188
{
186-
Suggestions = null;
187-
return;
188-
}
189+
if (_users == null)
190+
{
191+
if (_requestingUsers)
192+
return;
189193

190-
if (_worktreeFiles == null)
191-
{
192-
_requestingWorktreeFiles = true;
194+
_requestingUsers = true;
195+
196+
Task.Run(async () =>
197+
{
198+
var users = await new Commands.QueryUsers(_repo.FullPath)
199+
.GetResultAsync()
200+
.ConfigureAwait(false);
201+
202+
Dispatcher.UIThread.Post(() =>
203+
{
204+
_requestingUsers = false;
205+
206+
if (_repo.IsSearchingCommits)
207+
{
208+
_users = users;
209+
UpdateSuggestions();
210+
}
211+
});
212+
});
213+
214+
return;
215+
}
193216

194-
Task.Run(async () =>
217+
if (_users.Count == 0 || _filter.Length < 2)
195218
{
196-
var files = await new Commands.QueryRevisionFileNames(_repo.FullPath, "HEAD")
197-
.GetResultAsync()
198-
.ConfigureAwait(false);
219+
Suggestions = null;
220+
return;
221+
}
222+
223+
var matched = new List<object>();
224+
foreach (var user in _users)
225+
{
226+
if (user.Name.Contains(_filter, StringComparison.OrdinalIgnoreCase))
227+
matched.Add(user);
228+
}
199229

200-
Dispatcher.UIThread.Post(() =>
230+
Suggestions = matched;
231+
}
232+
else if (_method == (int)Models.CommitSearchMethod.ByPath)
233+
{
234+
if (_worktreeFiles == null)
235+
{
236+
if (_requestingWorktreeFiles)
237+
return;
238+
239+
_requestingWorktreeFiles = true;
240+
241+
Task.Run(async () =>
201242
{
202-
_requestingWorktreeFiles = false;
243+
var files = await new Commands.QueryRevisionFileNames(_repo.FullPath, "HEAD")
244+
.GetResultAsync()
245+
.ConfigureAwait(false);
203246

204-
if (_repo.IsSearchingCommits)
247+
Dispatcher.UIThread.Post(() =>
205248
{
206-
_worktreeFiles = files;
207-
UpdateSuggestions();
208-
}
249+
_requestingWorktreeFiles = false;
250+
251+
if (_repo.IsSearchingCommits)
252+
{
253+
_worktreeFiles = files;
254+
UpdateSuggestions();
255+
}
256+
});
209257
});
210-
});
211258

212-
return;
213-
}
259+
return;
260+
}
214261

215-
if (_worktreeFiles.Count == 0 || _filter.Length < 3)
216-
{
217-
Suggestions = null;
218-
return;
219-
}
262+
if (_worktreeFiles.Count == 0 || _filter.Length < 3)
263+
{
264+
Suggestions = null;
265+
return;
266+
}
220267

221-
var matched = new List<string>();
222-
foreach (var file in _worktreeFiles)
223-
{
224-
if (file.Contains(_filter, StringComparison.OrdinalIgnoreCase) && file.Length != _filter.Length)
268+
var matched = new List<object>();
269+
foreach (var file in _worktreeFiles)
225270
{
226-
matched.Add(file);
227-
if (matched.Count > 100)
228-
break;
271+
if (file.Contains(_filter, StringComparison.OrdinalIgnoreCase) && file.Length != _filter.Length)
272+
matched.Add(file);
229273
}
230-
}
231274

232-
Suggestions = matched;
275+
Suggestions = matched;
276+
}
277+
else
278+
{
279+
Suggestions = null;
280+
return;
281+
}
233282
}
234283

235284
private Repository _repo = null;
236285
private CancellationTokenSource _cancellation = null;
237286
private int _method = (int)Models.CommitSearchMethod.ByMessage;
238287
private string _filter = string.Empty;
239288
private bool _onlySearchCurrentBranch = false;
240-
private List<string> _suggestions = null;
241289
private bool _isQuerying = false;
242290
private List<Models.Commit> _results = null;
243291
private Models.Commit _selected = null;
244292
private bool _requestingWorktreeFiles = false;
245293
private List<string> _worktreeFiles = null;
294+
private bool _requestingUsers = false;
295+
private List<Models.User> _users = null;
296+
private List<object> _suggestions = null;
246297
}
247298
}

‎src/Views/Repository.axaml‎

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -569,14 +569,32 @@
569569
</ListBox.ItemsPanel>
570570

571571
<ListBox.ItemTemplate>
572-
<DataTemplate DataType="x:String">
573-
<StackPanel Background="Transparent" Orientation="Vertical" Margin="8,4" DoubleTapped="OnSearchSuggestionDoubleTapped">
574-
<StackPanel Orientation="Horizontal">
575-
<Path Width="12" Height="12" Data="{StaticResource Icons.File}"/>
576-
<TextBlock Margin="6,0,0,0" Text="{Binding Converter={x:Static c:PathConverters.PureFileName}}"/>
577-
</StackPanel>
578-
<TextBlock FontSize="12" Margin="18,2,0,0" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Converter={x:Static c:PathConverters.PureDirectoryName}}"/>
579-
</StackPanel>
572+
<DataTemplate>
573+
<Border Padding="8,4" Height="42" Background="Transparent" DoubleTapped="OnSearchSuggestionDoubleTapped">
574+
<ContentControl Content="{Binding}" IsHitTestVisible="False">
575+
<ContentControl.DataTemplates>
576+
<DataTemplate DataType="x:String">
577+
<Grid Background="Transparent" ColumnDefinitions="Auto,*" DoubleTapped="OnSearchSuggestionDoubleTapped">
578+
<Path Width="24" Height="24" Data="{StaticResource Icons.File}"/>
579+
<StackPanel Grid.Column="1" Margin="6,0,0,0" Orientation="Vertical" VerticalAlignment="Center">
580+
<TextBlock Text="{Binding Converter={x:Static c:PathConverters.PureFileName}}"/>
581+
<TextBlock FontSize="12" Margin="0,2,0,0" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Converter={x:Static c:PathConverters.PureDirectoryName}}"/>
582+
</StackPanel>
583+
</Grid>
584+
</DataTemplate>
585+
586+
<DataTemplate DataType="m:User">
587+
<Grid Background="Transparent" ColumnDefinitions="Auto,*" DoubleTapped="OnSearchSuggestionDoubleTapped">
588+
<v:Avatar Grid.Column="0" Width="24" Height="24" VerticalAlignment="Center" User="{Binding}"/>
589+
<StackPanel Grid.Column="1" Margin="6,0,0,0" Orientation="Vertical" VerticalAlignment="Center">
590+
<TextBlock Text="{Binding Name, Mode=OneWay}"/>
591+
<TextBlock FontSize="12" Margin="0,2,0,0" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Email, Mode=OneWay}"/>
592+
</StackPanel>
593+
</Grid>
594+
</DataTemplate>
595+
</ContentControl.DataTemplates>
596+
</ContentControl>
597+
</Border>
580598
</DataTemplate>
581599
</ListBox.ItemTemplate>
582600
</v:ListBoxEx>

‎src/Views/Repository.axaml.cs‎

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,21 @@ private void OnSearchSuggestionBoxKeyDown(object _, KeyEventArgs e)
317317
repo.SearchCommitContext.ClearSuggestions();
318318
e.Handled = true;
319319
}
320-
else if (e.Key == Key.Enter && SearchSuggestionBox.SelectedItem is string content)
320+
else if (e.Key == Key.Enter)
321321
{
322-
repo.SearchCommitContext.Filter = content;
323-
TxtSearchCommitsBox.CaretIndex = content.Length;
322+
var selected = SearchSuggestionBox.SelectedItem;
323+
if (selected is string content)
324+
{
325+
repo.SearchCommitContext.Filter = content;
326+
TxtSearchCommitsBox.CaretIndex = content.Length;
327+
}
328+
else if (selected is Models.User user)
329+
{
330+
var apply = user.ToString();
331+
repo.SearchCommitContext.Filter = apply;
332+
TxtSearchCommitsBox.CaretIndex = apply.Length;
333+
}
334+
324335
repo.SearchCommitContext.StartSearch();
325336
e.Handled = true;
326337
}
@@ -331,13 +342,20 @@ private void OnSearchSuggestionDoubleTapped(object sender, TappedEventArgs e)
331342
if (DataContext is not ViewModels.Repository repo)
332343
return;
333344

334-
var content = (sender as StackPanel)?.DataContext as string;
335-
if (!string.IsNullOrEmpty(content))
345+
var ctx = (sender as Control)?.DataContext;
346+
if (ctx is string content)
336347
{
337348
repo.SearchCommitContext.Filter = content;
338349
TxtSearchCommitsBox.CaretIndex = content.Length;
339-
repo.SearchCommitContext.StartSearch();
340350
}
351+
else if (ctx is Models.User user)
352+
{
353+
var apply = user.ToString();
354+
repo.SearchCommitContext.Filter = apply;
355+
TxtSearchCommitsBox.CaretIndex = apply.Length;
356+
}
357+
358+
repo.SearchCommitContext.StartSearch();
341359
e.Handled = true;
342360
}
343361

0 commit comments

Comments
 (0)