diff --git a/ILSpy.Tests/Views/DebugStepsTests.cs b/ILSpy.Tests/Views/DebugStepsTests.cs index cfd26b49b..0b1c4fc5a 100644 --- a/ILSpy.Tests/Views/DebugStepsTests.cs +++ b/ILSpy.Tests/Views/DebugStepsTests.cs @@ -133,6 +133,30 @@ public class DebugStepsTests languageService.Languages.Should().Contain(l => l.Name == "Typed IL"); return Task.CompletedTask; } + [AvaloniaTest] + public Task Pane_Reports_Not_Available_For_Languages_Without_Debug_Steps() + { + // The step tree only makes sense for IDebugStepProvider languages (C#, ILAst, Typed IL). + // For the plain IL disassembler the pane must not keep showing the previous language's + // stale step tree (whose commands would trigger pointless re-decompiles); it reports + // unavailability so the view swaps in a "not available" message instead. + var languageService = AppComposition.Current.GetExport(); + var debugStepsVm = AppComposition.Current.GetExport(); + + languageService.CurrentLanguage = languageService.Languages.OfType().First(); + global::Avalonia.Threading.Dispatcher.UIThread.RunJobs(); + debugStepsVm.IsAvailable.Should().BeTrue("C# provides debug steps"); + + // Simulate a populated tree from the C# run, then flip to the disassembler language. + debugStepsVm.Steps = new[] { new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("stale") }; + languageService.CurrentLanguage = languageService.Languages.OfType().First(l => l.Name == "IL"); + global::Avalonia.Threading.Dispatcher.UIThread.RunJobs(); + + debugStepsVm.IsAvailable.Should().BeFalse("the IL disassembler provides no debug steps"); + debugStepsVm.Steps.Should().BeNull("the previous language's step tree must not linger"); + return Task.CompletedTask; + } + [AvaloniaTest] public async Task Window_Menu_Toggle_Surfaces_The_Default_Hidden_Debug_Steps_Pane() { diff --git a/ILSpy/ViewModels/DebugStepsPaneModel.cs b/ILSpy/ViewModels/DebugStepsPaneModel.cs index eb963f79a..9a70af238 100644 --- a/ILSpy/ViewModels/DebugStepsPaneModel.cs +++ b/ILSpy/ViewModels/DebugStepsPaneModel.cs @@ -92,6 +92,14 @@ namespace ILSpy.ViewModels [ObservableProperty] Stepper.Node? selectedStep; + /// + /// True while the current language is an . When false, + /// the view replaces the step tree with a "not available" note instead of leaving the + /// previous language's stale tree (whose commands would trigger pointless re-decompiles). + /// + [ObservableProperty] + bool isAvailable; + public IRelayCommand ShowStateBeforeCommand { get; } public IRelayCommand ShowStateAfterCommand { get; } public IRelayCommand DebugStepCommand { get; } @@ -162,6 +170,7 @@ namespace ILSpy.ViewModels // Same language instance — just refresh the steps in case a decompile happened // while we were detached. Steps = activeLanguage!.Stepper.Steps; + IsAvailable = true; return; } DetachFromLanguage(); @@ -169,10 +178,13 @@ namespace ILSpy.ViewModels language.StepperUpdated += OnStepperUpdated; Steps = language.Stepper.Steps; Options = language.StepOptions; + IsAvailable = true; } void DetachFromLanguage() { + IsAvailable = false; + Steps = null; if (activeLanguage != null) { activeLanguage.StepperUpdated -= OnStepperUpdated; diff --git a/ILSpy/Views/DebugSteps.axaml b/ILSpy/Views/DebugSteps.axaml index fe6e7dbad..c5980b05d 100644 --- a/ILSpy/Views/DebugSteps.axaml +++ b/ILSpy/Views/DebugSteps.axaml @@ -7,7 +7,12 @@ mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="300" x:Class="ILSpy.Views.DebugSteps" x:DataType="vm:DebugStepsPaneModel"> - + + + @@ -49,4 +54,5 @@ +