Fix live line number margin toggle

AvaloniaEdit inserts the line number margin before ILSpy's bookmark gutter and can render it before its cached font size is initialized. Keep the gutter order stable after live toggles and prepare the margin before the first render.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
This commit is contained in:
Siegfried Pammer
2026-10-01 07:55:33 +02:00
parent 2e296e7186
commit 5d6481f59e
3 changed files with 88 additions and 0 deletions
+35
View File
@@ -17,9 +17,11 @@
// DEALINGS IN THE SOFTWARE.
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Headless;
using Avalonia.Headless.NUnit;
using AvaloniaEdit.Editing;
@@ -75,4 +77,37 @@ public class MarginCursorTests
$"the {margin.GetType().Name} is a click target, so it must show the arrow rather than the text I-beam");
}
}
[AvaloniaTest]
public async Task Toggling_Line_Numbers_On_Live_Keeps_Gutter_Order_And_Positive_Font_Size()
{
var (window, vm) = await TestHarness.BootAsync(1);
var settings = AppComposition.Current.GetExport<SettingsService>().DisplaySettings;
settings.ShowLineNumbers = false;
var coreLibName = typeof(object).Assembly.GetName().Name!;
var objectNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLibName, "System", "System.Object");
vm.AssemblyTreeModel.SelectNode(objectNode);
await vm.DockWorkspace.WaitForDecompiledTextAsync();
var view = await window.WaitForComponent<DecompilerTextView>();
settings.ShowLineNumbers = true;
var margins = view.Editor.TextArea.LeftMargins;
var lineNumberMargin = margins.OfType<LineNumberMargin>()
.Should().ContainSingle("line numbers should render after being toggled on live").Which;
margins.IndexOf(margins.OfType<BookmarkMargin>().Single())
.Should().Be(margins.IndexOf(lineNumberMargin) - 1,
"the bookmark gutter must stay left of line numbers when line numbers are toggled on live");
GetLineNumberMarginEmSize(lineNumberMargin).Should().BeGreaterThan(0);
window.UpdateLayout();
AvaloniaHeadlessPlatform.ForceRenderTimerTick();
}
static double GetLineNumberMarginEmSize(LineNumberMargin margin)
{
return (double)typeof(LineNumberMargin)
.GetProperty("EmSize", BindingFlags.Instance | BindingFlags.NonPublic)!
.GetValue(margin)!;
}
}
+12
View File
@@ -18,6 +18,7 @@
using System;
using System.ComponentModel;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
@@ -100,6 +101,17 @@ namespace ICSharpCode.ILSpy.TextView
FontFamily = new FontFamily(displaySettings.SelectedFont);
if (displaySettings.SelectedFontSize > 0)
FontSize = EditorZoom.EffectiveFontSize(displaySettings);
PrepareLineNumberMarginsForRender();
}
internal void PrepareLineNumberMarginsForRender()
{
foreach (var margin in TextArea.LeftMargins.OfType<LineNumberMargin>())
{
margin.SetValue(TextBlock.FontFamilyProperty, FontFamily);
margin.SetValue(TextBlock.FontSizeProperty, FontSize);
margin.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
}
static DisplaySettings? TryGetDisplaySettings()
@@ -34,6 +34,7 @@ using Avalonia.Threading;
using Avalonia.VisualTree;
using AvaloniaEdit.Document;
using AvaloniaEdit.Editing;
using AvaloniaEdit.Folding;
using AvaloniaEdit.Highlighting;
@@ -179,6 +180,41 @@ namespace ICSharpCode.ILSpy.TextView
margin.Cursor = ArrowCursor;
}
void KeepBookmarkMarginBeforeLineNumbers()
{
if (bookmarkMargin == null)
return;
var margins = Editor.TextArea.LeftMargins;
var lineNumberIndex = -1;
for (var i = 0; i < margins.Count; i++)
{
if (margins[i] is LineNumberMargin)
{
lineNumberIndex = i;
break;
}
}
var bookmarkIndex = margins.IndexOf(bookmarkMargin);
if (lineNumberIndex < 0 || bookmarkIndex < 0 || bookmarkIndex < lineNumberIndex)
return;
var lineNumberMargin = margins[lineNumberIndex];
Control? separator = null;
if (lineNumberIndex + 1 < margins.Count
&& margins[lineNumberIndex + 1] is global::Avalonia.Controls.Shapes.Line)
separator = margins[lineNumberIndex + 1];
if (separator != null)
margins.Remove(separator);
margins.Remove(lineNumberMargin);
bookmarkIndex = margins.IndexOf(bookmarkMargin);
margins.Insert(bookmarkIndex + 1, lineNumberMargin);
if (separator != null)
margins.Insert(bookmarkIndex + 2, separator);
}
void OnPreviewKeyDownForOmnibar(object? sender, KeyEventArgs e)
{
if (e.Key == Key.L && e.KeyModifiers == KeyModifiers.Control && Omnibar.IsVisible)
@@ -649,6 +685,11 @@ namespace ICSharpCode.ILSpy.TextView
{
case nameof(DisplaySettings.ShowLineNumbers):
Editor.ShowLineNumbers = s.ShowLineNumbers;
if (Editor.ShowLineNumbers)
{
KeepBookmarkMarginBeforeLineNumbers();
Editor.PrepareLineNumberMarginsForRender();
}
break;
case nameof(DisplaySettings.EnableWordWrap):
Editor.WordWrap = s.EnableWordWrap;