Skip to content

Commit b1b312c

Browse files
authored
feat(blame): add inline blame display mode option (#75)
Replace the unimplemented ShowOnHoverOnly boolean with a three-way InlineBlameDisplayMode setting (Always, CurrentLine, Hover) so users can control when inline blame annotations appear.
1 parent 7f2a12a commit b1b312c

3 files changed

Lines changed: 102 additions & 4 deletions

File tree

src/CodingWithCalvin.GitRanger/Editor/BlameAdornment/BlameAdornment.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Windows;
55
using System.Windows.Controls;
6+
using System.Windows.Input;
67
using System.Windows.Media;
78
using CodingWithCalvin.GitRanger.Core.Models;
89
using CodingWithCalvin.GitRanger.Options;
@@ -35,6 +36,7 @@ internal sealed class BlameAdornment
3536
private IReadOnlyList<BlameLineInfo> _blameData = Array.Empty<BlameLineInfo>();
3637
private string? _currentFilePath;
3738
private bool _isLoading;
39+
private int _activeLineNumber = -1;
3840

3941
/// <summary>
4042
/// Creates a new blame adornment for the given text view.
@@ -60,6 +62,9 @@ public BlameAdornment(
6062
// Subscribe to events
6163
_view.LayoutChanged += OnLayoutChanged;
6264
_view.Closed += OnViewClosed;
65+
_view.Caret.PositionChanged += OnCaretPositionChanged;
66+
_view.VisualElement.MouseMove += OnMouseMove;
67+
_view.VisualElement.MouseLeave += OnMouseLeave;
6368
_blameService.BlameLoaded += OnBlameLoaded;
6469
GeneralOptions.Saved += OnOptionsSaved;
6570

@@ -71,10 +76,63 @@ private void OnViewClosed(object sender, EventArgs e)
7176
{
7277
_view.LayoutChanged -= OnLayoutChanged;
7378
_view.Closed -= OnViewClosed;
79+
_view.Caret.PositionChanged -= OnCaretPositionChanged;
80+
_view.VisualElement.MouseMove -= OnMouseMove;
81+
_view.VisualElement.MouseLeave -= OnMouseLeave;
7482
_blameService.BlameLoaded -= OnBlameLoaded;
7583
GeneralOptions.Saved -= OnOptionsSaved;
7684
}
7785

86+
private void OnCaretPositionChanged(object sender, CaretPositionChangedEventArgs e)
87+
{
88+
var options = GeneralOptions.Instance;
89+
if (options == null || !options.EnableInlineBlame || options.InlineBlameDisplayMode != InlineBlameMode.CurrentLine)
90+
return;
91+
92+
var caretLineNumber = _view.TextSnapshot.GetLineNumberFromPosition(
93+
_view.Caret.Position.BufferPosition.Position) + 1;
94+
95+
if (caretLineNumber == _activeLineNumber)
96+
return;
97+
98+
_activeLineNumber = caretLineNumber;
99+
ClearAdornments();
100+
UpdateAdornments();
101+
}
102+
103+
private void OnMouseMove(object sender, MouseEventArgs e)
104+
{
105+
var options = GeneralOptions.Instance;
106+
if (options == null || !options.EnableInlineBlame || options.InlineBlameDisplayMode != InlineBlameMode.Hover)
107+
return;
108+
109+
var position = e.GetPosition(_view.VisualElement);
110+
var viewportPoint = new Point(position.X + _view.ViewportLeft, position.Y + _view.ViewportTop);
111+
112+
var hoveredLine = _view.TextViewLines.GetTextViewLineContainingYCoordinate(viewportPoint.Y);
113+
if (hoveredLine == null)
114+
return;
115+
116+
var lineNumber = _view.TextSnapshot.GetLineNumberFromPosition(hoveredLine.Start.Position) + 1;
117+
118+
if (lineNumber == _activeLineNumber)
119+
return;
120+
121+
_activeLineNumber = lineNumber;
122+
ClearAdornments();
123+
UpdateAdornments();
124+
}
125+
126+
private void OnMouseLeave(object sender, MouseEventArgs e)
127+
{
128+
var options = GeneralOptions.Instance;
129+
if (options == null || !options.EnableInlineBlame || options.InlineBlameDisplayMode != InlineBlameMode.Hover)
130+
return;
131+
132+
_activeLineNumber = -1;
133+
ClearAdornments();
134+
}
135+
78136
private void OnOptionsSaved(GeneralOptions options)
79137
{
80138
_ = ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
@@ -173,6 +231,12 @@ private void UpdateAdornments()
173231
if (_isLoading || _blameData.Count == 0)
174232
return;
175233

234+
if (options.InlineBlameDisplayMode == InlineBlameMode.CurrentLine)
235+
{
236+
_activeLineNumber = _view.TextSnapshot.GetLineNumberFromPosition(
237+
_view.Caret.Position.BufferPosition.Position) + 1;
238+
}
239+
176240
var viewportTop = _view.ViewportTop;
177241
var viewportBottom = _view.ViewportBottom;
178242

@@ -182,6 +246,10 @@ private void UpdateAdornments()
182246
continue;
183247

184248
var lineNumber = _view.TextSnapshot.GetLineNumberFromPosition(line.Start.Position) + 1;
249+
250+
if (options.InlineBlameDisplayMode != InlineBlameMode.Always && lineNumber != _activeLineNumber)
251+
continue;
252+
185253
var blameInfo = _blameData.FirstOrDefault(b => b.LineNumber == lineNumber);
186254
if (blameInfo == null)
187255
continue;

src/CodingWithCalvin.GitRanger/Options/GeneralOptions.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ public class GeneralOptions : BaseOptionModel<GeneralOptions>
8585
public bool CompactMode { get; set; } = false;
8686

8787
[Category("Display")]
88-
[DisplayName("Show On Hover Only")]
89-
[Description("Only show detailed blame information on mouse hover.")]
90-
[DefaultValue(false)]
91-
public bool ShowOnHoverOnly { get; set; } = false;
88+
[DisplayName("Inline Blame Display Mode")]
89+
[Description("Controls when inline blame is shown: Always (all lines), CurrentLine (caret line only), or Hover (mouse hover only).")]
90+
[DefaultValue(InlineBlameMode.Always)]
91+
public InlineBlameMode InlineBlameDisplayMode { get; set; } = InlineBlameMode.Always;
9292

9393
// Gutter Settings
9494
[Category("Gutter")]
@@ -136,6 +136,27 @@ public class GeneralOptions : BaseOptionModel<GeneralOptions>
136136
public LogLevel LogLevel { get; set; } = LogLevel.Error;
137137
}
138138

139+
/// <summary>
140+
/// Controls when inline blame annotations are displayed.
141+
/// </summary>
142+
public enum InlineBlameMode
143+
{
144+
/// <summary>
145+
/// Show inline blame on all visible lines.
146+
/// </summary>
147+
Always,
148+
149+
/// <summary>
150+
/// Show inline blame only on the current caret line.
151+
/// </summary>
152+
CurrentLine,
153+
154+
/// <summary>
155+
/// Show inline blame only when the mouse hovers over a line.
156+
/// </summary>
157+
Hover
158+
}
159+
139160
/// <summary>
140161
/// Color mode for blame annotations.
141162
/// </summary>

src/CodingWithCalvin.GitRanger/Options/GeneralOptionsPage.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ public bool CompactMode
126126
set => _options.CompactMode = value;
127127
}
128128

129+
[Category("Display")]
130+
[DisplayName("Inline Blame Display Mode")]
131+
[Description("Controls when inline blame is shown: Always (all lines), CurrentLine (caret line only), or Hover (mouse hover only).")]
132+
public InlineBlameMode InlineBlameDisplayMode
133+
{
134+
get => _options.InlineBlameDisplayMode;
135+
set => _options.InlineBlameDisplayMode = value;
136+
}
137+
129138
// Gutter Settings
130139
[Category("Gutter")]
131140
[DisplayName("Gutter Width")]

0 commit comments

Comments
 (0)