From f8debc04876bc24eaa8d04731e30fed13d379cf8 Mon Sep 17 00:00:00 2001
From: Daniil Tatianin <99danilt@gmail.com>
Date: Wed, 23 Sep 2026 22:33:58 +0300
Subject: [PATCH] feature: configurable description guide length for commit
messages
Add a `Description Guide Length` preference next to the existing subject
guide length. The commit message editor now:
- shows a `DESCRIPTION n/limit` counter (longest body line) with the same
warning color and icon used for the subject
- draws the vertical column guide at the configured length instead of a
hardcoded 80 columns
Since the working copy commit box, the reword editor in interactive
rebase, the commit template editor and the standalone editor mode all
share `CommitMessageToolBox`, the guide applies everywhere a message is
written. The default stays at 80 so existing users see no change.
Signed-off-by: Daniil Tatianin <99danilt@gmail.com>
---
src/Resources/Locales/en_US.axaml | 2 +
src/Resources/Locales/zh_CN.axaml | 2 +
src/Resources/Locales/zh_TW.axaml | 2 +
src/ViewModels/Preferences.cs | 7 +++
src/Views/CommitMessageToolBox.axaml | 18 +++++++
src/Views/CommitMessageToolBox.axaml.cs | 72 ++++++++++++++++++++++++-
src/Views/Preferences.axaml | 39 ++++++++++----
7 files changed, 131 insertions(+), 11 deletions(-)
diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index 4dc15a4c36..2ac3000d4e 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -204,6 +204,7 @@
SHA
Signer:
Open in Browser
+ DESCRIPTION
Enter commit message. Please use an empty-line to separate subject and description!
SUBJECT
Compare
@@ -696,6 +697,7 @@
GENERAL
Check for updates on startup
Date Format
+ Description Guide Length
Enable compact folders in changes tree
Language
History Commits
diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml
index a7f35949ba..28f8eb8ae5 100644
--- a/src/Resources/Locales/zh_CN.axaml
+++ b/src/Resources/Locales/zh_CN.axaml
@@ -208,6 +208,7 @@
提交指纹
签名者 :
浏览器中查看
+ 描述
请输入提交的信息。注意:主题与具体描述中间需要空白行分隔!
主题
比较
@@ -700,6 +701,7 @@
通用配置
启动时检测软件更新
日期时间格式
+ DESCRIPTION字数检测
在变更列表树中启用紧凑文件夹模式
显示语言
最大历史提交数
diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml
index 85612abe12..ac0d36a326 100644
--- a/src/Resources/Locales/zh_TW.axaml
+++ b/src/Resources/Locales/zh_TW.axaml
@@ -198,6 +198,7 @@
提交編號
簽署人:
在瀏覽器中檢視
+ 描述
請輸入提交訊息,標題與詳細描述之間請使用單行空白區隔。
標題
比較
@@ -690,6 +691,7 @@
一般設定
啟動時檢查軟體更新
日期時間格式
+ 提交描述字數偵測
在樹狀變更目錄中啟用密集資料夾模式
顯示語言
最大歷史提交數
diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs
index 4803ec4296..c1fa46e0d9 100644
--- a/src/ViewModels/Preferences.cs
+++ b/src/ViewModels/Preferences.cs
@@ -141,6 +141,12 @@ public int SubjectGuideLength
set => SetProperty(ref _subjectGuideLength, value);
}
+ public int DescriptionGuideLength
+ {
+ get => _descriptionGuideLength;
+ set => SetProperty(ref _descriptionGuideLength, value);
+ }
+
public int DateTimeFormat
{
get => Models.DateTimeFormat.ActiveIndex;
@@ -820,6 +826,7 @@ private bool RemoveInvalidRepositoriesRecursive(List collection)
private int _maxHistoryCommits = 20000;
private int _subjectGuideLength = 50;
+ private int _descriptionGuideLength = 80;
private bool _useFixedTabWidth = true;
private bool _useAutoHideScrollBars = true;
private bool _useGitHubStyleAvatar = true;
diff --git a/src/Views/CommitMessageToolBox.axaml b/src/Views/CommitMessageToolBox.axaml
index 8f65e5372c..5ca9490dac 100644
--- a/src/Views/CommitMessageToolBox.axaml
+++ b/src/Views/CommitMessageToolBox.axaml
@@ -21,6 +21,7 @@
Text="{Binding #ThisControl.CommitMessage, Mode=TwoWay}"
Watermark="{DynamicResource Text.CommitMessageTextBox.Placeholder}"
SubjectGuideLength="{Binding Source={x:Static vm:Preferences.Instance}, Path=SubjectGuideLength, Mode=OneWay}"
+ DescriptionGuideLength="{Binding Source={x:Static vm:Preferences.Instance}, Path=DescriptionGuideLength, Mode=OneWay}"
GuideLineBrush="{DynamicResource Brush.Border2}"
FontFamily="{DynamicResource Fonts.Monospace}">
@@ -138,6 +139,23 @@
Text="{Binding #Editor.SubjectGuideLength, Mode=OneWay}"
VerticalAlignment="Center"/>
+
+
+
+
+
diff --git a/src/Views/CommitMessageToolBox.axaml.cs b/src/Views/CommitMessageToolBox.axaml.cs
index 373e737261..ba447e9761 100644
--- a/src/Views/CommitMessageToolBox.axaml.cs
+++ b/src/Views/CommitMessageToolBox.axaml.cs
@@ -81,6 +81,40 @@ public bool WarnSubjectLength
set => SetAndRaise(WarnSubjectLengthProperty, ref _warnSubjectLen, value);
}
+ public static readonly DirectProperty DescriptionLengthProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(DescriptionLength),
+ static o => o.DescriptionLength);
+
+ public int DescriptionLength
+ {
+ get => _descriptionLen;
+ set => SetAndRaise(DescriptionLengthProperty, ref _descriptionLen, value);
+ }
+
+ public static readonly DirectProperty DescriptionGuideLengthProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(DescriptionGuideLength),
+ static o => o.DescriptionGuideLength,
+ static (o, v) => o.DescriptionGuideLength = v);
+
+ public int DescriptionGuideLength
+ {
+ get => _descriptionGuideLen;
+ set => SetAndRaise(DescriptionGuideLengthProperty, ref _descriptionGuideLen, value);
+ }
+
+ public static readonly DirectProperty WarnDescriptionLengthProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(WarnDescriptionLength),
+ static o => o.WarnDescriptionLength);
+
+ public bool WarnDescriptionLength
+ {
+ get => _warnDescriptionLen;
+ set => SetAndRaise(WarnDescriptionLengthProperty, ref _warnDescriptionLen, value);
+ }
+
public static readonly DirectProperty> SuggestionsProperty =
AvaloniaProperty.RegisterDirect>(
nameof(Suggestions),
@@ -173,7 +207,7 @@ public override void Render(DrawingContext context)
new Typeface(font),
ViewModels.Preferences.Instance.DefaultFontSize,
Brushes.White);
- var columnGuideX = columnTest.WidthIncludingTrailingWhitespace * 80 + 5.5;
+ var columnGuideX = columnTest.WidthIncludingTrailingWhitespace * _descriptionGuideLen + 5.5;
context.DrawLine(pen, new Point(columnGuideX, Math.Max(0, y)), new Point(columnGuideX, Bounds.Height));
}
@@ -209,10 +243,12 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
{
_subjectEndCharIdx = -1;
SubjectLength = 0;
+ DescriptionLength = 0;
return;
}
var subjectLen = 0;
+ var descriptionLen = 0;
var lastNonLineBreakCharIdx = 0;
var lastLineStart = 0;
for (var i = 0; i < text.Length; i++)
@@ -226,7 +262,10 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
if (string.IsNullOrWhiteSpace(line))
{
if (subjectLen > 0)
+ {
+ descriptionLen = GetMaxLineLength(text, lastLineStart);
break;
+ }
continue;
}
@@ -253,12 +292,18 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
}
SubjectLength = subjectLen;
+ DescriptionLength = descriptionLen;
_subjectEndCharIdx = lastNonLineBreakCharIdx;
}
else if (change.Property == SubjectLengthProperty || change.Property == SubjectGuideLengthProperty)
{
WarnSubjectLength = _subjectLen > _subjectGuideLen;
}
+ else if (change.Property == DescriptionLengthProperty || change.Property == DescriptionGuideLengthProperty)
+ {
+ WarnDescriptionLength = _descriptionLen > _descriptionGuideLen;
+ InvalidateVisual();
+ }
else if (change.Property == SubjectEndYProperty)
{
InvalidateVisual();
@@ -377,6 +422,28 @@ protected override void OnKeyDown(KeyEventArgs e)
base.OnKeyDown(e);
}
+ private static int GetMaxLineLength(string text, int start)
+ {
+ var maxLen = 0;
+ var lineStart = start;
+ for (var i = start; i <= text.Length; i++)
+ {
+ if (i == text.Length || text[i] == '\n')
+ {
+ var lineLen = i - lineStart;
+ while (lineLen > 0 && char.IsWhiteSpace(text[lineStart + lineLen - 1]))
+ lineLen--;
+
+ if (lineLen > maxLen)
+ maxLen = lineLen;
+
+ lineStart = i + 1;
+ }
+ }
+
+ return maxLen;
+ }
+
private void OnLayoutUpdated(object sender, EventArgs e)
{
if (_textPresenter == null || !IsEffectivelyVisible)
@@ -447,6 +514,9 @@ private void OnLayoutUpdated(object sender, EventArgs e)
private int _subjectEndCharIdx = -1;
private double _subjectEndY = 0;
private bool _warnSubjectLen = false;
+ private int _descriptionLen = 0;
+ private int _descriptionGuideLen = 80;
+ private bool _warnDescriptionLen = false;
private int _suggestionMatchStartIdx = -1;
private List _suggestions = null;
private int _selectedSuggestionIdx = 0;
diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml
index 55c2b17276..6960d93be7 100644
--- a/src/Views/Preferences.axaml
+++ b/src/Views/Preferences.axaml
@@ -47,7 +47,7 @@
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-