-
Notifications
You must be signed in to change notification settings - Fork 213
Description
Description
When using CompletionWindow with filtering enabled, typing a character that reduces the completion list to zero matching items causes the application to crash on OpenGL/Skia backends. The application becomes unresponsive and on Linux can only be terminated by killing the process.
Steps to Reproduce
- Open a document with XML syntax highlighting.
- Type
<to trigger the completion window. - Type
!to filter the list down to a single item (e.g. "Comment"). - Type
.(or any other character that results in zero matching items).
Expected: The completion window closes gracefully.
Actual: The application crashes with an unhandled InvalidOperationException.
Exception
System.InvalidOperationException: Can't create drawing context for surface with 241, 0 size and 1 scaling
at Avalonia.Skia.GlRenderTarget.BeginRenderingSessionCore(Nullable`1 expectedSize)
Surface size at time of crash: Width = 241, Height = 0.
Call Stack
Avalonia.Skia.GlRenderTarget.BeginRenderingSessionCore(Nullable`1 expectedSize)
Avalonia.Skia.GlRenderTarget.BeginRenderingSession(PixelSize size)
Avalonia.Skia.SkiaGpuRenderTarget.CreateDrawingContextCore(...)
Avalonia.Skia.SkiaGpuRenderTarget.CreateDrawingContext(...)
Avalonia.Platform.RenderTargetExtensions.CreateDrawingContextWithProperties(...)
Avalonia.Rendering.Composition.Server.ServerCompositionTarget.Render()
Avalonia.Rendering.Composition.Server.ServerCompositor.RenderCore(bool catchExceptions)
Avalonia.Rendering.Composition.Server.ServerCompositor.RenderReentrancySafe(bool catchExceptions)
Avalonia.Rendering.Composition.Server.ServerCompositor.Render(bool catchExceptions)
Avalonia.Rendering.Composition.Server.ServerCompositor.Render()
Avalonia.Rendering.RenderLoop.TimerTick(TimeSpan time)
Avalonia.Rendering.SleepLoopRenderTimer.LoopProc()
Root Cause
Two cooperating bugs:
Bug 1 — CompletionWindowBase.Show() resets MinHeight to 0:
public void Show()
{
UpdatePosition();
Open();
Height = double.NaN;
MinHeight = 0; // ← overwrites MinHeight = 15 set in CompletionWindow constructor
}CompletionWindow's constructor sets MinHeight = 15 to prevent a zero-height popup. However, Show() immediately overwrites this with MinHeight = 0, removing the safeguard.
Bug 2 — Zero matches hides the list instead of closing the window:
In CaretPositionChanged, when the filtered list becomes empty, the code only hides the CompletionList child control:
if (CompletionList.ListBox.ItemCount == 0) CompletionList.IsVisible = false;The popup itself stays open (IsOpen = true) but its only child is invisible, so Avalonia lays it out at height = 0. Because MinHeight was reset to 0, there is no constraint preventing this.
The renderer then attempts to create a drawing context for a surface of size 241×0, which hits this guard in GlRenderTarget.BeginRenderingSessionCore:
if (size.Width <= 0 || size.Height <= 0 || scaling < 0)
throw new InvalidOperationException(
$"Can't create drawing context for surface with {size} size and {scaling} scaling");This exception does not match the GPU context-loss filter in ServerCompositor.RenderCore, so it propagates unhandled and crashes the render thread.
Fix
See this commit:
iplus-framework@7f4faaf
CompletionWindowBase.cs — Show()
Removed MinHeight = 0. This line overwrote the MinHeight = 15 set in CompletionWindow's constructor, allowing the popup to be laid out at zero height by Avalonia's layout engine when its child was hidden.
// Before
public void Show()
{
UpdatePosition();
Open();
Height = double.NaN;
MinHeight = 0; // removed
}
// After
public void Show()
{
UpdatePosition();
Open();
Height = double.NaN;
}CompletionWindow.cs — CaretPositionChanged()
When ItemCount == 0 and CloseAutomatically is true, the window now calls Hide() instead of setting CompletionList.IsVisible = false. An open popup with no visible content and MinHeight = 0 results in a zero-height surface being submitted to the renderer, which throws an unhandled InvalidOperationException on OpenGL/Skia backends and deadlocks the render thread. Closing the window is also the correct UX behavior — if nothing matches, there is nothing to show.
// Before
if (CompletionList.ListBox.ItemCount == 0) CompletionList.IsVisible = false;
else CompletionList.IsVisible = true;
// After
if (CompletionList.ListBox.ItemCount == 0)
{
if (CloseAutomatically) { Hide(); return; }
CompletionList.IsVisible = false;
}
else CompletionList.IsVisible = true;The IsVisible = false path is preserved for the CloseAutomatically = false case. The restored MinHeight = 15 provides an additional safety net even in that path.
For the sake of simplicity, I would be grateful if the code could be adopted directly without the cumbersome process of a pull request.