feature: auto-complete git commit message keywords, such as Co-authored-by:

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-11-05 16:39:31 +08:00
parent 54fedef50c
commit a744a93560
2 changed files with 96 additions and 0 deletions

View File

@@ -5,6 +5,7 @@
xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
xmlns:ae="using:AvaloniaEdit"
xmlns:acc="using:AvaloniaEdit.CodeCompletion"
xmlns:aee="using:AvaloniaEdit.Editing"
xmlns:aes="using:AvaloniaEdit.Search">
<Design.PreviewWith>
@@ -1347,4 +1348,16 @@
<Setter Property="IsVisible" Value="False"/>
</Style>
</Style>
<Style Selector="acc|CompletionListBox">
<Setter Property="Background" Value="{DynamicResource Brush.Popup}"/>
<Setter Property="Padding" Value="8,4"/>
<Setter Property="CornerRadius" Value="4"/>
</Style>
<Style Selector="acc|CompletionListBox ListBoxItem">
<Setter Property="Height" Value="24"/>
<Setter Property="Margin" Value="0,2"/>
<Setter Property="CornerRadius" Value="4"/>
</Style>
</Styles>

View File

@@ -11,12 +11,52 @@ using Avalonia.Layout;
using Avalonia.Media;
using AvaloniaEdit;
using AvaloniaEdit.CodeCompletion;
using AvaloniaEdit.Document;
using AvaloniaEdit.Editing;
using AvaloniaEdit.Rendering;
using AvaloniaEdit.Utils;
namespace SourceGit.Views
{
public class CommitMessageCodeCompletionData : ICompletionData
{
public IImage Image
{
get => null;
}
public string Text
{
get;
}
public object Content
{
get => Text;
}
public object Description
{
get => null;
}
public double Priority
{
get => 0;
}
public CommitMessageCodeCompletionData(string text)
{
Text = text;
}
public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs)
{
textArea.Document.Replace(completionSegment, Text);
}
}
public class CommitMessageTextEditor : TextEditor
{
public static readonly StyledProperty<string> CommitMessageProperty =
@@ -183,9 +223,50 @@ namespace SourceGit.Views
{
base.OnTextChanged(e);
if (!IsLoaded)
return;
_isEditing = true;
SetCurrentValue(CommitMessageProperty, Text);
_isEditing = false;
var caretOffset = CaretOffset;
var start = caretOffset;
while (start > 0 && !char.IsWhiteSpace(Text[start - 1]))
start--;
if (caretOffset == start)
{
_completionWnd?.Close();
return;
}
var word = Text.Substring(start, caretOffset - start);
var matches = new List<CommitMessageCodeCompletionData>();
foreach (var keyword in _keywords)
{
if (keyword.StartsWith(word, StringComparison.OrdinalIgnoreCase) && keyword.Length != word.Length)
matches.Add(new(keyword));
}
if (matches.Count > 0)
{
if (_completionWnd == null)
{
_completionWnd = new CompletionWindow(TextArea);
_completionWnd.Closed += (_, ev) => _completionWnd = null;
_completionWnd.Show();
}
_completionWnd.CompletionList.CompletionData.Clear();
_completionWnd.CompletionList.CompletionData.AddRange(matches);
_completionWnd.StartOffset = start;
_completionWnd.EndOffset = caretOffset;
}
else
{
_completionWnd?.Close();
}
}
private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e)
@@ -235,7 +316,9 @@ namespace SourceGit.Views
InvalidateVisual();
}
private readonly List<string> _keywords = ["git", "GitHub", "GitLab", "Acked-by: ", "Co-authored-by: ", "Reviewed-by: ", "Signed-off-by: ", "BREAKING CHANGE: "];
private bool _isEditing = false;
private CompletionWindow _completionWnd = null;
}
public partial class CommitMessageToolBox : UserControl